PHP PCRE匹配标点但不是++

时间:2016-09-13 19:27:02

标签: php regex pcre negative-lookbehind

我试图寻找一段时间的答案,但找不到它。有许多与匹配文本相关的帖子,其中没有某些文本,但似乎没有一个文章适用于+匹配的情况,但只有在单个+(例如。++)之前才允许这样做

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

$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#

解决方案应该易于扩展。对于这些遗漏信息造成的不便,我感到很遗憾。

4 个答案:

答案 0 :(得分:3)

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

(?<!\+)[+#](?!\+)
# 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上查看此演示文稿。

最后但并非最不重要: 如果你想添加应该避免使用的字符/表达式,你可以将它们放在一个非捕获组中,让它失败:

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

又一个demo on the wonderful regex101.com site

答案 1 :(得分:2)

你的第一个正则表达式(?!\+\+)[[: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\+(?!\+)

答案 2 :(得分:2)

第一个代码段的工作方式如下:找到标点符号,如果它不是++序列的起点,则匹配并删除它。因此,+中的第二个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:]]+ - 匹配一个或多个标点符号。

在替换模式中,我们只需用空格替换。

答案 3 :(得分:0)

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

注意 - 这是关于加号的左右规则。没有规则,但这些。

查找:

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

替换:'$1 '

解释

    [^\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 ++
   \+