regex 屏蔽字符串中的任何15位或16位信用卡号,除了最后4位以外,都可以替换为X,C#中的复杂正则表达式谜题

ruyhziif  于 2023-02-10  发布在  C#
关注(0)|答案(2)|浏览(98)

我在C#/.NET 4.7中遇到了一个我认为很难的Regex问题/难题
我的任务是屏蔽字符串中的任何15位16位信用卡号(如果有的话)。信用卡号的数字之间也可能有空格。下面是一些例子,希望能让大家明白这一点:

  • "CC# 123456789012345"变为"CC# XXXXXXXXXXX2345"15-数字示例)
  • "Credit Card 123456 7890 123456"变为"Credit Card XXXXXXXXXXXX3456"16-带随机空格的数字示例)
  • "Text 1234 5678 9012 3456 Text"变为"Text XXXXXXXXXXXX3456 Text"
  • "Something 123 456 789 012 345"变为"Something XXXXXXXXXXX2345"

基本上,信用卡 * 的最后4个应该总是存在 *,并且数字的 * 其余 * 用X屏蔽.因此对于15数字的情况,在4数字之前应该有11``X,并且对于16数字的情况,在最后的4数字之前应该有12X

我们的想法是,如果用户在一个开放文本字段中输入信用卡号,我们在将其存储到数据库之前将其屏蔽。我的一个喘息机会是,他们不能在字符串中放入除spaces之外的任何特殊字符。字符串的最大长度始终为40个字符。

事实证明,这是一个相当困难的正则表达式问题(或者也许我根本不应该使用正则表达式?...)。因此,我一直在使用 ChatGPT 创建一些正则表达式,并取得了一些进展,但没有一个真正为我提供正确的输出。我得到的最接近的结果大致如下:

private static string MaskCreditCardNumber(string input) 
{
  string pattern = @"\b(?:\d[ -]*?){11,15}(?=\d{4}\b)";

  string output = Regex.Replace(input, pattern, match => 
      new string('X', match.Length - 4) + 
      match.Value.Substring(match.Length - 4));

  return output;
}

对于我的大多数测试用例,这似乎屏蔽了除了最后一个8数字之外的所有数字,但这显然不是我想要的,因为我想要最后一个4
尝试了大约8个小时的ChatGPT创建各种不同的Regex,以及尝试我自己的。仍然无法解决这个替换算法。寻找帮助创建这个正则表达式替换算法。

8ftvxx2r

8ftvxx2r1#

您可以分两步完成此任务:
1.借助 * 正则表达式 * 获取 * 可能匹配 *:数字与空格混合
1.计算位数,如果您得到的是1516,则将此 * true匹配 * 格式化为所需的表示形式
例如,我们可以使用[0-9][0-9 ]{13,}[0-9]模式

[0-9]       - digit in 0..9 range
[0-9 ]{13,} - at least 13 digits or spaces
[0-9]       - digit in 0..9 range

和下面的代码:

using System.Linq;
using System.Text.RegularExpressions;

...

private static string MaskCreditCardNumber(string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  return Regex.Replace(text, "[0-9][0-9 ]{13,}[0-9]", match => {
    string digits = string.Concat(match.Value
      .Where(c => char.IsDigit(c)));

    return digits.Length == 16 || digits.Length == 15
      ? new string('X', digits.Length - 4) + digits.Substring(digits.Length - 4)
      : match.Value;
  });
}

演示:

string[] tests = new string[] {
  "CC# 123456789012345",
  "Credit Card 123456 7890 123456",
  "Text 1234 5678 9012 3456 Text",
  "Something 123 456 789 012 3456",
  "Not a card (too long): 12345689123456789123",
  "Just a value 123",
};

var report = string.Join(Environment.NewLine, tests
  .Select(test => $"{test,45} => {MaskCreditCardNumber(test)}"));

Console.Write(report);

输出:

CC# 123456789012345 => CC# XXXXXXXXXXX2345
               Credit Card 123456 7890 123456 => Credit Card XXXXXXXXXXXX3456
                Text 1234 5678 9012 3456 Text => Text XXXXXXXXXXXX3456 Text
               Something 123 456 789 012 3456 => Something XXXXXXXXXXXX3456
  Not a card (too long): 12345689123456789123 => Not a card (too long): 12345689123456789123
                             Just a value 123 => Just a value 123

拜托,你自己去吧

rjjhvcjd

rjjhvcjd2#

我猜你可以使用thir正则表达式找到15或16个信用卡号码:

(?<!\d *)\d(?: *\d *){13,14}\d(?! *\d)

示例:https://regex101.com/r/UnxpFm/1
此外,您可能需要使用校验和验证(例如Luhn algorithm)来检查这些数字是否构成信用卡号码。
和代码:

string Sanitize(string input) =>
    Regex.Replace(input, @"(?<!\d.*)\d(?: *\d *){13,14}\d(?! *\d)", m =>
    {
        var digitsOnly = m.Value.Replace(" ", string.Empty);
        var numberOfSanitizedDigits = m.Value.Count(char.IsDigit) - 4;

        return new string('X', numberOfSanitizedDigits) + digitsOnly[^4..];
    });

相关问题