【问题标题】:Only match unique string occurrences仅匹配出现的唯一字符串
【发布时间】:2017-03-21 00:59:00
【问题描述】:

我们正在为电子邮件做一些数据丢失防护,但问题是当人们多次回复电子邮件时,有时信用卡号或帐号会出现多次。

我们如何让 Java 正则表达式每个字符串只匹配一次。

例如,我们使用以下正则表达式来捕获匹配 2 个字母后跟 5 个或 6 个数字的帐号。在任何一种情况下,它也会省略 CR。

\b(?!CR)(?!cr)[A-Za-z]{2}[0-9]{5,6}\b

我们怎样才能找到它:

CX12345
CX14584
JB145888
JD748452
CX12345 (Ignore as its already found it above)
LM45855

【问题讨论】:

  • 我宁愿建议匹配最后一次出现:(?s)\b((?!CR|cr)[A-Za-z]{2}\d{5,6})\b(?!.*\b\1\b)。在 Java 中,不要忘记使用双反斜杠。
  • 您好,感谢您的帮助。我曾尝试将此字符串添加到正则表达式测试器中,但它又回来了,它不喜欢它开头的 (?s)?另外,当我尝试从中删除 (?s) 时,它仍然会执行以前的操作。匹配多个相同的字符串。请问有什么想法吗?我用来测试的工具是使用 javascript 的 www.regextester.com。谢谢
  • 为什么是“正则表达式测试器”?在 Java 代码中使用它。我已经在评论中在在线测试仪中提供了测试。在 Java 中,使用 String pattern = "(?s)\\b((?!CR|cr)[A-Za-z]{2}\\d{5,6})\\b(?!.*\\b\\1\\b)";Here is a Java demo.
  • 大家好,抱歉耽搁了。它似乎没有奏效。我认为这可能是我的错,我在原始问题中提到它是 java 脚本。我认为情况并非如此。我在一个名为 Mimecast 的控制台中使用它,我们将其用作外部邮件服务。他们要求我们在通过它的每封电子邮件中捕获一行正则表达式。我知道它可以使用基于 Java 的 Regex 查询和 Python。因此,由于我不是 Java 开发人员,因此我不太确定您对使用 Java 代码的意思。我希望这是有道理的。

标签: regex


【解决方案1】:

唯一的字符串出现可以匹配

<STRING_PATTERN>(?!.*<STRING_PATTERN>)  // Find the last occurrence
(?<!<STRING_PATTERN>.*)<STRING_PATTERN> // Find the first occurrence, only works in regex
                                        // that supports infinite-width lookbehind patterns

其中&lt;STRING_PATTERN&gt; 是人们搜索的唯一出现的模式。请注意,both 将与 .NET 正则表达式库一起使用,但大多数其他库通常不支持第二个(只有 PyPi Python regex 库和 JavaScript ECMAScript 2018 正则表达式支持它) .注意.默认不匹配换行符,所以你需要传递一个像DOTALL这样的修饰符(在大多数库中,你可以在模式中添加(?s)修饰符(只有在Ruby中(?m)也是如此),或者使用传递给正则表达式编译方法的特定标志。在How do I match any character across multiple lines in a regular expression?中查看更多信息

您似乎需要这样的正则表达式:

/\b((?!CR|cr)[A-Za-z]{2}\d{5,6})\b(?![\s\S]*\b\1\b)/

regex demo is available here

详情

  • \b - 前导词边界
  • ((?!CR|cr)[A-Za-z]{2}\d{5,6}) - 第 1 组捕获
    • (?!CR|cr) - 接下来的两个字符不能是 CRcr,负前瞻检查
    • [A-Za-z]{2} - 2 个 ASCII 字母
    • \d{5,6} - 5 到 6 位数字
  • \b - 词尾边界
  • (?![\s\S]*\b\1\b) - 如果有任何 0+ 字符 ([\s\S]*) 后跟单词边界 (\b),则匹配失败的负前瞻,相同的值捕获到第 1 组(使用 \1 反向引用),以及结尾的单词边界。

【讨论】:

    【解决方案2】:

    我会在这里使用某种Map 来记录您遇到的字符串。例如:

    String ccNumber = "CX12345";
    Map<String, Boolean> ccMap = new HashMap<>();
    
    if (ccNumber.matches("^(?!CR)(?!cr)[A-Za-z]{2}[0-9]{5,6}$")) {
        ccMap.put(ccNumber, null);
    }
    

    然后只需遍历地图的键集以获取与您的正则表达式中的模式匹配的唯一信用卡号:

    for (String key : map.keySet()) {
        System.out.println("Found a matching credit card: " + key);
    }
    

    【讨论】:

    • 您好,感谢您的回复。但是我不确定这是否适用于我们的电子邮件客户端。我相信它只能寻找一个基本的字符串。不过我会试试看。
    • if (ccNumber.matches("^(?!CR)(?!cr)[A-Za-z]{2}[0-9]{5,6}$")) = if (ccNumber.matches("(?!CR)(?!cr)[A-Za-z]{2}[0-9]{5,6}")) = if (ccNumber.matches("^(?!CR|cr)[A-Za-z]{2}[0-9]{5,6}")) 并且只会匹配符合模式的整个字符串。
    • @WiktorStribiżew 我假设 OP 将解析一组字符串,想知道每个字符串是否匹配。
    猜你喜欢
    • 2019-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-08
    • 2012-07-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多