Удаление нежелательных символов, которые нарушают readline ()

#python #regex #email #quoted-printable

#python #регулярное выражение #Адрес электронной почты #в кавычках-для печати

Вопрос:

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

Я столкнулся с одной странной проблемой, когда несколько файлов, которые я просматриваю, выдают странные символы в середине строки, разрушая мой анализ результатов readline (). При чтении в текстовом редакторе рассматриваемая строка выглядит нормально, но readline () считывает символ ‘=’ и два символа ‘n’ прямо посередине IP.

например

 Normal return from readline():
"IP Address: xxx.xxx.xxx.xxx"

Broken readline() return:
"IP Address: xxx.xxx.xxx="

The next two lines after that being:
""
".xxx"
  

Есть идеи, как я мог бы это обойти? На самом деле я не контролирую, какая проблема может быть причиной этого, мне просто нужно разобраться с этим, не слишком сходя с ума.

Соответствующая функция для справки (я знаю, что это беспорядок):

 def getIP(em):
ce = codecs.open(em, encoding='latin1')
iplabel = ""
while  not ("Torrent Hash Value: " in iplabel):
    iplabel = ce.readline()

ipraw = ce.readline()
if ("File Size" in ipraw):
    ipraw = ce.readline()

ip = re.findall( r'[0-9] (?:.[0-9] ){3}', ipraw)
if ip:
    return ip[0]
    ce.close()
else:
    ipraw = ce.readline()
    ip = re.findall( r'[0-9] (?:.[0-9] ){3}', ipraw)
    if ip:
        return ip[0]
        ce.close()
    else:
        return ("No IP found in: "   ipraw)
        ce.close()
  

Комментарии:

1. Вы уверены, что перед двумя = есть только n символ? Как насчет того, что какой-то другой IP имеет какой-то другой символ, подобный = , и может быть более одного? В случае, если у вас есть только =nn , вы можете написать свое регулярное выражение для IP, чтобы учесть это, указав (?:=n*)? непосредственно перед вашей последней частью IP .xxx

2. Проблема в том, что я применяю регулярное выражение только после преобразования строки в строку, а символы новой строки разбивают строку на части. Моим первым побуждением было бы прочитать 3 строки, объединить их, затем выразить regex, но это было бы довольно большой дополнительной нагрузкой на скрипт, если бы он запускался каждый раз, и это был бы довольно спагетти-код, если бы я просто вставил его в еще один else: в конце, поскольку мне нужно было бы сохранить позицию строки и вернуться к ней, если «обычные» поиски не работают.

3. Если ваши данные разбиты на несколько строк, я предлагаю вам, по крайней мере, поработать со строкой, объединив по крайней мере две строки, и на каждом шаге читать еще одну строку и отбрасывать первую строку и соединять вторую строку со следующей новой строкой и повторять этот путь, иначе захват / извлечение правильных шаблонов будет для вас затруднительным.

4. В итоге я просто сохранил ранее прочитанные строки, объединил их, затем использовал re.sub для удаления (= r * n), и это сработало (оказывается, между = и n также был символ r , что на минуту сбило с толку). Спасибо за вашу помощь.

5. Если вы решили проблему, пожалуйста, добавьте и примите ее в качестве ответа, а не помещайте решение в вопрос.

Ответ №1:

Представляется вероятным, что по крайней мере некоторые из обрабатываемых вами электронных писем были закодированы как доступные для печати в кавычках.

Эта кодировка используется для переноса 8-разрядных символьных данных по 7-разрядным (только для ASCII) системам, но она также обеспечивает фиксированную длину строки в 76 символов. Это реализуется путем вставки мягкого разрыва строки, состоящего из «=», за которым следует маркер конца строки.

Python предоставляет модуль quopri для обработки кодирования и декодирования из quoted-printable. Декодирование ваших данных из quoted-printable удалит эти мягкие разрывы строк.

В качестве примера давайте используем первый абзац вашего вопроса.

 >>> import quopri
>>> s = """I'm writing a small script to run through large folders of copyright notice emails and finding relevant information (IP and timestamp). I've already found ways around a few little formatting hurdles (sometimes IP and TS are on different lines, sometimes on same, sometimes in different places, timestamps come in 4 different formats, etc.)."""

>>> # Encode to latin-1 as quopri deals with bytes, not strings.
>>> bs = s.encode('latin-1')

>>> # Encode
>>> encoded = quopri.encodestring(bs)
>>> # Observe the "=n" inserted into the text.
>>> encoded
b"I'm writing a small script to run through large folders of copyright notice=n emails and finding relevant information (IP and timestamp). I've already f=nound ways around a few little formatting hurdles (sometimes IP and TS are o=nn different lines, sometimes on same, sometimes in different places, timest=namps come in 4 different formats, etc.)."

>>> # Printing without decoding from quoted-printable shows the "=".
>>> print(encoded.decode('latin-1'))
I'm writing a small script to run through large folders of copyright notice=
 emails and finding relevant information (IP and timestamp). I've already f=
ound ways around a few little formatting hurdles (sometimes IP and TS are o=
n different lines, sometimes on same, sometimes in different places, timest=
amps come in 4 different formats, etc.).

>>> # Decode from quoted-printable to remove soft line breaks.
>>> print(quopri.decodestring(encoded).decode('latin-1'))
I'm writing a small script to run through large folders of copyright notice emails and finding relevant information (IP and timestamp). I've already found ways around a few little formatting hurdles (sometimes IP and TS are on different lines, sometimes on same, sometimes in different places, timestamps come in 4 different formats, etc.).
  

Для правильного декодирования необходимо обработать все тело сообщения, что противоречит вашему подходу с использованием readline . Один из способов обойти это — загрузить декодированную строку в буфер:

 import io

def getIP(em):
    with open(em, 'rb') as f:
        bs = f.read()
    decoded = quopri.decodestring(bs).decode('latin-1')

    ce = io.StringIO(decoded)
    iplabel = ""
    while  not ("Torrent Hash Value: " in iplabel):
        iplabel = ce.readline()
        ...
  

Если ваши файлы содержат полные электронные письма, включая заголовки, то использование инструментов в модуле email автоматически обработает это декодирование.

 import email
from email import policy

with open('message.eml') as f:
    s = f.read()
msg = email.message_from_string(s, policy=policy.default)
body = msg.get_content()
  

Ответ №2:

Решаемая, если у кого-либо еще есть подобная проблема, сохраните каждую строку как строку, объедините их вместе и удалите их.sub (), имея в виду символы r и n . Мое решение немного похоже на спагетти, но предотвращает выполнение ненужного регулярного выражения для каждого файла:

 def getIP(em):
ce = codecs.open(em, encoding='latin1')
iplabel = ""
while  not ("Torrent Hash Value: " in iplabel):
    iplabel = ce.readline()

ipraw = ce.readline()
if ("File Size" in ipraw):
    ipraw = ce.readline()

ip = re.findall( r'[0-9] (?:.[0-9] ){3}', ipraw)
if ip:
    return ip[0]
    ce.close()
else:
    ipraw2 = ce.readline()                              #made this a new var
    ip = re.findall( r'[0-9] (?:.[0-9] ){3}', ipraw2)
    if ip:
        return ip[0]
        ce.close()
    else:
        ipraw = ipraw   ipraw2                          #Added this section
        ipraw = re.sub(r'(=r*n)', '', ipraw)          #
        ip = re.findall( r'[0-9] (?:.[0-9] ){3}', ipraw)
        if ip:
            return ip[0]
            ce.close()
        else:
            return ("No IP found in: "   ipraw)
            ce.close()