регулярное выражение — PHP PCRE соответствует пунктуации, но не ++

Я некоторое время пытался найти ответ на этот вопрос, но не смог его найти. Было много постов, связанных с соответствующим текстом, которому не предшествует определенный текст, но, похоже, ни один из них не работает в этом случае, где + соответствует, но это разрешено, только если ему предшествует один + (например, ++)

Я пытаюсь удалить знаки препинания из текста, но оставляю два последовательных знака ++, но одиночные + исчезают

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

решение должно быть легко расширяемым. Я сожалею о неудобствах, вызванных этой недостающей информацией.

1

Решение

У вас есть несколько вариантов здесь, один из которых:

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

Увидеть демо на 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.

Последний, но тем не менее важный:
Если вы хотите добавить символы / выражения, которых также следует избегать, вы можете поместить их в группу без захвата и позволить этому произойти сбой:

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

Еще один демо на замечательном сайте regex101.com.

3

Другие решения

Ваше первое регулярное выражение (?!\+\+)[[:punct:]] не работает, потому что он ищет два подряд + знаки отрицания — в каждой позиции — затем утверждают, что следующим непосредственным символом является знак пунктуации. Когда он видит C++ курсор находится рядом с первым + знак, этот матч успешно, так как нет + после второго +, Итак, сначала + совпадает.

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

Regex:

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

Притяжательное совпадение в дополнение к условному положительному взгляду за утверждением сделает работу.

Живая демо

Объяснение:

[[: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

Первый фрагмент кода работает следующим образом: символ пунктуации найден, и если он не является отправной точкой для ++ последовательность, это сопоставлено и удалено. Итак, второе + в C++ совпадает и удаляется.

Вы можете соответствовать и отказаться от матча с помощью (*SKIP)(*FAIL) глаголы что вы хотите сохранить и просто сопоставить то, что вы хотите удалить:

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

Добавление большего количества символов — на всякий случай:

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

Увидеть PHP демо

подробности:

  • \+{2}(*SKIP)(*FAIL) — Матчи 2 + символы, а затем сбрасывает их из матча
  • | — или же
  • [[:punct:]]+ — соответствует одному или нескольким знакам препинания.

В шаблоне замены мы просто заменяем пробелом.

2

Я думаю, что есть три случая, чтобы соответствовать плюсу.
Двойной плюс должен быть подобран, чтобы пройти мимо него.

Примечание. Это следует правилам слева направо о знаках плюса. Без правил, кроме этих.

Найти:

[^\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 ++
\+
0
По вопросам рекламы [email protected]