Java中的RegEx无法按预期工作

时间:2009-06-25 16:55:23

标签: java regex

尝试提取用双括号括起来的字符串。例如[[这是一个标记]]应该匹配。为了使事情更加优雅,应该有一个转义序列,以便像[[这个转义的令牌\]]这样的双括号项目不会匹配。

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

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

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\\]]"));
    }

}

3 个答案:

答案 0 :(得分:3)

您只需使用(^|[^\\]) 匹配字符串的开头(假设您在正则表达式上设置了MULTILINE模式)一个不是反斜杠的单个字符(包括空格,换行符等)。

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

第三点是,您不必在\[的字符类中包装单个字符(甚至包括\][]等转义字符)。

这样就可以制作以下正则表达式(原谅我为了清晰起见而删除了双重转义):

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

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

答案 1 :(得分:1)

你想要一个“零宽度负向后观断言”,即(?<!expr)。尝试:

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

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

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

答案 2 :(得分:1)

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

foo\\[[blah]]bar

我要问的是你是否支持转义反斜杠。如果你是,那么后视将不起作用。你不必寻找单个反斜杠,而是要检查奇数但未知数量,而Java lookbehinds不能像这样开放式。另外,如果转义括号里面的一个令牌 - 这有效吗?

foo[[blah\]]]bar

在任何情况下,我建议您从另一个方向出现反斜杠问题:将第一个括号前面的任意数量的转义字符(即反斜杠加上任何内容)作为令牌的一部分进行匹配。在令牌内,匹配除方括号或反斜杠之外的任意数量的字符,或任意数量的转义字符。这是实际的正则表达式:

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

...这里它是一个Java字符串文字:

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