Как использовать '\' в регулярном выражении утверждения Python lookbehind (?‹=\\) для сопоставления строк в кавычках, подобных С++

Как мне сопоставить r'\a' в Python с помощью проверки назад?
На самом деле мне нужно сопоставить строки C++, такие как "a \" b" и

"str begin \
end"

Я старался:

>>> res = re.compile('(?<=\)a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/re.py", line 190, in compile
    return _compile(pattern, flags)
  File "/usr/lib/python2.7/re.py", line 244, in _compile
    raise error, v # invalid expression

>>> res = re.compile('(?<=\\)a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/re.py", line 190, in compile
    return _compile(pattern, flags)
  File "/usr/lib/python2.7/re.py", line 244, in _compile
    raise error, v # invalid expression
sre_constants.error: unbalanced parenthesis

>>> res = re.compile('(?<=\\\)a')
>>> ms = res.match(r'\a')
>>> ms is None
True

Реальный пример:
когда я анализирую "my s\"tr"; 5; как ms = res.match(r'"my s\"tr"; 5;'), ожидаемый результат: "my s\"tr"

Ответ
Наконец, stribizhev предоставил решение. Я думал, что мое первоначальное регулярное выражение менее затратно в вычислительном отношении, и единственная проблема заключалась в том, что оно должно быть объявлено с использованием необработанной строки:

>>> res = re.compile(r'"([^\n"]|(?<=\\)["\n])*"', re.UNICODE)
>>> ms = res.match(r'"my s\"tr"; 5;')
>>> print ms.group()
"my s\"tr"

person luart    schedule 29.04.2015    source источник
comment
Почему ты оглядываешься назад a? Каков фактический образец, который вы пытаетесь сопоставить?   -  person thefourtheye    schedule 29.04.2015
comment
попробуйте с \\\\ вместо \\\   -  person Morb    schedule 29.04.2015
comment
Для четвертого глаза: фактический шаблон: res = re.compile('([^\n]|(?‹=\)[\n])*') для сопоставления таких строк, как: ms = res.match('my s\ tr; 5;') Для Morb: '\\\\' анализируется, но не работает должным образом.   -  person luart    schedule 29.04.2015
comment
Извини, Морб, ты прав! re.compile('([^\n]|(?‹=\\\)[\n])*') также решает проблему, я просто не предоставил строку как необработанную при первом тестировании   -  person luart    schedule 29.04.2015
comment
@luart: Пожалуйста, рассмотрите возможность изменения названия на что-то вроде Match C++-like quoted strings regex. Я пытался найти один и потерпел неудачу.   -  person Wiktor Stribiżew    schedule 29.04.2015
comment
Сделано, спасибо стрибижеву.   -  person luart    schedule 29.04.2015


Ответы (4)


EDIT: окончательное регулярное выражение является адаптацией регулярного выражения, представленного на странице Выровнено по словам

Я думаю, вы ищете это регулярное выражение:

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

См. демонстрацию regex101.

Пример кода Python (проверено на TutorialsPoint):

import re
p = re.compile(ur'(?s)"(?:[^"\\]|\\.)*"')
ms = p.match('"my s\\"tr"; 5;')
print ms.group(0)
person Wiktor Stribiżew    schedule 29.04.2015
comment
Это вообще не ответ на этот вопрос! пожалуйста, получите a от \a, если можете - person kasravnd; 29.04.2015
comment
@Kasra: На самом деле мне нужно сопоставить строки C++, такие как a \ b и *str begin \ end. - person Wiktor Stribiżew; 29.04.2015
comment
@luart: не используйте match, так как этот метод ищет совпадение только в начале строки. Используйте findall. Или хотя бы search. - person Wiktor Stribiżew; 29.04.2015
comment
Стрибижеву: я всегда позиционируюсь в начале строки, и мне нужно извлечь только эту первую строку из всего текста, поэтому совпадение мне подходит. - person luart; 29.04.2015
comment
@luart: Хорошо, теперь стало яснее. Просто обратите внимание, что жадное сопоставление, предложенное Касрой ("(.*)"), может привести к чрезмерному совпадению, если в двойных кавычках содержится более 1 значения. Я использую look-behinds, чтобы убедиться, что этого не происходит. - person Wiktor Stribiżew; 29.04.2015
comment
to stribizhev: Спасибо большое, работает! re.compile(ur'(?s)((?‹!\).+?(?‹!\))') - person luart; 29.04.2015
comment
@luart: Отлично! Я добавил пример кода Python, чтобы вы могли проверить мой подход. Я также считаю, что в приведенном вами примере есть опечатка (символ `\` должен быть удвоен). - person Wiktor Stribiżew; 29.04.2015
comment
Обратите внимание, что .+? всегда плохая идея для сопоставления строкового литерала, и вы просто отвергаете допустимый случай "\\" - person nhahtdh; 29.04.2015
comment
Спасибо stribizhev, я тоже исправил опечатку в примере - person luart; 29.04.2015
comment
@nhahtdh: это можно исправить, добавив в качестве альтернативы этот маргинальный регистр: (?s)((?<!\\)".+?(?<!\\)"|"\\\\"): regex101.com/r/ yN0cN9/2 - person Wiktor Stribiżew; 29.04.2015
comment
@stribizhev: Это не способ исправить. Это только уход от проблемы. Вам это сойдет с рук только потому, что это редко встречается в исходном файле. - person nhahtdh; 29.04.2015
comment
@nhahtdh: я понимаю твою точку зрения. Я изменил регулярное выражение, чтобы убедиться, что двойная обратная косая черта также захвачена с просмотром внутри просмотра. Если вы знаете больше подобных случаев, поделитесь ими или опубликуйте свой собственный ответ. Однако текущая версия кажется уже безопасной. Думаю, нет необходимости проверять обратную косую черту перед первой двойной кавычкой. - person Wiktor Stribiżew; 29.04.2015
comment
@stribizhev: Проверьте мой ответ. Я не понимаю твоего обозначения сейфа. В этих случаях, по крайней мере, вы должны быть в состоянии сопоставить любой правильный строковый литерал из компилируемого исходного кода. Это проблема, с которой сталкиваются и решают снова и снова на SO, и если в вопросе упоминается возможность escape-последовательности, то жесткий ответ предпочтительнее этого ответа. - person nhahtdh; 29.04.2015
comment
Хорошо, я немного поискал в Интернете и нашел решение, похожее на решение @nhahtdh. Я его немного адаптировал. - person Wiktor Stribiżew; 29.04.2015
comment
@stribizhev: В основном то же самое, да. Мое регулярное выражение не позволяет \n (которое не является частью продолжения строки) появляться в строке, но, оглядываясь назад, оно дает только ложное чувство безопасности, поскольку я уже предполагаю, что код компилируется. - person nhahtdh; 29.04.2015

Предполагая, что исходный код компилируется, это классическое решение для сопоставления с обычным строковым литералом в C и C++ с учетом синтаксиса продолжения строки:

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

Оглядываясь назад, поскольку я уже предполагаю, что исходный код компилируется, нет необходимости предотвращать случайные новые строки, которые не являются частью синтаксиса продолжения строки в [^"\\\n], поэтому использование только [^"\\] также будет работать.

Приведенное выше регулярное выражение правильно соответствует всем следующим тестам:

"a \" b"

"a \
 b"

"\\"

"\\\
kjsh\a\b\tdfkj\"\\\\\\"

"kjsdhfksd f\\\\"

"kjsdhfksd f\\\""

Демонстрация regex101

старый ответ Стрибижева (?s)((?<!\\)".+?(?<!(?<!\\)\\)") не соответствует допустимому случаю "kjsdhfksd f\\\"", и добавление дополнительной проверки устраняет проблему только для ограниченного числа \.

Возможность множества последовательных \ подряд в строковом литерале является причиной того, что такое регулярное выражение не работает, и почему мы не должны использовать операцию разделения для токенизации CSV с полями в кавычках.

person nhahtdh    schedule 29.04.2015
comment
Обратите внимание, что это решение не тестировалось с необработанным строковым литералом в C++, и я считаю, что для работы с ним требуется модификация. - person nhahtdh; 29.04.2015
comment
Спасибо, nahtdh. В общем, окончательное решение от стрибижева — это доработанное вами! - person luart; 29.04.2015

Лучший способ, вы можете избежать повторения чередования только с одним символом, если вы «развернете» шаблон следующим образом:

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

Обратите внимание, что вам также не нужно использовать просмотр назад.

Как предложил nhahtdh, если вы хотите убедиться/проверить, что вся строка находится в одной строке, вам нужно только исключить \n из классов символов:

(?s)"[^"\\\n]*(?:\\.[^"\\\n]*)*"
person Casimir et Hippolyte    schedule 29.04.2015

Поскольку \ является escape-символом, вам нужно использовать \\ (экранировать его один раз) и в вашей строке, потому что python будет интерпретировать \a как hex:

>>> '\a'
'\x07'

также вы должны использовать re.search, потому что re.match mпроверяет соответствие только в начале строки :

>>> re.search(r'(?<=\\)a','\\a')
<_sre.SRE_Match object at 0x7fb704dd0370>
>>> re.search(r'(?<=\\)a','\\a').group(0)
'a'

Но для вашего последнего примера вам вообще не нужно оглядываться, вы можете использовать простую группировку:

>>> re.search(r'"(.*)"','"my s\"tr"; 5;').group(0)
'"my s"tr"'
person kasravnd    schedule 29.04.2015
comment
Нет, когда я разбираю: ››› ms = res.match('my s\tr; 5;') я ожидаю получить свой s\tr. - person luart; 29.04.2015
comment
@luart Итак, почему вы думаете, что результатом будет "my s\"tr"? - person kasravnd; 29.04.2015
comment
Для Касты мне нужно (моя задача) получить мой s\tr в результате разбора моего s\tr; 5; - person luart; 29.04.2015
comment
@luart Итак, вам просто нужна группа захвата, проверьте редактирование! - person kasravnd; 29.04.2015
comment
Kasra: my str' не является 'my s\tr' (обратная косая черта пропущена) в вашем окончательном выводе - person luart; 29.04.2015
comment
Регулярное выражение "(.*)" может вызвать проблемы, если ввод содержит несколько строк в кавычках, см. regex101.com/r/rK6xM2/1. - person Wiktor Stribiżew; 29.04.2015
comment
@stribizhev У таких проблем может быть много сценариев, я просто пишу один для этого случая! - person kasravnd; 29.04.2015