Используя регулярные выражения, как эффективно сопоставлять строки между двойными кавычками со встроенными двойными кавычками?
У нас есть текст, в котором мы хотим сопоставить все строки между двойными кавычками; но внутри этих двойных кавычек могут быть указаны двойные кавычки. Пример:
"He said \"Hello\" to me for the first time"
Используя регулярные выражения, как вы подходите это эффективно?
1 ответ
Очень эффективное решение для сопоставления таких входных данных заключается в использовании normal* (special normal*)*
шаблон; это имя процитировано из превосходной книги Джеффри Фридла " Освоение регулярных выражений".
В целом, этот шаблон полезен для сопоставления входных данных, состоящих из обычных записей (нормальная часть), с разделителями между ними (специальная часть).
Обратите внимание, что, как и все регулярные выражения, его следует использовать, когда нет лучшего выбора; в то время как можно использовать этот шаблон для анализа CSV-данных, например, если вы используете Java, лучше использовать OpenCSV.
Также обратите внимание, что хотя квантификаторы в имени шаблона являются звездами (т. Е. Ноль или более), вы можете изменять их в соответствии со своими потребностями.
Строки со встроенными двойными кавычками
Давайте снова возьмем приведенный выше пример; и, пожалуйста, учтите, что этот образец текста может быть где угодно в вашем вводе:
"He said \"Hello\" to me for the first time"
Как бы вы ни старались, магия "точка плюс жадные / ленивые квантификаторы" не поможет вам решить ее. Вместо этого классифицируйте ввод между кавычками как нормальный и специальный:
- нормальный - это что угодно, кроме обратной косой черты или двойной кавычки:
[^\\"]
; - special - последовательность обратной косой черты, за которой следует двойная кавычка:
\\"
,
Подставляя это в normal* (special normal*)*
шаблон, это дает следующее регулярное выражение:
[^\\"]*(\\"[^\\"]*)*
Добавление двойных кавычек в соответствие с полным текстом дает окончательное регулярное выражение:
"[^\\"]*(\\"[^\\"]*)*"
Вы заметите, что это также будет соответствовать пустым кавычкам.
Слова с разделителями тире
Здесь нам придется использовать вариант с квантификаторами, так как:
- мы не хотим пустых слов,
- мы не хотим, чтобы слова начинались с тире,
- когда появляется тире, она должна иметь хотя бы одну букву перед другой тире, если таковая имеется.
Для простоты мы также предположим, что разрешены только строчные буквы ASCII.
Пример ввода:
the-word-to-match
Давайте снова разложим на нормальные и специальные:
- нормальный: строчная, буква ASCII:
[a-z]
; - специальные: тире:
-
Каноническая форма шаблона:
[a-z]*(-[a-z]*)*
Но как мы уже говорили:
- мы не хотим, чтобы слова начинались с тире: первый
*
должен стать+
; - при обнаружении тире после нее должна быть хотя бы одна буква: вторая
*
должен стать+
,
Мы заканчиваем с:
[a-z]+(-[a-z]+)*
Добавляем привязки к словам для получения окончательного результата:
\b[a-z]+(-[a-z]+)*\b
Другие варианты оператора
Приведенные выше примеры ограничиваются заменой *
с +
, но, конечно, вы можете иметь столько вариантов, сколько пожелаете. Одним ультра классическим примером будет IP-адрес:
- нормальный до трех цифр (
\d{1,3}
), - особая точка:(
\.
), - первый
normal
появляется только один раз, поэтому нет количественного показателя, normal
внутри(special normal*)
также появляется только один раз, поэтому нет квантификатора,- наконец то
(special normal*)
часть появляется ровно три раза, поэтому{3}
,
Что дает выражение (украшенное якорями слова):
\b\d{1,3}(\.\d{1,3}){3}\b
Заключение
Гибкость этого шаблона делает его одним из самых полезных инструментов в вашем наборе регулярных выражений. Хотя существует много проблем, для которых не следует использовать регулярные выражения, если библиотеки существуют, в некоторых ситуациях вы должны использовать регулярные выражения. И это станет одним из ваших лучших друзей, когда вы немного потренировались в этом!
подсказки
- Более чем вероятно, что вам не нужно (или вы хотите) захватить повторяющуюся часть (
(special normal*)
часть); поэтому рекомендуется использовать группу без захвата. Например, используйте"[^\\"]*(?:\\"[^\\"]*)*"
для цитируемых строк. Фактически, если бы вы этого хотели, захват почти никогда не приведет к желаемым результатам в этом случае, потому что повторение группы захвата даст вам только последний захват (все предыдущие повторы будут перезаписаны), если вы не используете этот шаблон в.СЕТЬ. (спасибо @ohaal)