Java: Как определить, почему соответствие шаблона регулярного выражения не удается?

Я использую регулярное выражение для соответствия тому, соответствует ли шаблон, но я также хочу знать, когда он терпит неудачу.

Например, скажем, у меня есть шаблон "N {1,3} Y". Я сопоставляю его со строкой "NNNNY". Я хотел бы знать, что он потерпел неудачу, потому что было слишком много Ns. Или, если я сопоставляю его со строкой "XNNY", я хотел бы знать, что она потерпела неудачу, потому что в строке был недопустимый символ "X".

От взгляда на API Java регулярного выражения (java.util.regex) дополнительная информация только кажется доступной из класса Matcher, когда совпадение выполнено успешно.

Есть ли способ решить эту проблему? Или это регулярное выражение даже в этом сценарии?

Ответ 1

Я думаю, вы должны использовать парсер, а не простые регулярные выражения.

Регулярные выражения хороши, обеспечивая совпадения строк, но не совсем так, предоставляя NON-match, не говоря уже о том, почему совпадение не удалось.

Ответ 2

То, о чем вы просите, потребует, чтобы синтаксический анализатор определял ближайшую строку, которая фактически соответствует вашему выражению. Это нетривиальная проблема, которая, вероятно, будет выполняться в экспоненциальном времени (например, поиск всех возможных строк одинаковой длины, чтобы найти совпадение.)

Итак, короче говоря, нет.

Ответ 3

Это может сработать, но я не знаю, так ли оно вам нужно.

Когда вы используете matches, он терпит неудачу, если вся последовательность не соответствует, но вы все равно можете использовать find, чтобы увидеть, содержит ли остальная часть последовательности шаблон и, таким образом, понять, почему это не удалось:

import java.util.regex.*;
import static java.lang.System.out;
class F { 
    public static void main( String ... args ) { 
        String input = args[0];
        String re = "N{1,3}Y";
        Pattern p = Pattern.compile(re);
        Matcher m = p.matcher(input);
        out.printf("Evaluating: %s on %s%nMatched: %s%n", re, input, m.matches() );
        for( int i = 0 ; i < input.length() ; i++ ) { 
           out.println();
           boolean found = m.find(i);
           if( !found ) { 
               continue;
           }
           int s = m.start();
           int e = m.end();
           i = s;
           out.printf("m.start[%s]%n"
                     +"m.end[%s]%n"
                     +"%s[%s]%s%n",s,e,
                     input.substring(0,s), 
                     input.substring(s,e), 
                     input.substring(e) );
        }

    }
}

Вывод:

C:\Users\oreyes\java\re>java F NNNNY
Evaluating: N{1,3}Y on NNNNY
Matched: false

m.start[1]
m.end[5]
N[NNNY]

m.start[2]
m.end[5]
NN[NNY]

m.start[3]
m.end[5]
NNN[NY]


C:\Users\oreyes\java\re>java F XNNY
Evaluating: N{1,3}Y on XNNY
Matched: false

m.start[1]
m.end[4]
X[NNY]

m.start[2]
m.end[4]
XN[NY]

В первом выводе: N[NNNY] вы можете указать там, где слишком много N, во втором: X[NNY] присутствовал X.

Здесь другой выход

C:\Users\oreyes\java\re>java F NYXNNXNNNNYX
Evaluating: N{1,3}Y on NYXNNXNNNNYX
Matched: false

m.start[0]
m.end[2]
[NY]XNNXNNNNYX

m.start[7]
m.end[11]
NYXNNXN[NNNY]X

m.start[8]
m.end[11]
NYXNNXNN[NNY]X

m.start[9]
m.end[11]
NYXNNXNNN[NY]X

Шаблон существует, но все выражение не совпадает.

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

совпадение похоже на /^YOURPATTERNHERE$/

lookAt похоже на /^YOURPATTERNHERE/

find похож на /YOURPATTERNHERE/

Надеюсь, это поможет.

Ответ 4

Для простых выражений типа "N {1,3} Y" вы найдете решение без инструментов самостоятельно. Но для более сложных выражений мой опыт подсказывает:

  • разделить большие выражения на более мелкие и проверить их самостоятельно.
  • поскольку вам нравится иметь быструю обратную связь, вы можете использовать интерактивную оболочку, такую ​​как Beanshell, для быстрого тестирования некоторых строк и шаблонов без большой компиляции, public static void main (bla...) и так далее. Или попробуйте scala для этой задачи. Sed - еще один мощный инструмент для использования регулярных выражений, но в синтаксисе есть тонкие различия, которые могут вводить новые ошибки.
  • Часто маскирование является проблемой. Поскольку обратная косая черта нуждается в другой обратной косой чертой, может быть преимуществом читать выражение из JTextField, где вам не нужно столько маскировки.
  • Напишите небольшую платформу тестирования для своих выражений, где вы можете легко вставлять свои выражения, тестировать строки, создавать автоматические тестовые данные и получать визуальную обратную связь.