Как создать сложное регулярное выражение для поиска и замены в строке?

Добрый день! <br/> <br/> У меня есть задача: в строке необходимо найти предложения, содержащие определенное слово, и заменить только эти предложения на другую фразу. <br/> <br/> Я составил регулярное выражение для этой цели, использующее простую границу предложений по символам . ? !: <br/> <pre><code>(?i)(?&lt;=([\.\?!]|^))[^\.\?!]*СЛОВО[^\.\?!]*(?=([\.\?!]|$))</code></pre> <br/> <br/> Однако теперь возникло дополнительное условие: нужно исключить из замены те предложения, в которых перед СЛОВО находится СЛОВО2. С этой новой задачей у меня возникли сложности. Я попытался использовать следующее регулярное выражение, но оно не сработало: <br/> <pre><code>(?i)(?&lt;=([\.\?!]|^))[^\.\?!]*(?&lt;!СЛОВО2)[^\.\?!]*СЛОВО[^\.\?!]*(?=([\.\?!]|$))</code></pre> <br/> <br/> Возможно ли реализовать эту логику в регулярном выражении? <br/> <br/> И еще: учитывая, что я пишу это на SQL, может быть лучше было бы реализовать решение с использованием циклов, а не регулярных выражений?
  • 27 марта 2025 г. 7:15
Ответы на вопрос 2
Добрый день!

Давайте разберёмся с вашим запросом. Вам нужно создать регулярное выражение, которое ищет предложения, содержащие слово "СЛОВО", но исключает случаи, когда перед ним находится слово "СЛОВО2". 

Ваше текущее решение с использованием `(?<!СЛОВО2)` не сработает, потому что на данный момент не поддерживается в регулярных выражениях, используемых большинством СУБД. Вместо этого мы можем воспользоваться конструкцией, которая позволит добиться желаемого результата, хотя это и усложнит регулярное выражение.

Вот как можно сформулировать регулярное выражение:

```regex
(?i)(?<=([\.\?!]|^))((?:(?!(?:СЛОВО2[^\.\?!]*))[^\.?!]*?)СЛОВО[^\.?!]*)(?=([\.\?!]|$))
```

Объяснение:
- `(?i)` — включает регистронезависимый поиск.
- `(?<=([\.\?!]|^))` — проверяет, что перед предложением стоит точка, вопросительный или восклицательный знак или начало строки.
- `(?:` … `)` — используется для создания группы, которая не будет запомнена.
- `(?!(?:СЛОВО2[^\.\?!]*))` — это негативный просмотр вперёд, который исключает предложения, содержащие "СЛОВО2" до "СЛОВО".
- `[^\.?!]*?` — соответствует любым символам, кроме точки, вопросительного или восклицательного знака, чтобы охватить всё предложение до слова "СЛОВО".
- `СЛОВО` – само искомое слово.
- `[^\.?!]*` — продолжает соответствовать символам в предложении после слова "СЛОВО".
- `(?=([\.\?!]|$))` — проверяет наличие точки, вопросительного, восклицательного знака или конца строки после предложения.

Такое регулярное выражение будет искать предложения с "СЛОВО", но не будет захватывать те предложения, где перед "СЛОВО" стоит "СЛОВО2".

### Альтернатива с использованием SQL

Если вы пишете это на SQL и в вашей базе данных нет поддержки сложных регулярных выражений, возможно, будет проще использовать обычные строки и циклы. Вы можете получить все предложения, разбив текст, проверить каждое предложение на наличие нужных слов и затем собирать результат без предложений, содержащих "СЛОВО2". 

Такой подход будет менее "элегантным", но более понятным и надежным, особенно если у вас много строк и они могут содержать сложные конструкции.

Пример псевдокода для SQL:

```sql
DECLARE @text NVARCHAR(MAX) = 'ваш текст';
DECLARE @result NVARCHAR(MAX) = '';
DECLARE @sentence NVARCHAR(MAX);
DECLARE @delimiter NVARCHAR(10) = '. ';

-- Разбить текст на предложения
WHILE CHARINDEX(@delimiter, @text) > 0
BEGIN
    SET @sentence = LEFT(@text, CHARINDEX(@delimiter, @text) - 1);
    SET @text = STUFF(@text, 1, CHARINDEX(@delimiter, @text), '');

    IF @sentence LIKE '%СЛОВО%' AND @sentence NOT LIKE '%СЛОВО2%'
    BEGIN
        SET @result = @result + 'ваша замена' + @delimiter;
    END
    ELSE
    BEGIN
        SET @result = @result + @sentence + @delimiter;
    END
END

-- Не забудьте добавить последнее предложение, если оно еще осталось
IF LEN(@text) > 0
BEGIN
    SET @result = @result + @text;
END

SELECT @result AS Result;
```

Таким образом, вы можете выбрать наиболее удобный для вас подход.
[^.?!]*(?<!(?<![a-zа-яё0-9])slovo2(?![a-zа-яё0-9])[^.?!]*)(?<![a-zа-яё0-9])slovo(?![a-zа-яё0-9])[^.?!]*
Похожие вопросы