Совпадение многострочного текста с использованием регулярного выражения - PullRequest
158 голосов
/ 06 сентября 2010

Я пытаюсь сопоставить многострочный текст с использованием Java.Когда я использую класс Pattern с модификатором Pattern.MULTILINE, я могу сопоставить, но я не могу сделать это с (?m).

Тот же шаблон с (?m) и использованием String.matches не похоже на работу.

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

Это то, что я пытался

String test = "User Comments: This is \t a\ta \n test \n\n message \n";

String pattern1 = "User Comments: (\\W)*(\\S)*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find());  //true

String pattern2 = "(?m)User Comments: (\\W)*(\\S)*";
System.out.println(test.matches(pattern2));  //false - why?

Ответы [ 4 ]

271 голосов
/ 06 сентября 2010

Во-первых, вы используете модификаторы в неверном предположении.

Pattern.MULTILINE или (?m) указывает Java принимать якоря ^ и $ для сопоставления в начале и конце каждой строки (в противном случае они совпадают только в начале / конце всей строки).

Pattern.DOTALL или (?s) указывает Java также разрешать точке совпадать с символами новой строки.

Во-вторых, в вашем случае регулярное выражение завершается неудачно, потому что вы используете метод matches(), который ожидает, что регулярное выражение будет соответствовать всей строке - что, конечно, не работает, так как есть некоторые символы осталось после совпадения (\\W)*(\\S)*.

Так что, если вы просто ищете строку, которая начинается с User Comments:, используйте регулярное выражение

^\s*User Comments:\s*(.*)

с опцией Pattern.DOTALL:

Pattern regex = Pattern.compile("^\\s*User Comments:\\s+(.*)", Pattern.DOTALL);
Matcher regexMatcher = regex.matcher(subjectString);
if (regexMatcher.find()) {
    ResultString = regexMatcher.group(1);
} 

ResultString будет содержать текст после User Comments:

38 голосов
/ 06 сентября 2010

Это не имеет ничего общего с флагом MULTILINE; вы видите разницу между методами find() и matches(). find() успешно выполняется, если совпадение можно найти в любом месте целевой строки , тогда как matches() ожидает, что регулярное выражение будет соответствовать всей строке .

Pattern p = Pattern.compile("xyz");

Matcher m = p.matcher("123xyzabc");
System.out.println(m.find());    // true
System.out.println(m.matches()); // false

Matcher m = p.matcher("xyz");
System.out.println(m.matches()); // true

Кроме того, MULTILINE не означает, что вы думаете, что он делает. Многие люди приходят к выводу, что вы должны использовать этот флаг, если ваша целевая строка содержит новые строки, то есть если она содержит несколько логических строк. Я видел несколько ответов здесь о SO на этот счет, но на самом деле все, что делает флаг, это изменяет поведение якорей ^ и $.

Обычно ^ соответствует самому началу целевой строки, а $ соответствует самому концу (или перед новой строкой в ​​конце, но мы пока оставим это в стороне). Но если строка содержит символы новой строки, вы можете выбрать ^ и $ для соответствия в начале и конце любой логической строки, а не только в начале и конце всей строки, установив флаг MULTILINE.

Итак, забудьте о том, что MULTILINE означает , и просто запомните, что делает : изменяет поведение якорей ^ и $. Режим DOTALL первоначально назывался «однострочным» (и до сих пор присутствует в некоторых разновидностях, включая Perl и .NET), и он всегда вызывал подобную путаницу. Нам повезло, что в этом случае разработчики Java получили более описательное имя, но разумной альтернативы для «многострочного» режима не было.

В Perl, где все это безумие началось, они признали свою ошибку и избавились от «многострочного» и «однострочного» режимов в регулярных выражениях Perl 6. Возможно, через двадцать лет остальной мир последует их примеру.

20 голосов
/ 06 сентября 2010

str.matches(regex) ведет себя как Pattern.matches(regex, str), который пытается сопоставить всю входную последовательность с шаблоном и возвращает

true тогда и только тогда, когда вся входная последовательность соответствует шаблону этого сопоставителя

, тогда как matcher.find() пытается найти следующую подпоследовательность входной последовательности, которая соответствует шаблону и возвращает

true тогда и только тогда, когда подпоследовательность входной последовательности соответствует шаблону этого сопоставителя

Таким образом, проблема в регулярном выражении.Попробуйте следующее.

String test = "User Comments: This is \t a\ta \ntest\n\n message \n";

String pattern1 = "User Comments: [\\s\\S]*^test$[\\s\\S]*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find());  //true

String pattern2 = "(?m)User Comments: [\\s\\S]*^test$[\\s\\S]*";
System.out.println(test.matches(pattern2));  //true

Короче говоря, часть (\\W)*(\\S)* в вашем первом регулярном выражении соответствует пустой строке, так как * означает ноль или более вхождений, а реальная совпавшая строка - User Comments:, а невся строка, как и следовало ожидать.Второй сбой, поскольку он пытается сопоставить всю строку, но не может, поскольку \\W соответствует несловесному символу, то есть [^a-zA-Z0-9_], а первый символ T, символ слова.

0 голосов
/ 31 октября 2018

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...