【问题标题】:Regex masking of words that contain a digit包含数字的单词的正则表达式屏蔽
【发布时间】:2013-11-04 19:26:43
【问题描述】:

试图想出一个“简单”的正则表达式来屏蔽看起来可能包含帐号的文本位。

简单的英语:

  • 应匹配包含数字的任何单词(或一系列此类单词)
  • 保留最后 4 位数字
  • 用四个 X (xxxx) 替换匹配字符串的所有先前部分

到目前为止

我正在使用以下内容:

[\-0-9 ]+(?<m1>[\-0-9]{4})

替换为

xxxx${m1}

但这错过了下面最后几个示例

样本数据:

123456789
a123b456
a1234b5678
a1234 b5678
111 22 3333
this is a a1234 b5678 test string

实际结果

xxxx6789
a123b456
a1234b5678
a1234 b5678
xxxx3333
this is a a1234 b5678 test string

预期结果

xxxx6789
xxxxb456
xxxx5678
xxxx5678
xxxx3333
this is a xxxx5678 test string

这样的安排可以用正则表达式替换吗?

我认为我需要一些贪婪和前瞻功能,但我在这些领域的经验为零。

【问题讨论】:

  • 很抱歉,我看不到问题所在?您得到的错误结果是什么?
  • @Sniffer 我已经根据他的模式添加了实际结果。
  • 您的预期结果对吗? xxxxb456 看起来不应该在那里。

标签: c# regex


【解决方案1】:

这适用于您的示例:

var result = Regex.Replace(
    input,
    @"(?<!\b\w*\d\w*)(?<m1>\s?\b\w*\d\w*)+",
    m => "xxxx" + m.Value.Substring(Math.Max(0, m.Value.Length - 4)));

如果你有像111 2233 33 这样的值,它将打印xxxx3 33。如果您希望它没有空格,您可以将 lambda 转换为从值中删除空格的多行语句。

为了稍微解释一下正则表达式模式,它有一个否定的lookbehind,所以它确保它后面的单词没有数字(数字周围有可选的单词字符)。然后它有m1 部分,它查找其中包含数字的单词。在正则表达式模式解析其余字符后,通过一些 C# 代码获取最后四个字符。

【讨论】:

    【解决方案2】:

    我不认为正则表达式是解决此问题的最佳方法,这就是我发布此答案的原因。对于如此复杂的情况,构建相应的正则表达式太难了,更糟糕的是,它的清晰度和适应性远低于较长代码的方法。

    这些行下面的代码提供了您所追求的确切功能,它足够清晰并且可以轻松扩展。

    string input = "this is a a1234 b5678 test string";
    string output = "";
    string[] temp = input.Trim().Split(' ');
    bool previousNum = false;
    string tempOutput = "";
    foreach (string word in temp)
    {
        if (word.ToCharArray().Where(x => char.IsDigit(x)).Count() > 0)
        {
            previousNum = true;
            tempOutput = tempOutput + word;
        }
        else
        {
            if (previousNum)
            {
                if (tempOutput.Length >= 4) tempOutput = "xxxx" + tempOutput.Substring(tempOutput.Length - 4, 4);
                output = output + " " + tempOutput;
                previousNum = false;
            }
            output = output + " " + word;
        }
    }
    if (previousNum)
    {
        if (tempOutput.Length >= 4) tempOutput = "xxxx" + tempOutput.Substring(tempOutput.Length - 4, 4);
        output = output + " " + tempOutput;
        previousNum = false;
    }
    

    【讨论】:

    • 一个快速评论,你说if (!previousNum) previousNum = true;,你可以通过说previousNum = true;并删除if语句来降低复杂性。它将将该语句的圈复杂度降低 1
    • @joe_coolish 你是完全正确的。我很快就写好了代码。
    • 除此之外,+1 用于解开正则表达式!
    • 我同意直接的正则表达式不是最好的方法......但这就是我的追求
    • @BrianAdkins 我很高兴看到我们都同意这一点。您可以继续尝试获取基于正则表达式的方法(因为您被指示这样做,或者因为这是个人挑战或出于任何原因),并且如果找不到任何提供足够准确的解决方案的方法,您可以只依靠在此代码上。
    【解决方案3】:

    你试过这个吗:

    .*(?<m1>[\d]{4})(?<m2>.*)
    

    替换

    xxxx${m1}${m2}
    

    这会产生

    xxxx6789
    xxxx5678
    xxxx5678
    xxxx3333
    xxxx5678 test string
    

    在 'b' 变成一个数字之前,你不会得到 'a123b456' 来匹配...。 ;-)

    【讨论】:

    • 这将替换上一个示例中的“This is a”
    • 在我写答案的时候,他的例子也删除了那个。从那时起,要求发生了变化;-)
    【解决方案4】:

    这是我非常快速的尝试:

    (\s|^)([a-z]*\d+[a-z,0-9]+\s)+
    

    这将选择所有这些测试用例。现在对于 C# 代码,您需要检查每个匹配项以查看匹配序列的开头或结尾是否有空格(例如,最后一个示例将在选中前后有空格)

    这是进行替换的 C# 代码:

    var redacted = Regex.Replace(record, @"(\s|^)([a-z]*\d+[a-z,0-9]+\s)+",
        match => "xxxx" /*new String("x",match.Value.Length - 4)*/ + 
        match.Value.Substring(Math.Max(0, match.Value.Length - 4)));
    

    【讨论】:

      猜你喜欢
      • 2020-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-07
      • 1970-01-01
      • 2016-06-14
      • 1970-01-01
      • 2016-01-14
      相关资源
      最近更新 更多