RegEx в Java не работает, как я ожидал - PullRequest
0 голосов
/ 25 июня 2009

Попытка извлечь строки, заключенные в двойные скобки. Например, [[это один токен]], который должен соответствовать. Чтобы сделать вещи более элегантными, должна быть escape-последовательность, чтобы элементы в двойных скобках, такие как \ [[escape-токен \]], не сопоставлялись.

Шаблон [^\\\\]([\\[]{2}.+[^\\\\][\\]]{2}) с «группой 1» для извлечения токена близок, но есть ситуации, когда он не работает. Кажется, проблема в том, что первое утверждение «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\\]]"));
    }

}

Ответы [ 3 ]

3 голосов
/ 25 июня 2009

Вы можете просто использовать (^|[^\\]), который будет либо соответствовать началу строки (при условии, что вы задали режим MULTILINE в своем регулярном выражении) или один символ, который не является обратной косой чертой (включая пробелы, переводы строки и т. д.).

Вы также захотите заменить .+ на .+?, потому что в противном случае строка, такая как "[[one]] and [[two]]", будет рассматриваться как одиночное совпадение, где "one]] and [[two" считается заключенным в скобки.

Третий момент заключается в том, что вам не нужно переносить один символ (даже экранированные, такие как \[ или \]) в классе символов с [].

Так что это сделало бы следующее регулярное выражение (простите, я убрал двойную экранированность для ясности):

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

(Также обратите внимание, что вы не можете экранировать символ экранирования с помощью своего регулярного выражения. Две косые черты до [ не будут обрабатываться как одиночная (экранированная) косая черта, но будут указывать одиночную (неоткрытую) косую черту и экранированный кронштейн.)

1 голос
/ 25 июня 2009

Что должно произойти с этой строкой? (Фактическое содержимое строки, а не литерал Java.)

foo\\[[blah]]bar

Я спрашиваю, поддерживаете ли вы побег обратной косой черты. Если да, то взгляд назад не сработает. Вместо того, чтобы искать одну обратную косую черту, вам придется проверять их на нечетное, но неизвестное количество, и взгляды Java не могут быть такими же открытыми. А как насчет экранированных скобок внутри токена - действительно ли это?

foo[[blah\]]]bar

В любом случае, я предлагаю вам прийти к проблеме обратной косой черты с другой стороны: сопоставить любое количество экранированных символов (то есть обратную косую черту плюс что-либо), непосредственно предшествующее первой скобке, как часть токена. Внутри токена укажите любое количество символов, кроме квадратных скобок или обратной косой черты, или любое количество экранированных символов. Вот фактическое регулярное выражение:

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

... и вот он как строковый литерал Java:

"(?<!\\\\)(?:\\\\.)*+\\[\\[((?:[^\\[\\]\\\\]++|\\\\.)*+)\\]\\]"
1 голос
/ 25 июня 2009

Требуется «отрицательное утверждение с нулевой шириной за спиной», которое равно (?<!expr). Попробуйте:

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

На самом деле, это можно упростить и сделать более общим, вырезав некоторые из этих ненужных скобок и добавив отрицательный вид сзади для закрывающей скобки. (Ваша версия также потерпит неудачу, если в середине строки будет экранированная скобка, например [[text\]]moretext]]).

(?<!\\\\)(\\[{2}.*?(?<!\\\\)\\]{2}) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...