Регулярное выражение для извлечения пар метка-значение в Java

StackOverflow https://stackoverflow.com/questions/393121

  •  23-08-2019
  •  | 
  •  

Вопрос

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

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, поскольку файл содержит и другую информацию.

Это было полезно?

Решение

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

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

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

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

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

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

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

Другие советы

Возможно, вы захотите изучить 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"));

Предполагая, что «содержимое» — это строка, содержащая содержимое файла, ваша основная проблема заключается в том, что вы используете 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.

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

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;

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

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

Не Java-человек, но не стал бы "Address: (.*)$" работа?

Редактировать:Без шаблона.multiline | Паттерн. Dotall Option он должен соответствовать только на этой строке.

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

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

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

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

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

Вам обязательно стоит проверить ЯМЛ.

Вы могли бы попробовать JYaml.

Самое приятное, что он имеет реализации на многих языках.

PS Я попробовал образец текста в ЯМЛ::XS, и это работает отлично.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top