RegEx в Java: как работать с новой строкой

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

www.foo.com/Archives/monkeys.htm
Описание веб-сайта Monkey.

www.foo.com/Archives/pigs.txt
Описание веб-сайта Pig.

www.foo.com/Archives/kitty.txt
Описание веб-сайта Китти.

www.foo.com/Archives/apple.htm
Описание веб-сайта Apple.

Если бы я хотел получить один веб-сайт вместе с его описанием, это регулярное выражение, похоже, работает с инструментом тестирования: .*www.*\\s.*Pig.*

Однако, когда я пытаюсь запустить его в своем коде, он не работает. Правильно ли это выражение? Я попытался заменить «\s» на «\n», и это, похоже, не работает.


person user415663    schedule 10.08.2010    source источник
comment
Просто напомню о потенциально более простых решениях: для моего собственного случая с явными \n, даже с предложениями Pattern.DOTALL / (?s) и двойным экранированием (\\), как указано ниже, я нашел это достаточно неудобным, чтобы просто вернуться к нерегулярному выражению строковые методы. str.contains("\n") работал нормально. str.replaceAll("\n", replacement) тоже работало. Однако я не смог найти вариант String.matches или Pattern.compile, который возвращал true, в Java 11. (В отличие от приведенных ниже решений, это не поможет, если вам нужно поймать различные типы новых строк.)   -  person Joshua Goldberg    schedule 22.09.2020


Ответы (6)


Строки, вероятно, разделены \r\n в вашем файле. И \r (возврат каретки), и \n (перевод строки) считаются символами-разделителями строк в регулярных выражениях Java, и метасимвол . не будет соответствовать ни одному из них. \s будет соответствовать этим символам, поэтому он потребляет \r, но оставляет .* для соответствия \n, что терпит неудачу. Ваш тестер, вероятно, использовал только \n для разделения строк, которые были использованы \s.

Если я прав, изменение \s на \s+ или [\r\n]+ должно заставить его работать. Это, вероятно, все, что вам нужно сделать в этом случае, но иногда вам нужно сопоставить ровно один разделитель строк или, по крайней мере, отслеживать, сколько вы сопоставляете. В этом случае вам нужно регулярное выражение, которое точно соответствует одному из трех наиболее распространенных типов разделителей строк: \r\n (Windows/DOS), \n (Unix/Linus/OSX) и \r (старые Mac). Любой из них подойдет:

\r\n|[\r\n]

\r\n|\n|\r

Обновление: начиная с Java 8 у нас есть еще один вариант, \R. Он соответствует любому разделителю строк, включая не только \r\n, но и несколько других, определенных в стандарте Unicode. Это эквивалентно этому:

\r\n|[\n\x0B\x0C\r\u0085\u2028\u2029]

Вот как вы можете его использовать:

(?im)^.*www.*\R.*Pig.*$

Параметр i делает его нечувствительным к регистру, а m переводит его в многострочный режим, позволяя ^ и $ совпадать на границах строк.

person Alan Moore    schedule 10.08.2010
comment
необработанный '\R' не разрешен java 8 final:/ - person Joe; 26.03.2019
comment
Ответ @Davinder Singh имеет двойную обратную косую черту, чтобы компенсировать декодирование строковых литералов компилятором Java. Возможно, наблюдение Джо связано с попытками использовать одну обратную косую черту, за которой следует новая буква регулярного выражения. Это, вероятно, превратилось бы в недопустимый строковый литерал Java во время компиляции. Следуя примеру Давиндера, я думаю, что использование двойной обратной косой черты должно работать для Джо. - person eel ghEEz; 27.12.2020

Для дальнейшего использования можно также использовать флаг Pattern.DOTALL для "." чтобы соответствовать даже \r или \n.

Пример:

Скажем, мы анализируем одну строку строк заголовка http, подобную этой (каждая строка заканчивается на \r\n)

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: SAMEORIGIN
Location: http://localhost:8080/blah.htm
Content-Length: 0

Этот шаблон:

final static Pattern PATTERN_LOCATION = Pattern.compile(".*?Location\\: (.*?)\\r.*?", Pattern.DOTALL);

Можно проанализировать значение местоположения, используя «matcher.group(1)».

"." в приведенном выше шаблоне будет соответствовать \r и \n, поэтому приведенный выше шаблон может фактически анализировать «Местоположение» из строк заголовка http, где могут быть другие заголовки до или после целевой строки (не то чтобы это рекомендуемый способ разобрать заголовки http).

Кроме того, вы можете использовать «?s» внутри шаблона для достижения того же эффекта.

Если вы делаете это, вам может быть лучше использовать Matcher.find().

person javaPhobic    schedule 19.05.2015
comment
DOTALL в этом случае бесполезен. OP должен знать, когда регулярное выражение использует разделитель строк, чтобы он мог быть уверен, что соответствует только одному из них. И еще менее полезно в вашем примере, где все интересующее содержимое содержится в одной строке. Я почти никогда не использую режим DOTALL; кажется, что это создает больше проблем, чем решает. - person Alan Moore; 19.05.2015
comment
Вы, вероятно, правы, но в моем примере это полезно, моя единственная строка для анализа фактически содержала все строки. - person javaPhobic; 19.05.2015
comment
Особенность режима DOTALL в том, что он значительно расширяет возможности для шалостей. Например, когда я применяю ваше регулярное выражение к вашим демонстрационным данным, первый .*? использует все заголовки, перечисленные выше заголовка Location. Я знаю, что вас интересует только URL-адрес, который вы фиксируете в группе № 1, но вы все равно получите его с выключенным режимом DOTALL, и вы сэкономите много ненужной работы для регулярного выражения. - person Alan Moore; 19.05.2015
comment
Нет, без DOTALL он не сможет соответствовать . с \r или \n. Следовательно, местоположение не может быть проанализировано. Если я разделю строку на основе границ строк и передам только строку местоположения в регулярное выражение без DOTALL, это сработает. - person javaPhobic; 20.05.2015
comment
Нет, я говорю, что вам не нужно сопоставлять какие-либо разделители строк. "Location: (.*)", вероятно, будет работать нормально, но я бы использовал якоря, чтобы быть в безопасности: "(?m)^Location: (.*)$" - person Alan Moore; 20.05.2015
comment
Вы говорите о том, что вы уже разбили заголовок на строки, я говорю о том, когда весь заголовок входит в одну строку, оба используют match(). С другой стороны, использование find() — это совсем другая история. - person javaPhobic; 12.01.2017

попробуй это

([^\r]+\r[^\r])+
person user414661    schedule 10.08.2010

Работает на меня:

import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Foo {
  public static void main(String args[]) {
    Pattern p = Pattern.compile(".*www.*\\s.*Pig.*");
    String s = "www.foo.com/Archives/monkeys.htm\n"
             + "Description of Monkey's website.\n"
             + "\n"
             + "www.foo.com/Archives/pigs.txt\n"
             + "Description of Pig's website.\n"
             + "\n"
             + "www.foo.com/Archives/kitty.txt\n"
             + "Description of Kitty's website.\n"
             + "\n"
             + "www.foo.com/Archives/apple.htm\n"
             + "Description of Apple's website.\n";
    Matcher m = p.matcher(s);
    if (m.find()) {
      System.out.println(m.group());
    } else {
      System.out.println("ERR: no match");
    }
  }
}

Возможно, проблема заключалась в том, как вы использовали объекты Pattern и Matcher?

person maerics    schedule 10.08.2010
comment
Это работает, только если строки всегда отформатированы с \n, как в unix - person Gary; 10.08.2010

String str="I am  a   "+"\n  Man    of  Peace"+"\t"+"   .";

str=str.replaceAll("[\\s|\\t|\\r\\n]+"," ").trim();
System.out.println(str);

Этот пример выше работает для tabSpaces, newLines и обычных пробелов. И я использовал метод обрезки java.lang.String, чтобы удалить все дополнительные пробелы в 'str'. Я надеюсь, что это поможет вам и другим замечательным людям здесь.

person davinder singh    schedule 02.06.2020

Эта версия соответствует новым строкам, которые могут быть Windows (\r\n) или Unix (\n).

Pattern p = Pattern.compile("(www.*)((\r\n)|(\n))(.*Pig.*)");
String s = "www.foo.com/Archives/monkeys.htm\n"
           + "Description of Monkey's website.\n"
           + "\r\n"
           + "www.foo.com/Archives/pigs.txt\r\n"
           + "Description of Pig's website.\n"
           + "\n"
           + "www.foo.com/Archives/kitty.txt\n"
           + "Description of Kitty's website.\n"
           + "\n"
           + "www.foo.com/Archives/apple.htm\n"
           + "Description of Apple's website.\n";
Matcher m = p.matcher(s);
if (m.find()) {
  System.out.println("found: "+m.group());
  System.out.println("website: "+m.group(1));
  System.out.println("description: "+m.group(5));
}
System.out.println("done");
person Gary    schedule 10.08.2010