如何转义未聚集成特殊形状的特殊字符

How to escape special characters that that are not gathered in a special shape

我有这个字符串:

$var = "Foo (.* ) bar blla (.* ) b*l?a$ bla bla ";

我想转义 * 和 ?以及所有没有聚集在这个形状中的特殊字符

"(.*)"

我想用 preg_quote($var, '\') 但它转义了所有特殊字符,我只需要转义单个特殊字符。 我想要这个结果:

$var = "Foo (.* ) bar bla(.*) b\*l\?a$ bla bla ";

我想在 preg_match 中使用最终的 $var(结果)匹配其他字符串中的所有 (.*),以及在我的例子中的特殊字符 theses :

., \, ^, $, |, ?, *, +, (, ), [, ], {, }, and /

应该被解析为普通文本,因此它们应该被转义。而 (.*) 不应该被转义。 只有上面的特殊字符应该被转义,因为我将不得不在 preg_match 中使用 $var。其他特殊字符,不用转义。

preg_match("/" . $var . "/s", $anotherstring, $match);

编辑3

它似乎对你不起作用,所以这是另一种尝试。由于 mickmack 似乎担心性能,他会很高兴它减少到 146 步 ;)

替换

([\w\s]*(?:\([^)]*\)[\w\s]*)*)([*?$&])

\

Here at regex101.

它捕获非特殊字符的可选范围。它继续捕获一个可选的括号组,然后是一个可选的非特殊字符范围。最后一部分可以重复任意次数。最后它捕获了特殊字符。

因此我们必须捕获组 - 一个包含导致特殊字符(如果有)的文本,另一个 带有 特殊字符。

用中间的 \ 替换它们的内容,就可以了。

括号部分也更灵活(快乐米克?;)。它允许在括号内使用更复杂的正则表达式(只是不是嵌套的括号)。

如果处理 \ 的新要求不是必须的,并且否定词 class 没问题 \W 我们失败了达到惊人的 76 步 :) Here at regex101.

--原答案--

这是一种方法 - 替换

(?<!\(|\(.|\(..)([^\w\s])(?![^(]*\))

$1

注意!您必须转义 php 字符串中的 \ - 即 "\\$1".

由于 php 只允许 fixed with look-behinds,它用 [=] 分四步测试 特殊字符 前没有左括号19=]构建。然后它匹配并捕获 特殊字符 (不是 单词字符 ,也不是 space)。最后,它使用否定的前瞻性来确保它后面没有跟一个右括号。不过,检查 之前的括号可能是多余的。

单独替换匹配和捕获的字符 - </code> - 前面加上想要的转义字符 <code>\ 就可以了。

See it here at regex101.

编辑

如果 特殊字符 仅限于您的示例中的字符,这是另一种方法 - 使用

(?<!\(\.)([*?$&])(?!\))

作为搜索字符串并替换为 $1

匹配您的特殊字符,只要它们前面没有(.,后面也没有)

Here at regex101.

(这两种方法都不防水,因为它们无法逃离 (.& )中的&。)

编辑2

已更新,因为 OP 将有问题的转义字符从 / 更改为 \。 并删除了捕获组中的 space,因为 OP 不需要它。

使用preg_replace_callback,可以看正则表达式https://regex101.com/r/52qQwv/1

$s = 'Foo (.*) bar blla (.*) b*l?a$&& bla bla';
$regexp = '/([\.\*\?\&$])[\w\s\&]/iu';
$f = function ($matches) {
    return '/' . $matches[1];
};
$a = preg_replace_callback($regexp, $f, $s);
var_dump($a);

字符串(39) "Foo (.) bar blla (.) b/*/?/$/&bla bla"

以下是一些优于 ClasG 答案的模式:

输入:Foo (.* ) bar blla (.* ) b*l?a$ && bla bla

模式:/\([^)]*\)(*SKIP)(*FAIL)|([^a-z\d ])/i替换为:\

输出:Foo (.* ) bar blla (.* ) b\*l\?a$ \&\& bla bla

Pattern Demo(只需 122 步)

基本上它只是省略 "protected" 括号部分并匹配任何非字母和非 space 字符。


如果你想具体列出符号,你可以将OP中的否定字符class更改为字符class,如下所示:(仍然是122步)

/\([^)]*\)(*SKIP)(*FAIL)|([-\/~`!@#$%^&*()_+={}[\]|;:'"<>,.?\])/

或者您可以只使用示例中的符号,这是完整的模式(仍然是 122 个步骤):

/\([^)]*\)(*SKIP)(*FAIL)|([*?$&])/

All of ClasG's patterns are slower than my 3 patterns above:

ClasG's written pattern: (?<!\(|\(.|\(..)([^\w\s])(?![^(]*\)) fails and takes 418 steps - demo

ClasG's linked demo pattern: (?<!\(|\(.|\(..)([^\w\s])(?![^(]*\)) is correct but takes 367 steps - demo

ClasG's third pattern: (?<!\(\.)([*?$&])(?!\)) is correct but has a strict requirement for the parenthetical portion. It is the best pattern in that answer taking 186 steps - demo.