Я маскирую все символы между одинарными кавычками (включительно) внутри строки, используя 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)
Правила, которые, я считаю, должны быть установлены здесь:
^
либо \s
(следовательно, (?<=^|\s)
). \s
(следовательно, (?!\s)
). 2. Сопоставление вещей внутри цитаты и окончательная цитата
(?!^)\G(?<!'(?=\s)).
Правила, которые, я считаю, должны быть установлены здесь:
.
) (?!^)\G
). (?<!'(?=\s))
и это часть « назад к будущему »). Это фактически не будет соответствовать \s
, которому предшествует символ '
и будет отмечать конец символов, заключенных между одинарными кавычками. Другими словами, заключительная цитата будет обозначена как одиночная кавычка, за которой следует \s
. Если вы предпочитаете фотографии …