2016-09-13 12 views
1

Я попытался найти ответ на это некоторое время, но не смог найти его. Было много сообщений, связанных с совпадающим текстом, который не предшествует определенному тексту, но ни один из них не работает для этого случая, когда + сопоставляется, но разрешено только в том случае, если он предшествует одному + (например. ++)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

Но результат я хочу:

Здравствуйте World C C++ C

Благодаря

UPDATE: Я понял, что, вероятно, следует отметить, что у меня будут другие символы, которые я хочу, чтобы избежать. Возможно, я упростил этот вопрос. Например я могу хотеть, чтобы избежать # и, таким образом, результат был бы

Hello World C C++ C#

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

+0

Проблема возникает из сценария типа «++++». Что ты собираешься делать? – sln

+0

Затем просто перечислите специальные случаи внутри группы, не содержащей захвата, перед '(* SKIP) (* F)' - 'preg_replace ('/ (?: [# ^] | \ * {3} | \ + {2}) (* SKIP) (* F) | [[: punct:]] +/',' ', $ text); ' –

+0

' preg_replace (' \ b (\ + (?! \ +) | [^ \ P { P} #]) ',' ', $ text) 'вам нужно добавить специальные символы внутри класса символов. Например. для исключения '!': '[^ \ P {P} #!]' – revo

ответ

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.

+0

Это, очевидно, не соответствует остальным символам пунктуации. Я мог бы изменить [+ #] на [[: punct:]], и это решает эту проблему. Но теперь я вижу, что должен был сказать в вопросе, что мне также нужно будет добавить больше символов пунктуации, которых следует избегать. Как мне это сделать? Например, если я тоже хочу избежать #? –

+0

Мне очень понравился (* SKIP) (* FAIL). Он решает все мои проблемы. НО у меня есть вопрос. В руководстве perl говорится, что матч, ведущий до (* SKIP), не может быть частью какого-либо соответствия шаблону. Он говорит, что двигатель регулярных выражений переходит к этой позиции и повторяет попытку. Поэтому я не понимаю, что делает (* FAIL)? потому что руководство perl говорит, что оно вызывает обратное отслеживание. Что, насколько я понимаю, означает переход к следующему персонажу после 1-го символа, который был сопоставлен. Но зачем нам (* FAIL), если (* SKIP) уже пропускает символы ++? –

+1

Evren, главное, что при использовании вместе эти глаголы вызывают полное отключение текущего матча от объекта соответствия и - что очень важно - продолжайте анализировать строку после этого отброшенного совпадения. Backtracking означает попытку попробовать другое изменение шаблона, если шаблон имеет неизвестные кванторы ширины или чередование. –

2

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

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

Regex:

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

Update

Если + врачевание всегда предшествуют некоторые буквы есть много короче, раствор:

\b\+(?!\+) 
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:]]+ - соответствует одному или более символов пунктуации.

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

+0

Я считаю его самым чистым проще всего расширить подход. –

+0

Это очень приятно, но у него есть проблема. Я хочу заменить найденные символы пробелом. Это просто удаляет их. A + B становится AB –

+0

См. Обновление, его можно использовать с помощью метода '(* SKIP) (* FAIL)'. См. Обновленную версию - http://ideone.com/78HCwZ –

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 ++ 
    \+ 
+0

Вы написали '\ +' в стороне чередования. Таким образом, он будет соответствовать всем символам '+', независимо от способа их присутствия. Ваше первое регулярное выражение было в порядке. – revo

+0

@revo - Я знаю, что он соответствует любому +. Но он будет пассивно соответствовать '+++', если он доступен. К сожалению, правила должны быть решены слева направо. Казалось бы, это просто «++». Затем сравните это с «++++++++++++» или «++++», тогда некоторые правила должны быть рассмотрены. – sln