Мне интересно, как удалить любой заголовок предыдущего сообщения в электронном письме. Есть пример сообщения:
Something above
-----Message d'origine-----
De : Myself <[email protected]>
Envoyé : vendredi 8 mars 2019 14:30
À : Someone <[email protected]>
Cc : AnotherGuy <[email protected]>
Objet : My bad I forgot how to do it
Hi,
blabla
И мне нужно удалить все между -----Message d'origine-----
и carriage return
и empty new line
до "Hi,"
.
Я пробовал следующее регулярное выражение:
-----Message d'origine-----[\s\S]*?[\r\n]
Но только -----Message d'origine-----
совпало без других строк ниже. Однако, если я использую вместо этого "Hi,"
, он соответствует всем строкам с ним:
-----Message d'origine-----[\s\S]*?Hi
Кто-нибудь может мне помочь, в чем проблема и как использовать вместо carriage return
и empty new line
?
Спасибо :)
Легкий. Сопоставьте две новые строки вместо одной: (?:\r?\n){2}
. Это дубликат.
@revo Дубликат какие?
Вам нужно сопоставить до первого появления разрыва строки двойной:
r"-----Message d'origine-----[\s\S]*?(?:\r?\n){2}"
^^^^^^^^^^^^
См. демонстрация регулярных выражений. Шаблон (?:\r?\n){2}
соответствует двум повторениям окончаний строк CRLF или LF.
Пример кода Python:
import re
s = "YOUR STRING HERE"
s = re.sub(r"-----Message d'origine-----.*?(?:\r?\n){2}", '', s, flags=re.S)
Обратите внимание, что [\s\S]
равно .
в регулярном выражении, когда используется флаг re.S
(=re.DOTALL
).
Если вас беспокоит производительность, на которую влияет нежадный шаблон .*?
, разверните его как
s = re.sub(r"-----Message d'origine-----.*(?:\r?\n(?!\r?\n).*)*\s*", "", s)
См. эта демонстрация регулярных выражений. Не используйте re.S
/ re.DOTALL
с этим шаблоном!
[\s\S]*?(?:\r?\n){2}
теперь .*(?:\r?\n(?!\r?\n).*)*
:
.*
- остаток строки(?:\r?\n(?!\r?\n).*)*
- 0 и более повторений
\r?\n(?!\r?\n)
- за разрывом строки не следует другой разрыв строки.*
- остаток строкиПочему нежадный шаблон будет менее эффективным, чем ваш вариант? Я не знаю внутренностей механизма регулярных выражений Python, но по наивности они должны выполнять примерно ту же работу.
@KonradRudolph Проверьте количество шагов для обоих регулярных выражений в regex101: 582/72. Разница связана с возвратом с помощью шаблона .*?
, поскольку шаблон расширяется каждый раз, когда последующие шаблоны не совпадают. .*
захватывает все символы до разрыва строки, и здесь меньше возвратов, поскольку проверка выполняется после того, как найден конец строки, нет необходимости расширять какой-либо шаблон char за char.
Хм, это кажется мне ошибкой производительности в реализации: конечно, нежадное совпадение может быть реализовано таким же образом?
@KonradRudolph Это ожидаемое поведение, а не ошибка. См. Могу ли я еще больше улучшить производительность этого регулярного выражения? для конкретного примера соответствия .*
и .*?
. При работе с длинными текстами не стоит полагаться на .*?
или .*
, если вы не уверены в позициях соответствия. Тогда следует использовать метод Развернуть петлю или вообще отказаться от регулярного выражения в пользу какого-либо метода синтаксического анализа.
Точно, я запутался. Я неправильно понял вас, поскольку вы сказали, что .*?
сам откатывается, а не следующий (?:\r?\n){2}
(который сопоставляется на каждом шаге и обычно терпит неудачу). Я также пропустил, что ваш второй пример не работает в однострочном режиме, поэтому .
просто соответствует меньше и это, поэтому он более эффективен, чем нежадный случай.
Отлично, @WiktorStribiżew, спасибо за все советы, которые вы дали :) Я был довольно близок, но я не знал здесь концепции флагов и того, как здесь улучшить производительность.
Вы показали испробованное регулярное выражение, но не соответствующий код Python. Это важно: включили ли вы многострочное сопоставление регулярных выражений?