Как мне вернуть только ненулевые группы захвата для каждого совпадения? - PullRequest
2 голосов
/ 10 марта 2012

Я использую это регулярное выражение для разбора строк CSV в APEX:

Pattern csvPattern = Pattern.compile('(?:^|,)(?:\"([^\"]+|\"\")*\"|([^,]+)*)');

Это прекрасно работает, но возвращает две группы для каждого соответствия (одну для указанных значений и одну для неприведенные значения).См. Ниже:

Matcher csvMatcher = csvPattern.matcher('"hello",world');
Integer m = 1;
while (csvMatcher.find()) {
    System.debug('Match ' + m);
    for (Integer i = 1; i <= csvMatcher.groupCount(); i++) {
        System.debug('Capture group ' + i + ': ' + csvMatcher.group(i));
    }
    m++;
}

Запуск этого кода вернет следующее:

[5]|DEBUG|Match 1
[7]|DEBUG|Capture group 1: hello
[7]|DEBUG|Capture group 2: null
[5]|DEBUG|Match 2
[7]|DEBUG|Capture group 1: null
[7]|DEBUG|Capture group 2: world

Я бы хотел, чтобы в каждом совпадении возвращался только ненулевой захват.Это возможно?

Ответы [ 2 ]

3 голосов
/ 11 марта 2012

Это на самом деле трудная вещь.
Это может быть сделано с заглядыванием / утверждениями за спиной.
Хотя не очень интуитивно понятно.

Это выглядит примерно так:
(?:^|,)(\s*"(?=(?:[^"]+|"")*"\s*(?:,|$)))?((?<=")(?:[^"]+|"")*(?="\s*(?:,|$))|[^,]*)

Как это работает - выстроить текстовое тело после первой кавычки " в допустимом поле в кавычках.Если это поле не является допустимым в кавычках, оно выравнивается по самой кавычке.В этот момент текстовое тело может быть захвачено либо как поле без кавычек, либо как поле с кавычками минус кавычки, в одном буфере захвата.

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

Вот прототип на Perl с прокомментированным расширенным регулярным выражением под ним.
Удачи!

$samp = '  "hello " , world",,me,and,th""is, or , "tha""t"  ';

$regex = '
  (?: ^ | , )
  (\s*" (?= (?:[^"]+|"")* " \s*(?:,|$) ) )?
  (
     (?<=") (?:[^"]+|"")* (?="\s*(?:,|$) )
   |
     [^,]*
  )
';
while ($samp =~ /$regex/xg)
{
   print "'$2'\n";
}

Вывод

'hello '
' world"'
''
'me'
'and'
'th""is'
' or '
'tha""t'

Комментируется

(?: ^ | , )          # Consume comma (or BOL is fine)

(                    # Capture group 1, capture '"' only if a complete quoted field
   \s*                  # Optional many spaces
   "
   (?=                  # Lookahead, check for a valid quoted field, determines if a '"' will be consumed
      (?:[^"]+|"")*
      "
      \s*
      (?:,|$)
   )
)?                   # End capt grp 1. 0 or 1 quote

(                    # Capture group 2, the body of text
   (?<=")                 # If there is a '"' behind us, we have consumed a '"' in capture grp 1, so this is valid
   (?:[^"]+|"")*
   (?="\s*(?:,|$) )
 |                      # OR,
   [^,]*                  # Just get up to the next ',' This could be incomplete quoted fields
)                    # End capt grp 2

Расширение

Если на самом деле вы можете использовать это, его можно ускорить для потребленияцитируемое поле с обратными ссылками
вместо того, чтобы совпадать с цитируемым полем дважды.Обратные ссылки обычно преобразуются в одну строку
API сравнения, например strncmp() на языке Си, что делает его намного быстрее.
В качестве дополнительного примечания, пробелы перед / после тела поля не заключенных в кавычки полей можно обрезать
внутри регулярного выражения с небольшим дополнительным обозначением.
Удачи!

Сжатый

(?:^|,)(?:\s*"(?=((?:[^"]+|"")*)"\s*(?:,|$)))?((?<=")\1|[^,]*)

Расширенный

(?: ^|, )
(?: \s* " (?=  ( (?:[^"]+|"")* )  " \s*  (?: ,|$ )  ))?
( (?<=") \1 | [^,]* )

Расширенный с комментариями

(?: ^ | , )          # Consume comma (or BOL is fine)

(?:                  # Start grouping
   \s*                  # Spaces, then double quote '"' (consumed if valid quoted field)
   "                    #
   (?=                  # Lookahead, nothing consumed (check for valid quoted field)
      (                     # Capture grp 1
         (?:[^"]+|"")*          # Body of quoted field  (stored for later consumption)
      )                     # End capt grp 1
      "                     # Double quote '"'
      \s*                   # Optional spaces
      (?: , | $ )           # Comma or EOL
   )                    # End lookahead
)?                   # End grouping, optionaly matches and consumes '\s*"'

(                    # Capture group 2, consume FIELD BODY
   (?<=")                 # Lookbehind, if there is a '"' behind us the field is quoted
   \1                     # Consume capt grp 1
 |                      # OR,
   [^,]*                  # Invalid-quoted or Non-quoted field, get up to the next ','
)                    # End capt grp 2
0 голосов
/ 10 марта 2012

С вдохновением от ruakh я обновил регулярное выражение, чтобы оно возвращало только одну группу захвата за матч (и обрабатывал кавычки в поле и пробелах).

(?:^|[\s]*?,[\s]*)(\"(?:(?:[^\"]+|\"\")*)[^,]*|(?:[^,])*)
...