Регулярное выражение для извлечения пар метка-значение в Java - PullRequest
7 голосов
/ 25 декабря 2008

У меня есть файл, содержащий несколько строк, похожих на:

Name: Peter
Address: St. Serrano número 12, España
Country: Spain

И мне нужно извлечь адрес с помощью регулярного выражения, учитывая, что он может содержать точки, специальные символы (ñ, ç), символы ...

Текущий код работает, но выглядит довольно некрасиво:.

Pattern p = Pattern.compile("^(.+?)Address: ([a-zA-Z0-9ñÑçÇáéíóú., ]+)(.+?)$",
                            Pattern.MULTILINE | Pattern.DOTALL);
Matcher m = p.matcher(content);
if (m.matches()) { ... }

Редактировать: Поле адреса также можно разделить на несколько строк

Name: Peter
Address: St. Serrano número 12,   
Madrid
España
Country: Spain

Редактировать: я не могу использовать объект Properties или синтаксический анализатор YAML, так как файл также содержит другую информацию.

Ответы [ 7 ]

6 голосов
/ 27 декабря 2008

Я не очень хорошо знаю объекты регулярных выражений Java, но что-то вроде этого будет делать это:

^Address:\s*((?:(?!^\w+:).)+)$

при условии, что включены режимы многострочного и точечного подключения.

Это будет соответствовать любой строке, начинающейся с Address, за которой следует что-либо до символа новой строки и одного слова, за которым следует двоеточие.

Если вы знаете, что следующее поле должно быть "Страна", вы можете немного упростить это:

^Address:\s*((?:(?!^Country:).)+)$

Хитрость заключается в утверждении о предвкушении в повторяющейся группе. '(?!Страна:).' будет соответствовать всему, кроме начала строки 'Country:', поэтому мы просто вставим ее в не захватывающие скобки (?: ...) и количественно определим ее с помощью +, а затем сгруппируем все это в обычных собирающих скобках.

3 голосов
/ 26 декабря 2008

Предполагая, что "content" - это строка, содержащая содержимое файла, ваша основная проблема заключается в том, что вы используете matches() там, где вы должны использовать find().

Pattern p = Pattern.compile("^Address:\\s*(.*)$", Pattern.MULTILINE);
Matcher m = p.matcher(content);
if ( m.find() )
{
  ...
}

В других ответах о режимах MULTLINE и DOTALL, похоже, есть некоторая путаница. MULTILINE - это то, что позволяет якорям ^ и $ соответствовать началу и концу логической линии соответственно. DOTALL позволяет точке (точка, полная остановка, что угодно) соответствовать символам разделителя строк, таким как \n (перевод строки) и \r (возврат каретки). Это регулярное выражение должно использовать режим MULTILINE, а не должно использовать режим DOTALL.

3 голосов
/ 25 декабря 2008

Возможно, вы захотите заглянуть в класс Properties вместо регулярных выражений. Он предоставляет вам способы управления обычным текстом или файлами XML для представления пар ключ-значение.

Таким образом, вы можете прочитать в своем примере файла и затем получить значения после загрузки в Properties объект:

Properties properties = new Properties();
properties.load(/* InputStream of your file */);

Assert.assertEquals("Peter", properties.getProperty("Name"));
Assert.assertEquals("St. Serrano número 12, España", properties.getProperty("Address"));
Assert.assertEquals("Spain", properties.getProperty("Country"));
1 голос
/ 26 декабря 2008

Я не хочу быть в грязи, но вы должны использовать регулярное выражение? Почему бы не пощадить свою будущую себя (или других) от головной боли и сделать:

String line = reader.readLine();
while(line != null)
{
    line = line.trim();
    if(line.startsWith("Address: "))
    {
        return line.substr("Address: ".length()).trim();
    }
    line = reader.readLine();
}
return null;

Конечно, это можно немного параметризировать и поместить в метод.

В противном случае я бы поддержал предложения по свойствам или JYaml.

0 голосов
/ 26 декабря 2008

Вы обязательно должны проверить YAML .

Вы можете попробовать JYaml .

Лучше всего он имеет реализации на многих языках.

ps Я пробовал образец текста в YAML :: XS , и он отлично работает.

0 голосов
/ 25 декабря 2008

Может ли он содержать перевод строки? Если он не может содержать символ новой строки, вам не нужно использовать многострочный модификатор, и вместо этого можно сделать

Pattern p = Pattern.compile("^Address: (.*)$");

Если это возможно, я могу придумать альтернативу

Pattern p = Pattern.compile("Address: (.*)\nCountry", Pattern.MULTILINE);

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

0 голосов
/ 25 декабря 2008

Не человек Java, но не будет "Address: (.*)$" работать?

Редактировать: Без шаблона. МУЛЬТИЛАЙН | Опция Pattern.DOTALL должна соответствовать только в этой строке.

...