【问题标题】:RegEx in Java not working as I expectedJava中的RegEx没有按我的预期工作
【发布时间】:2009-06-25 16:55:22
【问题描述】:

试图提取用双括号括起来的字符串。例如应该匹配的 [[this is one token]]。为了使事情更优雅,应该有一个转义序列,以便像 \[[this escaped token\]] 这样的双括号项目不会被匹配。

使用“group 1”提取令牌的模式[^\\\\]([\\[]{2}.+[^\\\\][\\]]{2}) 很接近,但在某些情况下它不起作用。问题似乎是第一个“not”语句被评估为“除了反斜杠之外的任何内容”。问题是,“任何东西”不包括“无”。那么,是什么让这个模式匹配“除了反斜杠之外的任何字符或任何字符”?

这是一个显示所需行为的单元测试:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.TestCase;

public class RegexSpike extends TestCase {
    private String regex;
    private Pattern pattern;
    private Matcher matcher;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        regex = "[^\\\\]([\\[]{2}.+[^\\\\][\\]]{2})";
        pattern = Pattern.compile(regex);
    }

    private String runRegex(String testString) {
        matcher = pattern.matcher(testString);
        return matcher.find() ? matcher.group(1) : "NOT FOUND";
    }

    public void testBeginsWithTag_Passes() {
        assertEquals("[[should work]]", runRegex("[[should work]]"));
    }

    public void testBeginsWithSpaces_Passes() {
        assertEquals("[[should work]]", runRegex("   [[should work]]"));
    }

    public void testBeginsWithChars_Passes() {
        assertEquals("[[should work]]", runRegex("anything here[[should
work]]"));
    }

    public void testEndsWithChars_Passes() {
        assertEquals("[[should work]]", runRegex("[[should
work]]with anything here"));
    }

    public void testBeginsAndEndsWithChars_Passes() {
        assertEquals("[[should work]]", runRegex("anything here[[should
work]]and anything here"));
    }

    public void testFirstBracketsEscaped_Fails() {
        assertEquals("NOT FOUND", runRegex("\\[[should NOT work]]"));
    }

    public void testSingleBrackets_Fails() {
        assertEquals("NOT FOUND", runRegex("[should NOT work]"));
    }

    public void testSecondBracketsEscaped_Fails() {
        assertEquals("NOT FOUND", runRegex("[[should NOT work\\]]"));
    }

}

【问题讨论】:

  • 没有什么意思是 NULL 或空白吗?

标签: java regex


【解决方案1】:

您可以简单地使用(^|[^\\]),它要么匹配字符串的开头(前提是您在正则表达式上设置了MULTILINE模式)单个不是反斜杠的字符(包括空格、换行符等)。

您还需要将.+ 替换为.+?,否则"[[one]] and [[two]]" 之类的字符串将被视为单个匹配项,其中"one]] and [[two" 被视为在括号之间。

第三点是您不必将单个字符(即使是转义字符,例如 \[\])包装在带有 [] 的字符类中。

这样就可以生成以下正则表达式(请原谅我为了清楚起见删除了双重转义):

(^|[^\\])(\[{2}.+?[^\\]\]{2})

(另请注意,您不能使用正则表达式转义转义字符。[ 之前的两个斜杠不会被解析为单个(转义)斜杠,但会指示一个(未转义)斜杠和一个转义括号。)

【讨论】:

    【解决方案2】:

    您想要一个“零宽度的否定后向断言”,即(?<!expr)。试试:

    (?<!\\\\)([\\[]{2}.+[^\\\\][\\]]{2}) 
    

    实际上,这可以通过删除一些不必要的括号来简化并使其更通用,并为右括号添加一个否定的lookbehind,也是。 (如果字符串中间有一个转义括号,您的版本也会失败,例如[[text\]]moretext]])。

    (?<!\\\\)(\\[{2}.*?(?<!\\\\)\\]{2}) 
    

    【讨论】:

      【解决方案3】:

      这个字符串会发生什么? (实际的字符串内容,不是 Java 文字。)

      foo\\[[blah]]bar
      

      我要问的是您是否支持转义的反斜杠。如果你是,向后看将不起作用。您必须检查奇数但未知数量的反斜杠,而不是寻找单个反斜杠,并且 Java 后视不能像那样是开放式的。另外,转义括号 inside 一个标记怎么办——这有效吗?

      foo[[blah\]]]bar
      

      无论如何,我建议你从另一个方向来解决反斜杠问题:匹配紧接在第一个括号之前的任意数量的转义字符(即反斜杠加上任何内容)作为令牌的一部分。在标记内,匹配除方括号或反斜杠之外的任意数量的字符,或任意数量的转义字符。这是实际的正则表达式:

      (?<!\\)(?:\\.)*+\[\[((?:[^\[\]\\]++|\\.)*+)\]\]
      

      ...这里是 Java 字符串文字:

      "(?<!\\\\)(?:\\\\.)*+\\[\\[((?:[^\\[\\]\\\\]++|\\\\.)*+)\\]\\]"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-05-22
        • 2012-03-14
        • 1970-01-01
        • 2015-11-17
        • 2010-10-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多