маскирование переменной длины с помощью preg_replace

Я маскирую все символы между одинарными кавычками (включительно) внутри строки, используя preg_replace_callback() . Но я хотел бы использовать preg_replace() если это возможно, но не смог понять это. Любая помощь будет оценена по достоинству.

Это то, что я использую preg_replace_callback() который производит правильный вывод:

 function maskCallback( $matches ) { return str_repeat( '-', strlen( $matches[0] ) ); } function maskString( $str ) { return preg_replace_callback( "('.*?')", 'maskCallback', $str ); } $str = "TEST 'replace''me' ok 'me too'"; echo $str,"\n"; echo $maskString( $str ),"\n"; 

Выход:

 TEST 'replace''me' ok 'me too' TEST ------------- ok -------- 

Я пробовал использовать:

 preg_replace( "/('.*?')/", '-', $str ); 

но черточки потребляются, например:

 TEST -- ok - 

Все, что я пробовал, тоже не работает. (Я, очевидно, не специалист по регулярному выражению.) Можно ли это сделать? Если да, то как?

Да, вы можете это сделать (при условии, что кавычки сбалансированы) пример:

 $str = "TEST 'replace''me' ok 'me too'"; $pattern = "~[^'](?=[^']*(?:'[^']*'[^']*)*+'[^']*\z)|'~"; $result = preg_replace($pattern, '-', $str); 

Идея такова: вы можете заменить символ, если это цитата, или если за ним следует нечетное число кавычек.

Без кавычек:

 $pattern = "~(?:(?!\A)\G|(?:(?!\G)|\A)'\K)[^']~"; $result = preg_replace($pattern, '-', $str); 

Шаблон будет соответствовать символу только в том случае, если он смежн к совпадению с прецедентом (другими словами, когда он сразу после последнего совпадения) или когда ему предшествует цитата, которая не соприкасается с совпадением с прецедентом.

\G – позиция после последнего совпадения (в начале это начало строки)

шаблон детали:

 ~ # pattern delimiter (?: # non capturing group: describe the two possibilities # before the target character (?!\A)\G # at the position in the string after the last match # the negative lookbehind ensure that this is not the start # of the string | # OR (?: # (to ensure that the quote is a not a closing quote) (?!\G) # not contiguous to a precedent match | # OR \A # at the start of the string ) ' # the opening quote \K # remove all precedent characters from the match result # (only one quote here) ) [^'] # a character that is not a quote ~ 

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

РЕДАКТИРОВАТЬ:

Способ (*SKIP)(*FAIL) :

Вместо того, чтобы тестировать, если одна цитата не является закрывающей цитатой с (?:(?!\G)|\A)' как в шаблоне прецедента, вы можете сломать совпадение совпадений при закрытии кавычек с помощью контрольных глаголов backtracking (*SKIP) и (*FAIL) (Это может быть сокращено до (*F) ).

 $pattern = "~(?:(?!\A)\G|')(?:'(*SKIP)(*F)|\K[^'])~"; $result = preg_replace($pattern, '-', $str); 

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

Шаблон может быть более эффективным, например:

 $pattern = "~(?:\G(?!\A)(?:'(*SKIP)(*F))?|'\K)[^']~"; 

(Вместо (*SKIP) вы можете использовать (*PRUNE) (*SKIP) .)

Короткий ответ: возможно!

Используйте следующий шаблон

 ' # Match a single quote (?= # Positive lookahead, this basically makes sure there is an odd number of single quotes ahead in this line (?:(?:[^'\r\n]*'){2})* # Match anything except single quote or newlines zero or more times followed by a single quote, repeat this twice and repeat this whole process zero or more times (basically a pair of single quotes) (?:[^'\r\n]*'[^'\r\n]*(?:\r?\n|$)) # You guessed, this is to match a single quote until the end of line ) | # or \G(?<!^) # Preceding contiguous match (not beginning of line) [^'] # Match anything that's not a single quote (?= # Same as above (?:(?:[^'\r\n]*'){2})* # Same as above (?:[^'\r\n]*'[^'\r\n]*(?:\r?\n|$)) # Same as above ) | \G(?<!^) # Preceding contiguous match (not beginning of line) ' # Match a single quote 

Обязательно используйте модификатор m .

Онлайн-демонстрация .

Длинный ответ: Это боль 🙂

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

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

1) Сначала нам нужно знать, что мы действительно хотим заменить, мы хотим заменить каждый символ (включая одинарные кавычки), который находится между двумя одинарными кавычками с дефисом.
2) Если мы собираемся использовать preg_replace() это означает, что наш шаблон должен соответствовать одному одиночному символу каждый раз.
3) Итак, первый шаг был бы очевиден: ' .
4) Мы будем использовать \G что означает начало начала строки или смежный символ, который мы сопоставляли ранее. Возьмем этот простой пример ~a|\Gb~ . Это будет соответствовать a или b если оно находится в начале или b если предыдущий матч был a . См. Эту демонстрацию .
5) Мы не хотим иметь ничего общего с началом строки. Поэтому мы будем использовать \G(?<!^) .
6) Теперь нам нужно сопоставить все, что не является ни одной цитатой ~'|\G(?<!^)[^']~ .
7) Теперь начинается настоящая боль, откуда мы знаем, что вышеприведенный шаблон не будет соответствовать c в 'ab'c ? Ну, это будет, нам нужно подсчитать одиночные кавычки …

Напомним:

 a 'bcd' efg 'hij' ^ It will match this first ^^^ Then it will match these individually with \G(?<!^)[^'] ^ It will match since we're matching single quotes without checking anything ^^^^^ And it will continue to match ... 

То, что мы хотим, может быть сделано в этих трех правилах:

 a 'bcd' efg 'hij' 1 ^ Match a single quote only if there is an odd number of single quotes ahead 2 ^^^ Match individually those characters only if there is an odd number of single quotes ahead 3 ^ Match a single quote only if there was a match before this character 

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

 (?: # non-capturing group (?: # non-capturing group [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote ){2} # Repeat 2 times (We'll be matching 2 single quotes) )* # Repeat all this zero or more times. So we match 0, 2, 4, 6 ... single quotes 

9) Нечетное число было бы легко сейчас, нам просто нужно добавить:

 (?: [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times (?:\r?\n|$) # End of line ) 

10) Слияние выше в одном взгляде:

 (?= (?: # non-capturing group (?: # non-capturing group [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote ){2} # Repeat 2 times (We'll be matching 2 single quotes) )* # Repeat all this zero or more times. So we match 0, 2, 4, 6 ... single quotes (?: [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times (?:\r?\n|$) # End of line ) ) 

11) Теперь нам нужно объединить все три правила, которые мы определили ранее:

 ~ # A modifier #################################### Rule 1 #################################### ' # A single quote (?= # Lookahead to make sure there is an odd number of single quotes ahead (?: # non-capturing group (?: # non-capturing group [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote ){2} # Repeat 2 times (We'll be matching 2 single quotes) )* # Repeat all this zero or more times. So we match 0, 2, 4, 6 ... single quotes (?: [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times (?:\r?\n|$) # End of line ) ) | # Or #################################### Rule 2 #################################### \G(?<!^) # Preceding contiguous match (not beginning of line) [^'] # Match anything that's not a single quote (?= # Lookahead to make sure there is an odd number of single quotes ahead (?: # non-capturing group (?: # non-capturing group [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote ){2} # Repeat 2 times (We'll be matching 2 single quotes) )* # Repeat all this zero or more times. So we match 0, 2, 4, 6 ... single quotes (?: [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times ' # Match a single quote [^'\r\n]* # Match anything that's not a single quote or newline, zero or more times (?:\r?\n|$) # End of line ) ) | # Or #################################### Rule 3 #################################### \G(?<!^) # Preceding contiguous match (not beginning of line) ' # Match a single quote ~x 

Онлайн-повторное издание . Демо-версия

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

 (?<=^|\s)'(?!\s)|(?!^)(?<!'(?=\s))\G. 

regex101 demo

Хорошо, он разбит на две части:

1. Соответствие начальной одинарной кавычки

 (?<=^|\s)'(?!\s) 

Правила, которые, я считаю, должны быть установлены здесь:

  1. До начала цитаты должно быть либо ^ либо \s (следовательно, (?<=^|\s) ).
  2. После стартовой цитаты нет \s (следовательно, (?!\s) ).

2. Сопоставление вещей внутри цитаты и окончательная цитата

 (?!^)\G(?<!'(?=\s)). 

Правила, которые, я считаю, должны быть установлены здесь:

  1. Символом может быть любой символ (следовательно . )
  2. Матч длится 1 символ и после немедленного предыдущего матча (отсюда (?!^)\G ).
  3. Не должно быть ни одной кавычки, за которой следует пробел перед ней (следовательно,
    (?<!'(?=\s)) и это часть « назад к будущему »). Это фактически не будет соответствовать \s , которому предшествует символ ' и будет отмечать конец символов, заключенных между одинарными кавычками. Другими словами, заключительная цитата будет обозначена как одиночная кавычка, за которой следует \s .

Если вы предпочитаете фотографии …

IMG