如何以有效和快速的方式使用不同的参数屏蔽字符串
How to mask a string using different parameters in an effective and fast way
我想使用不同的参数来屏蔽字符串,例如要屏蔽的字符百分比、屏蔽字符和应用屏蔽的位置(在字符串的开头、中间或结尾) .我想出了一个解决方案,但我认为这不是最好的解决方案。这是我的代码:
public static string MaskChars(this string value, char maskToApply = 'X', int percentToApply = 25, MaskOption maskOptions = MaskOption.InTheMiddleOfString)
{
string valueTrimmed = value.Trim();
int len = valueTrimmed.Length;
if (len == 0)
return Empty;
if (percentToApply >= 100)
return maskToApply.ToString(CultureInfo.InvariantCulture).Replicate(len);
var charsToMask = (int)Math.Round((decimal)(percentToApply * len) / 100);
if (charsToMask == 0)
charsToMask = 1;
int top = len - charsToMask;
int maskCounter = 0;
var builder = new StringBuilder(len);
for (int i = 0; i < len; i++)
{
if (maskCounter < charsToMask)
{
switch (maskOptions)
{
// Apply mask in the middle of the string
case MaskOption.InTheMiddleOfString:
if (i >= charsToMask && i < top)
{
builder.Append(maskToApply);
maskCounter++;
}
break;
// Apply mask at the begining of the string
case MaskOption.AtTheBeginingOfString:
if (i < charsToMask)
{
builder.Append(maskToApply);
maskCounter++;
}
break;
// Apply mask at the end of the string
case MaskOption.AtTheEndOfString:
if (i >= top)
{
builder.Append(maskToApply);
maskCounter++;
}
break;
}
}
else
{
builder.Append(valueTrimmed[i]);
}
}
return builder.ToString();
}
其中:
public enum MaskOption : byte
{
AtTheBeginingOfString = 1,
InTheMiddleOfString = 2,
AtTheEndOfString = 3
}
Replicate 是一种复制字符串的简单方法
public static string Replicate(this string value, int count)
{
if (IsNullOrEmpty(value))
return Empty;
if (count <= 0)
return value;
var builder = new StringBuilder();
builder.Append(value);
for (int i = count; i >= 1; i--)
builder.Append(value);
return builder.ToString();
}
首先,我 运行 您的代码以查看预期的行为是什么,我认为它不正确。这是测试代码和输出:
var testStr = "This is my string to mask characters in!";
Console.WriteLine(testStr.MaskChars('X', 25, Extensions.MaskOption.AtTheBeginingOfString));
Console.WriteLine(testStr.MaskChars('X', 25, Extensions.MaskOption.InTheMiddleOfString));
Console.WriteLine(testStr.MaskChars(maskOptions: Extensions.MaskOption.AtTheEndOfString));
输出
我的印象是字符串应该保持相同的长度,而被屏蔽的字符只会改变字符串中的位置。另外,我不确定你为什么要 trim 字符串(我不会在这个方法中这样做,我会让调用者决定他们是否想先 trim 它),但是我把那部分留在里面了。
下面是我将如何简化代码来做到这一点:
public static string MaskChars(this string input, char maskChar,
int percentToApply, MaskOption maskOptions)
{
// I would remove this. The caller can trim the string first if they want.
var result = input.Trim();
if (result.Length == 0 || percentToApply < 1) return result;
if (percentToApply >= 100) return new string(maskChar, result.Length);
var maskLength = Math.Max((int) Math.Round(percentToApply * result.Length / 100m), 1);
var mask = new string(maskChar, maskLength);
switch (maskOptions)
{
case MaskOption.AtTheBeginingOfString:
result = mask + result.Substring(maskLength);
break;
case MaskOption.AtTheEndOfString:
result = result.Substring(0, result.Length - maskLength) + mask;
break;
case MaskOption.InTheMiddleOfString:
var maskStart = (result.Length - maskLength) / 2;
result = result.Substring(0, maskStart) + mask +
result.Substring(maskStart + maskLength);
break;
}
return result;
}
输出
我要做的最后一件事是摆脱方法中的默认参数值,并创建一些重载方法,而不是使用缺少参数的默认值。通过这种方式,用户可以调整所需的值(在您的实现中,如果他们只想更改 MaskOption
,则他们必须重新声明其他默认值或像我上面那样使用命名参数):
private static char defaultMaskChar = 'X';
private static MaskOption defaultMaskOption = MaskOption.InTheMiddleOfString;
private static int defaultPercentToApply = 25;
public static string MaskChars(this string input)
{
return MaskChars(input, defaultMaskChar);
}
public static string MaskChars(this string input, char maskChar)
{
return MaskChars(input, maskChar, defaultPercentToApply);
}
public static string MaskChars(this string input, int percentToApply)
{
return MaskChars(input, defaultMaskChar, percentToApply);
}
public static string MaskChars(this string input, MaskOption maskOption)
{
return MaskChars(input, defaultMaskChar, defaultPercentToApply, maskOption);
}
public static string MaskChars(this string input, char maskChar, int percentToApply)
{
return MaskChars(input, maskChar, percentToApply, defaultMaskOption);
}
public static string MaskChars(this string input, char maskChar, MaskOption maskOption)
{
return MaskChars(input, maskChar, defaultPercentToApply, maskOption);
}
public static string MaskChars(this string input, int percentToApply, MaskOption maskOption)
{
return MaskChars(input, defaultMaskChar, percentToApply, maskOption);
}
您可以尝试另一种方法来做同样的事情。参数更少,更容易测试,也更容易阅读。
请注意,我没有 post 像 Guard 这样的界面和小细节。
public class StringMask : IStringMask
{
/// <summary>
/// The Mask character
/// </summary>
private readonly char MaskCharacter;
/// <summary>
/// The instance
/// </summary>
private readonly string Instance;
/// <summary>
/// The Mask
/// </summary>
private BitArray Mask;
/// <summary>
/// Initializes a new instance of the <see cref="StringMask"/> class.
/// </summary>
/// <param name="instance">The string you would like to mask.</param>
/// <param name="maskCharacter">The Mask character.</param>
public StringMask(string instance, char maskCharacter)
{
MaskCharacter = maskCharacter;
Instance = instance;
Mask = new BitArray(instance.Length, false);
}
/// <summary>
/// Shows the first [number] of characters and masks the rest.
/// </summary>
/// <param name="number">The number of the characters to show.</param>
/// <returns>IStringMask.</returns>
public IStringMask ShowFirst(int number)
{
Validate(number);
for (int i = 0; i < number; i++)
{
Mask[i] = true;
}
return this;
}
/// <summary>
/// Shows the last [number] of characters and masks the rest.
/// </summary>
/// <param name="number">The number of the characters to show.</param>
/// <returns>IStringMask.</returns>
public IStringMask ShowLast(int number)
{
Validate(number);
for (int i = 0; i < number; i++)
{
Mask[Instance.Length - i - 1] = true;
}
return this;
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
var sb = new StringBuilder();
for (int i = 0; i < Instance.Length; i++)
{
if (Mask[i])
sb.Append(Instance[i]);
else
sb.Append(MaskCharacter);
}
return sb.ToString();
}
private void Validate(int number)
{
Guard.IsBetweenExclusive(number, 0, Instance.Length, nameof(number));
}
}
虽然你可以在这里看到测试:
[TestFixture]
internal class MaskTests
{
private string input = "40770058698999513265";
private char maskChar = 'X';
private IStringMask mask;
[SetUp]
public void Initiate()
{
mask = new StringMask(input, maskChar);
}
[Test]
public void MaskShowLast()
{
var output = mask.ShowLast(10);
Console.WriteLine(output);
Assert.AreEqual("XXXXXXXXXX8999513265", output.ToString());
}
[Test]
public void MaskInTheMiddle()
{
var output = mask.ShowLast(5).ShowFirst(5);
Console.WriteLine(output);
Assert.AreEqual("40770XXXXXXXXXX13265", output.ToString());
}
[Test]
public void MaskInTheMiddleTooShort()
{
Assert.Throws<ArgumentOutOfRangeException>(() => mask.ShowLast(0).ShowFirst(0));
}
[Test]
public void MaskInTheMiddleTooLong()
{
Assert.Throws<ArgumentOutOfRangeException>(()=> mask.ShowLast(500).ShowFirst(500));
}
[Test]
public void MaskAtTheEnd()
{
var output = mask.ShowFirst(10);
Console.WriteLine(output);
Assert.AreEqual("4077005869XXXXXXXXXX", output.ToString());
}
}
我想使用不同的参数来屏蔽字符串,例如要屏蔽的字符百分比、屏蔽字符和应用屏蔽的位置(在字符串的开头、中间或结尾) .我想出了一个解决方案,但我认为这不是最好的解决方案。这是我的代码:
public static string MaskChars(this string value, char maskToApply = 'X', int percentToApply = 25, MaskOption maskOptions = MaskOption.InTheMiddleOfString)
{
string valueTrimmed = value.Trim();
int len = valueTrimmed.Length;
if (len == 0)
return Empty;
if (percentToApply >= 100)
return maskToApply.ToString(CultureInfo.InvariantCulture).Replicate(len);
var charsToMask = (int)Math.Round((decimal)(percentToApply * len) / 100);
if (charsToMask == 0)
charsToMask = 1;
int top = len - charsToMask;
int maskCounter = 0;
var builder = new StringBuilder(len);
for (int i = 0; i < len; i++)
{
if (maskCounter < charsToMask)
{
switch (maskOptions)
{
// Apply mask in the middle of the string
case MaskOption.InTheMiddleOfString:
if (i >= charsToMask && i < top)
{
builder.Append(maskToApply);
maskCounter++;
}
break;
// Apply mask at the begining of the string
case MaskOption.AtTheBeginingOfString:
if (i < charsToMask)
{
builder.Append(maskToApply);
maskCounter++;
}
break;
// Apply mask at the end of the string
case MaskOption.AtTheEndOfString:
if (i >= top)
{
builder.Append(maskToApply);
maskCounter++;
}
break;
}
}
else
{
builder.Append(valueTrimmed[i]);
}
}
return builder.ToString();
}
其中:
public enum MaskOption : byte
{
AtTheBeginingOfString = 1,
InTheMiddleOfString = 2,
AtTheEndOfString = 3
}
Replicate 是一种复制字符串的简单方法
public static string Replicate(this string value, int count)
{
if (IsNullOrEmpty(value))
return Empty;
if (count <= 0)
return value;
var builder = new StringBuilder();
builder.Append(value);
for (int i = count; i >= 1; i--)
builder.Append(value);
return builder.ToString();
}
首先,我 运行 您的代码以查看预期的行为是什么,我认为它不正确。这是测试代码和输出:
var testStr = "This is my string to mask characters in!";
Console.WriteLine(testStr.MaskChars('X', 25, Extensions.MaskOption.AtTheBeginingOfString));
Console.WriteLine(testStr.MaskChars('X', 25, Extensions.MaskOption.InTheMiddleOfString));
Console.WriteLine(testStr.MaskChars(maskOptions: Extensions.MaskOption.AtTheEndOfString));
输出
我的印象是字符串应该保持相同的长度,而被屏蔽的字符只会改变字符串中的位置。另外,我不确定你为什么要 trim 字符串(我不会在这个方法中这样做,我会让调用者决定他们是否想先 trim 它),但是我把那部分留在里面了。
下面是我将如何简化代码来做到这一点:
public static string MaskChars(this string input, char maskChar,
int percentToApply, MaskOption maskOptions)
{
// I would remove this. The caller can trim the string first if they want.
var result = input.Trim();
if (result.Length == 0 || percentToApply < 1) return result;
if (percentToApply >= 100) return new string(maskChar, result.Length);
var maskLength = Math.Max((int) Math.Round(percentToApply * result.Length / 100m), 1);
var mask = new string(maskChar, maskLength);
switch (maskOptions)
{
case MaskOption.AtTheBeginingOfString:
result = mask + result.Substring(maskLength);
break;
case MaskOption.AtTheEndOfString:
result = result.Substring(0, result.Length - maskLength) + mask;
break;
case MaskOption.InTheMiddleOfString:
var maskStart = (result.Length - maskLength) / 2;
result = result.Substring(0, maskStart) + mask +
result.Substring(maskStart + maskLength);
break;
}
return result;
}
输出
我要做的最后一件事是摆脱方法中的默认参数值,并创建一些重载方法,而不是使用缺少参数的默认值。通过这种方式,用户可以调整所需的值(在您的实现中,如果他们只想更改 MaskOption
,则他们必须重新声明其他默认值或像我上面那样使用命名参数):
private static char defaultMaskChar = 'X';
private static MaskOption defaultMaskOption = MaskOption.InTheMiddleOfString;
private static int defaultPercentToApply = 25;
public static string MaskChars(this string input)
{
return MaskChars(input, defaultMaskChar);
}
public static string MaskChars(this string input, char maskChar)
{
return MaskChars(input, maskChar, defaultPercentToApply);
}
public static string MaskChars(this string input, int percentToApply)
{
return MaskChars(input, defaultMaskChar, percentToApply);
}
public static string MaskChars(this string input, MaskOption maskOption)
{
return MaskChars(input, defaultMaskChar, defaultPercentToApply, maskOption);
}
public static string MaskChars(this string input, char maskChar, int percentToApply)
{
return MaskChars(input, maskChar, percentToApply, defaultMaskOption);
}
public static string MaskChars(this string input, char maskChar, MaskOption maskOption)
{
return MaskChars(input, maskChar, defaultPercentToApply, maskOption);
}
public static string MaskChars(this string input, int percentToApply, MaskOption maskOption)
{
return MaskChars(input, defaultMaskChar, percentToApply, maskOption);
}
您可以尝试另一种方法来做同样的事情。参数更少,更容易测试,也更容易阅读。
请注意,我没有 post 像 Guard 这样的界面和小细节。
public class StringMask : IStringMask
{
/// <summary>
/// The Mask character
/// </summary>
private readonly char MaskCharacter;
/// <summary>
/// The instance
/// </summary>
private readonly string Instance;
/// <summary>
/// The Mask
/// </summary>
private BitArray Mask;
/// <summary>
/// Initializes a new instance of the <see cref="StringMask"/> class.
/// </summary>
/// <param name="instance">The string you would like to mask.</param>
/// <param name="maskCharacter">The Mask character.</param>
public StringMask(string instance, char maskCharacter)
{
MaskCharacter = maskCharacter;
Instance = instance;
Mask = new BitArray(instance.Length, false);
}
/// <summary>
/// Shows the first [number] of characters and masks the rest.
/// </summary>
/// <param name="number">The number of the characters to show.</param>
/// <returns>IStringMask.</returns>
public IStringMask ShowFirst(int number)
{
Validate(number);
for (int i = 0; i < number; i++)
{
Mask[i] = true;
}
return this;
}
/// <summary>
/// Shows the last [number] of characters and masks the rest.
/// </summary>
/// <param name="number">The number of the characters to show.</param>
/// <returns>IStringMask.</returns>
public IStringMask ShowLast(int number)
{
Validate(number);
for (int i = 0; i < number; i++)
{
Mask[Instance.Length - i - 1] = true;
}
return this;
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
var sb = new StringBuilder();
for (int i = 0; i < Instance.Length; i++)
{
if (Mask[i])
sb.Append(Instance[i]);
else
sb.Append(MaskCharacter);
}
return sb.ToString();
}
private void Validate(int number)
{
Guard.IsBetweenExclusive(number, 0, Instance.Length, nameof(number));
}
}
虽然你可以在这里看到测试:
[TestFixture]
internal class MaskTests
{
private string input = "40770058698999513265";
private char maskChar = 'X';
private IStringMask mask;
[SetUp]
public void Initiate()
{
mask = new StringMask(input, maskChar);
}
[Test]
public void MaskShowLast()
{
var output = mask.ShowLast(10);
Console.WriteLine(output);
Assert.AreEqual("XXXXXXXXXX8999513265", output.ToString());
}
[Test]
public void MaskInTheMiddle()
{
var output = mask.ShowLast(5).ShowFirst(5);
Console.WriteLine(output);
Assert.AreEqual("40770XXXXXXXXXX13265", output.ToString());
}
[Test]
public void MaskInTheMiddleTooShort()
{
Assert.Throws<ArgumentOutOfRangeException>(() => mask.ShowLast(0).ShowFirst(0));
}
[Test]
public void MaskInTheMiddleTooLong()
{
Assert.Throws<ArgumentOutOfRangeException>(()=> mask.ShowLast(500).ShowFirst(500));
}
[Test]
public void MaskAtTheEnd()
{
var output = mask.ShowFirst(10);
Console.WriteLine(output);
Assert.AreEqual("4077005869XXXXXXXXXX", output.ToString());
}
}