PHP PCRE 匹配标点符号但不匹配 ++

PHP PCRE match punctuation but not ++

我试了一段时间寻找这个问题的答案,但没找到。有许多与匹配文本相关的帖子,这些文本前面没有特定文本,但 none 似乎适用于 + 匹配的这种情况,但只有在前面有单个 + 时才允许(例如 ++)

我正在尝试从文本中删除标点符号,但保留两个连续的 ++ 符号,但让单个 + 符号消失

$text="Hello World! C+ C++ C#";
print_r(preg_replace('/(?!\+\+)[[:punct:]]/', ' ', $text));

结果(我不确定为什么后者 + 被删除?有人可以解释一下吗?):

Hello World C C+ C

如果我尝试:

$text="Hello World! C+ C++ C#";
print_r(preg_replace('/(?!\+)[[:punct:]]/', ' ', $text));

结果是:

Hello World C+ C++ C

但是我想要的结果是:

Hello World C C++ C

谢谢

更新:我意识到我可能应该提到我会有其他我想避免的角色。我可能把问题简单化了。例如,我可能想避免 # 也因此结果将是

Hello World C C++ C#

解决方案应该易于扩展。对于此信息缺失给您带来的不便,我们深表歉意。

您在这里有几个选择,一个是:

(?<!\+)[+#](?!\+)
# with lookarounds making sure no + is after/behind

a demo on regex101.com


PHP:

<?php

$regex = '~(?<!\+)[+#](?!\+)~';

$string = 'Hello World! C+ C++ C#';
$string = preg_replace($regex, '', $string);

echo $string;
?>


另一种方法是使用 (*SKIP)(*FAIL) 机制(在本例中速度更快):

\+{2}(*SKIP)(*FAIL)|[+#]
# let two consecutive ++ always fail

regex101.com as well 上查看此演示。

最后但并非最不重要的: 如果你想添加也应该避免的 characters/expressions ,你可以将它们放在一个非捕获组中并让这个失败:

(?:\#|\+{2})(*SKIP)(*FAIL)|
[[:punct:]]

又一个demo on the wonderful regex101.com site.

你的第一个正则表达式 (?!\+\+)[[:punct:]] 不起作用,因为它在否定中寻找两个连续的 + 符号 - 在每个位置 - 然后断言下一个直接字符是标点符号。当它看到 C++ ,光标位于第一个 + 符号旁边时,此匹配成功,因为在第二个 + 之后没有 +。所以先匹配+.

Hello World! C+ C+|+ C#
                  ^ Cursor here - (?!\+\+)[[:punct:]] is matched

正则表达式:

[[:punct:]]++((?<=\+)(?<=[^+]\+))

除了有条件的积极回顾断言之外,所有格匹配也可以完成这项工作。

Live demo

解释:

[[:punct:]]++   // Match punctuation marks possessively - won't allow backtrack
((?<=\+)        // Start of a conditional statement, check if last match is a `+`
    (?<=[^+]\+) // If yes, it should not be preceded by another `+`
)               // End of conditional

PHP:

preg_replace('@[[:punct:]]++((?<=\+)(?<=[^+]\+))@', ' ', $text)

更新

如果 + 字母前面总是有一些字母,则有一个更短的解决方案:

\b\+(?!\+)

第一个代码片段是这样工作的:找到一个标点符号,如果它不是 ++ 序列的起点,则匹配并删除它。因此,C++ 中的第二个 + 匹配,并被删除。

您可以使用 (*SKIP)(*FAIL) 动词匹配并 从匹配中丢弃 您想要保留的内容,只匹配您想要删除的内容:

preg_replace('/\+{2}(*SKIP)(*F)|[[:punct:]]+/', ' ', $text);

添加更多字符 - 以防万一:

preg_replace('/(?:[#^]|\*{3}|\+{2})(*SKIP)(*F)|[[:punct:]]+/', ' ', $text);
               ^^^                ^

PHP demo

详情:

  • \+{2}(*SKIP)(*FAIL) - 匹配 2 个 + 符号,然后从匹配
  • 中丢弃它们
  • | - 或
  • [[:punct:]]+ - 匹配一个或多个标点符号。

在替换模式中,我们只是替换为space。

我认为这里有三种情况可以匹配加号。
必须匹配双加号才能越过它。

注意 - 这遵循从左到右的加号规则。除了这些没有规则。

查找:

[^\P{P}+]|(\+\+)\+|\+

替换:' '

已解释

    [^\P{P}+]           # Punctuation but not plus
 |  
    ( \+\+ )            # (1), Plus with leading ++
    \+
 |  
    \+                  # Any old plus sign

可以减少到

   [^\P{P}+]           # Punctuation but not plus
|  
   ( \+\+ )?           # (1), Plus with optional leading ++
   \+