Preg Patterns, чтобы игнорировать экранированные символы

Я хочу создать RegEx, который находит строки, начинающиеся и заканчивающиеся одинарными или двойными кавычками.

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

String: "Hello World"
RegEx: /[\"\'][^\"\']+[\"\']/

Однако проблема возникает, когда кавычки появляются в самой строке, например:

String: "Hello" World"

Мы знаем, что приведенное выше выражение не будет работать.

Что я хочу сделать, так это иметь escape внутри самой строки, поскольку в любом случае это будет необходимо:

String: "Hello\" World"

Теперь я мог придумать длинное и сложное выражение с различными шаблонами в группе, один из них:

RegEx: /[\"\'][^\"\']+(\\\"|\\\')+[^\"\']+[\"\']/

Однако мне это кажется чрезмерным, и я думаю, что может быть более короткое и элегантное решение.

Предполагаемый синтаксис:

run arg1 "arg1" "arg3 with \"" "\"arg4" "arg\"\"5"

Как видите, кавычки на самом деле используются только для того, чтобы строка с пробелами считалась одной строкой. Не беспокойтесь о arg1, я должен уметь сопоставлять аргументы без кавычек.

Я сделаю это проще, аргументы можно заключать только в двойные кавычки. Поэтому я убрал одинарные кавычки из требований этого вопроса.

Я изменил пример Руи Джаримбы:

/(?<=")(\\")*([^"]+((\\(\"))*[^"])+)((\\"")|")/

Теперь это довольно хорошо объясняет большинство случаев, однако есть один последний случай, который может победить это:

run -a "arg3 \" p2" "\"sa\"mple\"\\"

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


person Flosculus    schedule 21.11.2012    source источник
comment
На каком основании скрипт должен знать, какие кавычки менять, а какие считать начальными/конечными параметрами?   -  person Peon    schedule 21.11.2012
comment
В зависимости от внешних кавычек, да, большая строка может содержать более одной вложенной строки в кавычках, поэтому регулярное выражение должно быть в состоянии найти их все. Если вложенная строка заключена в одинарные кавычки, то любые внутренние двойные кавычки не нужно экранировать, и наоборот.   -  person Flosculus    schedule 21.11.2012
comment
Итак, вы ищете весь текст между кавычками first и last?   -  person Peon    schedule 21.11.2012
comment
Думайте об этом так же, как о попытке найти все строки в SQL-запросе. Любые двойные двойные кавычки (которые сообщают обработчику SQL, что это экранированная двойная кавычка) считаются частью строки. Здесь нужно применить тот же принцип, но с обратной косой чертой. Однако это решение не будет применяться к операторам SQL, я фактически пытаюсь создать анализатор аргументов командной строки.   -  person Flosculus    schedule 21.11.2012
comment
@Flosculus На самом деле правильное решение намного сложнее. Взгляните на этот вопрос .   -  person Carlos    schedule 21.11.2012
comment
@jackflash вопрос, который вы связали, намного сложнее, чем у нас здесь. другой пытается найти строки внутри кавычек, допуская экранированные кавычки. этот просто пытается найти строки в кавычках.   -  person Martin Ender    schedule 21.11.2012
comment
@m.buettner m.buettner Если вы проверите принятый ответ, вы увидите регулярное выражение для проверки строк в кавычках.   -  person Carlos    schedule 21.11.2012
comment
Привет, Flosculus, попробуй вот это: ['"]([^'"]+((\\(\"|'))*[^'"])+)['"]. Смотрите мой ответ ниже   -  person Rui Jarimba    schedule 21.11.2012


Ответы (2)


Попробуйте это регулярное выражение:

['"]([^'"]+((\\(\"|'))*[^'"])+)['"]

Учитывая следующую строку:

"Hello" World 'match 2' "wqwqwqwq wwqwqqwqw" no match here oopop "Hello \" World"

Это будет соответствовать

"Hello"
'match 2'
"wqwqwqwq wwqwqqwqw"
"Hello \" World"
person Rui Jarimba    schedule 21.11.2012
comment
Исправлено регулярное выражение. PS: я тестирую с использованием регулярных выражений .NET, но это должно работать с PHP. - person Rui Jarimba; 21.11.2012
comment
ваше экранирование непоследовательно (вы только один раз экранируете двойную кавычку). в противном случае это должно работать (кроме того, что вы не различаете две возможности разделителя) - person Martin Ender; 21.11.2012
comment
Это тоже исправлено. Теперь он обрабатывает более 1 экранированной кавычки - person Rui Jarimba; 21.11.2012
comment
да, я уже заметил (поэтому я отредактировал свой комментарий). теперь осталось только экранирование и тот факт, что ' и " взаимозаменяемы. - person Martin Ender; 21.11.2012
comment
Я заметил еще одну проблему с обоими нашими ответами. Экранированные кавычки перед строкой в ​​кавычках заставят эту строку начаться раньше. Я уже исправил свой, поэтому взгляните на мой последний пример, чтобы понять, что я имею в виду. - person Martin Ender; 21.11.2012
comment
Я должен отметить, что синтаксис вложенных строк важен для функциональности. Это не то, о чем вам нужно беспокоиться, поскольку, если синтаксис неверен, то не имеет значения, работает ли регулярное выражение. - person Flosculus; 21.11.2012
comment
@Flosculus, тогда, я думаю, мой ответ подойдет тебе? - person Martin Ender; 21.11.2012
comment
Спасибо @m.buettner за указание на эту проблему. Нельзя быть слишком осторожным с регулярными выражениями :) - person Rui Jarimba; 21.11.2012
comment
@m.buettner Частично да, однако пример начинается со строк в массиве. В моем случае будет несколько вложенных строк, поэтому разделение строк является важной частью этого. - person Flosculus; 21.11.2012
comment
@Flosculus, что вы подразумеваете под вложенным? что-то вроде последнего примера в вашем вопросе? Я использовал только массив в своей демонстрации, чтобы показать вам несколько возможных входных строк. как видите, некоторые из них сами содержат несколько строк. и они тоже нормально работают. просто назначьте свой ввод $str и используйте код только из foreach. этот массив и цикл были предназначены только для демонстрационных целей. - person Martin Ender; 21.11.2012
comment
@m.buettner Вложен как концептуальная строка внутри строки. Например, при рендеринге вызовов функций javascript в PHP (не то чтобы это хорошая идея), например echo('myfunction(\''.$value.'\');');. Это то, что я имею в виду. - person Flosculus; 21.11.2012
comment
@Flosculus, пожалуйста, просто попробуйте мой ответ (и посмотрите на отдельные строки внутри массива в демонстрациях). это именно то, о чем заботится мое регулярное выражение. - person Martin Ender; 21.11.2012
comment
Это решение настолько близко, насколько мне нужно, чтобы делать то, что мне нужно. Более тонкие штрихи я могу изменить непосредственно с помощью PHP. Спасибо за вашу помощь всем. И +1 m.buettner, ваши примеры действительно работают, я включу некоторые концепции. - person Flosculus; 22.11.2012

Во-первых, используйте ' строк для написания регулярных выражений. Это спасает вас от многих побегов.

Тогда я вижу две возможности. Проблема с вашей попыткой заключается в том, что она позволяет использовать только последовательные экранированные кавычки в одном месте строки. Кроме того, это позволяет использовать разные кавычки в начале и в конце. Вы можете использовать обратную ссылку, чтобы обойти это. Так что это было бы а) немного элегантнее и б) правильно:

$pattern = '/(["\'])(\\"|\\\'|[^"\'])+\1/';

Обратите внимание, что порядок чередования важен!

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

$pattern = '/(["\'])(?:(?!\1).|(?<=\\\\)\1)+\1/';

Обратите внимание, что четыре последовательных символа обратной косой черты всегда необходимы для соответствия одному буквальному символу обратной косой черты. Это потому, что в фактической строке $pattern они заканчиваются как \\, а затем механизм регулярных выражений «использует» первый, чтобы избежать второго.

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

Рабочая демонстрация

Это, кстати, эквивалентно:

$pattern = '/(["\'])(?:\\\\\1|(?!\1).)+\1/';

Но здесь вы должны снова написать чередование в этом порядке.

Рабочая демонстрация

Последнее замечание. Вы можете избежать обратной ссылки, предоставив две возможные строки отдельно (строки в одинарных и двойных кавычках):

$pattern = '/"(?:\\\\"|[^"])+"|\'(?:\\\\\'|[^\'])+\'/';

Но вы сказали, что ищете что-то короткое и элегантное ;) (хотя последнее могло бы быть более эффективным... но вам придется это профилировать).

Обратите внимание, что все мои регулярные выражения не учитывают один случай: экранированные кавычки вне строк в кавычках. т.е. Hello \" World "Hello" World даст вам " World". Вы можете избежать этого, используя другой отрицательный взгляд назад (используя в качестве примера второе регулярное выражение, для которого я предоставил рабочую демонстрацию; оно будет работать одинаково для всех остальных):

$pattern = '/(?<!\\\\)(["\'])(?:\\\\\1|(?!\1).)+\1/';
person Martin Ender    schedule 21.11.2012
comment
Не вижу здесь парня, проголосовавшего против, о котором я говорил в другом вопросе. Если ты намекаешь, что я тот парень, я должен сказать, что это не так. На самом деле, я проголосовал за ваш ответ. - person Carlos; 21.11.2012
comment
@jackflash Нет, я этого не имел в виду. Я только что увидел ваш ответ, и в последнее время меня часто минусовали, и никогда без объяснения причин. Так что я просто хотел выразить свое сочувствие вам и риджраннеру. - person Martin Ender; 21.11.2012
comment
Ох, хорошо! Я знаю одного парня, который систематически минусует меня только из-за спора, который у нас однажды был. - person Carlos; 21.11.2012