【问题标题】:A regex that doesn't match with this character sequence与此字符序列不匹配的正则表达式
【发布时间】:2013-01-12 08:05:28
【问题描述】:

这是我的正则表达式,我正在尝试搜索所有特殊字符,以便将它们转义。

(\(|\)|\[|\]|\{|\}|\?|\+|\\|\.|\$|\^|\*|\||\!|\&|\-|\@|\#|\%|\_|\"|\:|\<|\>|\/|\;|\'|\`|\~)

我的问题是,我不想只在顺序出现时转义一些特殊字符

喜欢这个(.*)

那么,让我们来看一个例子。

Sting message = "Hi, Mr.Xyz! Your account number is :- (1234567890) , (,*) &$@%#*(....))(((";

根据当前的正则表达式转义后,我得到的是,

Hi, Mr\.Xyz\! Your account number is \:\- \(1234567890\) , \(,\*\) \&\$\@\%\#\*\(\.\.\.\.\)\)\(\(\(

但是不想逃避这部分(.*)想保持原样。

我上面的正则表达式只用于搜索,所以我只是不想匹配这部分(.*),我的问题将得到解决

任何人都可以建议不逃避该部分字符串的正则表达式吗?

【问题讨论】:

  • 字符串中没有我可以看到的部分。
  • 你真的应该考虑使用[character classes]而不是a|l|t|e|r|n|a|t|i|o|n
  • 序列(.*) 在您的正则表达式输入中不存在。
  • 这个任务超出了正则表达式的能力。如果没有完整的问题规范,将很难为您提供帮助,即 (.*) 是唯一不应该转义的序列。您似乎想要的是转义任意字符串中的特殊字符,同时检测嵌入在字符串中的正则表达式。该任务需要人工干预,因为没有规则可以消除所有可能的情况。
  • 为什么要使用正则表达式,如果您可以简单地遍历字符串,逐个字符检查它是否在您的特殊字符列表中而下一个不在,那么它可能会很复杂?它可能也会运行得更快。正则表达式看起来很优雅,因为它们可以简洁地编写。但是,在后台,它们必须在运行时编译并执行。

标签: java regex


【解决方案1】:

请参阅 @nhahtdh 了解如何使用正则表达式。

作为替代方案,这是一个不使用正则表达式的解决方案,而是使用 Guava 的 CharMatcher

private static final CharMatcher SPECIAL
    = CharMatcher.anyOf("allspecialcharshere");
private static final String NO_ESCAPE = "(.*)";

public String doEncode(String input)
{
    StringBuilder sb = new StringBuilder(input.length());

    String tmp = input;

    while (!tmp.isEmpty()) {
        if (tmp.startsWith(NO_ESCAPE)) {
            sb.append(NO_ESCAPE);
            tmp = tmp.substring(NO_ESCAPE.length());
            continue;
        }
        char c = tmp.charAt(0);
        if (SPECIAL.matches(c))
            sb.append('\\');
        sb.append(c);
        tmp = tmp.substring(1);
    }

    return sb.toString();
}

【讨论】:

  • 我试过你的正则表达式,(?=[()[]{}?+\\.$^*|!&\-@#%_:/;'~])( ?!\Q(.*)\E)。但没有帮助,它确实与 (.*) 匹配
  • 我希望它与().* 单独匹配,但如果这个(.*) 以这个顺序组合在一起,我不想匹配它
  • 它确实适用于我的测试,所以你说的很奇怪。
  • 嗯不,确实,我忽略了一些东西。但是,CharMatcher 版本可以工作。
  • @fge: 替换部分作为代码相当于 Java 中的 Matcher 循环。
【解决方案2】:

这个答案只是为了证明这种可能性。在生产代码中使用它是有问题的。

可以使用 Java String replaceAll 函数:

String input = "Hi, Mr.Xyz! Your account number is :- (1234567890) , (.*) &$@%#*(....))(((";
String output = input.replaceAll("\\G((?:[^()\\[\\]{}?+\\\\.$^*|!&@#%_\":<>/;'`~-]|\\Q(.*)\\E)*+)([()\\[\\]{}?+\\\\.$^*|!&@#%_\":<>/;'`~-])", "$1\\\\$2");

结果:

"Hi, Mr\.Xyz\! Your account number is \:\- \(1234567890\) , (.*) \&\$\@\%\#\*\(\.\.\.\.\)\)\(\(\("

另一个测试:

String input = "(.*) sdfHi test message <> >>>>><<<<f<f<,,,,<> <>(.*) sdf (.*)  sdf (.*)";

结果:

"(.*) sdfHi test message \<\> \>\>\>\>\>\<\<\<\<f\<f\<,,,,\<\> \<\>(.*) sdf (.*)  sdf (.*)"

说明

原始正则表达式:

\G((?:[^()\[\]{}?+\\.$^*|!&@#%_":<>/;'`~-]|\Q(.*)\E)*+)([()\[\]{}?+\\.$^*|!&@#%_":<>/;'`~-])

注意\ 在字符串中指定正则表达式时会再次转义," 需要转义。上面可以看到生成的字符串中的正则表达式。

原始替换字符串:

$1\\$2

由于$在替换字符串中有特殊含义,并且您想保留它为$2,您需要转义\,这样\就不会转义$。并将替换字符串放在带引号的字符串中,您需要将\ 的数量加倍以转义\

在我们剖析怪物之前,让我们谈谈这个想法。我们将使用非特殊字符,以及我们不想替换的序列,并且尽可能多地使用。下一个字符要么是一个特殊字符,不是我们不想替换的序列,要么是字符串的结尾(这意味着我们已经找到了所有需要替换的字符,如果有的话)。

当然,我们可以将任意字符串视为由以下许多连续模式组成:[0 or more (non-special character or special pattern not to be replace)][special character],字符串以[0 or more (non-special character or special pattern not to be replace)] 结尾。

replaceAll 函数在与没有\G 的正则表达式一起使用时可能会找到不连续的匹配项,这可能会在不被替换的序列中间切开并搞砸。 \G 表示上一场比赛的边界,可用于确保下一场比赛从上一场比赛结束的地方开始。

  • \G:从最后一场比赛开始

  • ((?:[^()\[\]{}?+\\.$^*|!&amp;@#%_":&lt;&gt;/;'`~-]|\Q(.\*)\E)*+):捕获0个或多个,非特殊字符或特殊模式不被替换。请注意,我在* 之后添加了所有格限定符+。这将防止引擎在找不到我们在此之后指定的特殊字符时回溯。

    • [^()\[\]{}?+\\.$^*|!&amp;@#%_":&lt;&gt;/;'`~-]:特殊字符的否定字符类。

    • \Q(.*)\E:特殊序列(.*) 不可替换,由\Q\E 引用。

  • ([()\[\]{}?+\\.$^*|!&amp;@#%_":&lt;&gt;/;'`~-]):捕获单个特殊字符。

整个正则表达式将匹配最小长度为 1 的字符串(特殊字符)。第一个捕获组包含不应替换的部分,第二个捕获组包含应替换的特殊字符。

【讨论】:

  • 我相信您的解决方案会更好!谢谢。它适用于大多数情况。我发现了一种情况,它不能按设计工作。考虑用你的正则表达式 Hi test message &lt;&gt; &gt;&gt;&gt;&gt;&gt;&lt;&lt;&lt;&lt;&lt;&lt;,,,,&lt;&gt; &lt;&gt; 替换这个字符串,你会得到这样的结果 Hi test message (.*) \&gt;\&gt;\&gt;\&gt;\&gt;\&lt;\&lt;\&lt;\&lt;\&lt;\&lt;,,,,(.*) \(\.\*\)
  • @AvinashNair:请参阅编辑。很高兴我从这个错误中学到了一些东西。无论如何,我认为你应该使用 fge 的解决方案。比这一堆正则表达式要清晰得多。
  • 感谢您的帮助。你帮我学到了新东西!你们真是不可思议!
猜你喜欢
  • 2018-01-09
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多