Привязка к концу последнего матча
В процессе работы над этим ответом я наткнулся на аномалию с повторяющимися регулярными выражениями Python.
Скажем, мне дали строку CSV с произвольным количеством элементов в кавычках и без кавычек:
21, 2, '23.5R25 ETADT ',' описание, запятая '
Я хочу заменить все ','
вне цитаты с '\t'
, Так что я хотел бы вывод:
21 \ t2 \ t'23.5R25 ETADT '\ t'описание, с запятой'
Поскольку в строке будет несколько совпадений, естественно, я буду использовать g
модификатор регулярного выражения. Регулярное выражение, которое я буду использовать, будет соответствовать символам вне кавычек или строки в кавычках, за которой следует ','
:
('[^']*'|[^',]*),\s*
И я заменю на:
\1\t
Теперь проблема в том, что регулярное выражение ищет несоответствие, поэтому оно может пропустить символы, пока не совпадет. Поэтому вместо того, чтобы получить желаемый результат, я получаю:
21 \ t2 \ t'23.5R25 ETADT '\ t'описание \ с запятой'
Вы можете увидеть живой пример такого поведения здесь: https://regex101.com/r/sG9hT3/2
В. Есть ли способ закрепить g
Изменено ли регулярное выражение для начала сопоставления с персонажем после предыдущего совпадения?
Для тех, кто знаком с могущественными регулярными выражениями Perl, Perl предоставляет \G
, Что позволяет нам получить конец последней совпавшей позиции. Так что в Perl я могу выполнить то, что я прошу, с помощью регулярного выражения:
\G('[^']*'|[^',]*),\s*
Это приведет к несоответствию в последнем цитируемом элементе. Потому что вместо того, чтобы позволить реализации регулярного выражения найти точку, где регулярное выражение соответствует \G
заставит его начинать сопоставление с первого символа:
"Описание, с запятой"
1 ответ
Вы можете использовать следующее регулярное выражение с re.search
:
,?\s*([^',]*(?:'[^']*'[^',]*)*)
Посмотреть демо-версию регулярного выражения (я изменяю его на ,?[ ]*([^',\n]*(?:'[^'\n]*'[^',\n]*)*)
так как это многострочная демка)
Здесь регулярное выражение совпадает (в регулярном значении слова)...
,?
- 1 или 0 запятая\s*
- 0 или больше пробелов([^',]*(?:'[^']*'[^',]*)*)
- Группа 1, хранящая захваченный текст, который состоит из...[^',]*
- 0 или более символов, кроме,
а также'
(?:'[^']*'[^',]*)*
- 0 или более последовательностей...'[^']*'
- а'string'
подстрока, не содержащая апострофов[^',]*
- 0 или более символов, кроме,
а также'
,
Если вы хотите использовать re.match
и сохранять захваченные тексты внутри групп захвата, это невозможно, поскольку механизм регулярных выражений Python не сохраняет все записи в стеке, как это делает механизм регулярных выражений.NET CaptureCollection
,
Кроме того, регулярное выражение Python не поддерживает \G
оператор, поэтому вы не можете привязать какой-либо подшаблон в конце успешного совпадения здесь.
В качестве альтернативы / обходного пути вы можете использовать следующий код Python для возврата последовательных совпадений, а затем оставшуюся часть строки:
import re
def successive_matches(pattern,text,pos=0):
ptrn = re.compile(pattern)
match = ptrn.match(text,pos)
while match:
yield match.group()
if match.end() == pos:
break
pos = match.end()
match = ptrn.match(text,pos)
if pos < len(text) - 1:
yield text[pos:]
for matched_text in successive_matches(r"('[^']*'|[^',]*),\s*","21, 2, '23.5R25 ETADT', 'description, with a comma'"):
print matched_text
Смотрите демоверсию IDEONE, вывод
21,
2,
'23.5R25 ETADT',
'description, with a comma'