регулярные выражения для разбора форматированных чисел - PullRequest
2 голосов
/ 26 августа 2009

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

 Frc consts  --     1.4362                 1.4362                 5.4100
 IR Inten    --     0.0000                 0.0000                 0.0000
 Atom AN      X      Y      Z        X      Y      Z        X      Y      Z
    1   6     0.00   0.00   0.00     0.00   0.00   0.00     0.00   0.00   0.00
    2   1     0.40  -0.20   0.23    -0.30  -0.18   0.36     0.06   0.42   0.26

Это отдельные строки, все со значительным начальным пробелом и могут быть или не быть значительными завершающими пробелами). Они состоят из 72, 72, 78, 78 и 78 символов. Я могу вывести границы между полями. Их можно описать (используя формат Fortran (nx = nspaces, алфавит = n, in = целое число в n столбцах, fm.n = число m символов с n местами после десятичной запятой):

 (1x,a14,1x,f10.4,13x,f10.4,13x,f10.4)
 (1x,a14,1x,f10.4,13x,f10.4,13x,f10.4)
 (1x,a4,a4,3(2x,3a7))
 (1x,2i4,3(2x,3f7.2))
 (1x,2i4,3(2x,3f7.2))

У меня есть потенциально несколько тысяч различных форматов (которые я могу автоматически генерировать или обрабатывать), и я описываю их с помощью регулярных выражений, описывающих компоненты. Таким образом, если regf10_4 представляет регулярное выражение для любой строки, удовлетворяющей ограничению f10.4, я могу создать регулярное выражение в форме:

COMMENTS 
      (\s
      .{14}
      \s
      regf10_4,
      \s{13}
      regf10_4,
      \s{13}
      regf10_4,
)

Я хотел бы знать, существуют ли регулярные выражения, которые удовлетворяют повторному использованию таким образом. Существует множество способов, которыми компьютеры и люди создают числа, совместимые, например, с f10.4. Я полагаю, что все приведенные ниже допустимые входные данные и / или выходные данные для фортрана (мне не требуются суффиксы формы f или d, как в 12.4f) [форматирование в SO следует читать как отсутствие начальных пробелов для первого, один для второй и т. д.]

-1234.5678
 1234.5678
            // missing number
 12345678.
 1.
 1.0000000
    1.0000
        1.
 0.
        0.
     .1234
    -.1234
    1E2
    1.E2
    1.E02
  -1.0E-02
**********  // number over/underflow

Они должны быть устойчивы к содержимому соседних полей (например, проверять только 10 символов в точной позиции. Таким образом, для (a1, f5.2, a1) допустимо следующее:

a-1.23b   // -1.23
- 1.23.   // 1.23
3 1.23-   // 1.23

Я использую Java, поэтому мне нужны конструкции регулярных выражений, совместимые с Java 1.6 (например, не расширения Perl)

Ответы [ 3 ]

2 голосов
/ 27 августа 2009

Насколько я понимаю, каждая строка содержит одно или несколько полей фиксированной ширины, которые могут содержать метки, пробелы или данные разных видов. Если вы знаете ширину и типы полей, извлечение их данных является простым делом substring(), trim() и (необязательно) Whatever.parseWhatever(). Регулярные выражения не могут облегчить эту работу - фактически, все, что они могут сделать, это сделать ее намного сложнее.

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

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

1 голос
/ 26 августа 2009

Вы можете начать с этого и идти оттуда.

Это регулярное выражение соответствует всем числам, которые вы указали.
К сожалению, он также соответствует 3 в 3 1,23-

// [-+]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][-+]?[0-9]+)?
// 
// Match a single character present in the list “-+” «[-+]?»
//    Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
// Match the regular expression below «(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)»
//    Match either the regular expression below (attempting the next alternative only if this one fails) «[0-9]+(?:\.[0-9]*)?»
//       Match a single character in the range between “0” and “9” «[0-9]+»
//          Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
//       Match the regular expression below «(?:\.[0-9]*)?»
//          Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
//          Match the character “.” literally «\.»
//          Match a single character in the range between “0” and “9” «[0-9]*»
//             Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
//    Or match regular expression number 2 below (the entire group fails if this one fails to match) «\.[0-9]+»
//       Match the character “.” literally «\.»
//       Match a single character in the range between “0” and “9” «[0-9]+»
//          Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
// Match the regular expression below «(?:[eE][-+]?[0-9]+)?»
//    Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
//    Match a single character present in the list “eE” «[eE]»
//    Match a single character present in the list “-+” «[-+]?»
//       Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
//    Match a single character in the range between “0” and “9” «[0-9]+»
//       Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
Pattern regex = Pattern.compile("[-+]?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][-+]?[0-9]+)?");
Matcher matcher = regex.matcher(document);
while (matcher.find()) {
    // matched text: matcher.group()
    // match start: matcher.start()
    // match end: matcher.end()
} 
0 голосов
/ 26 августа 2009

Это только частичный ответ, но я был предупрежден о Сканере в Java 1.5, который может сканировать текст и интерпретировать числа, которые дают BNF для чисел, которые можно сканировать и интерпретировать с помощью этой утилиты Java. В принципе, я думаю, что BNF можно использовать для построения регулярных выражений.

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