Какой самый быстрый способ исправить окончания строк для отправки SMTP? - PullRequest
2 голосов
/ 26 августа 2009

Я пишу почтовое приложение, которое выдает сообщения для отправки по SMTP. Это означает, что мне нужно изменить все одинокие символы \ n и \ r на каноническую последовательность \ r \ n, которую мы все знаем и любим. Вот код, который я получил сейчас:

CRLF = '\r\n'
msg = re.sub(r'(?<!\r)\n', CRLF, msg)
msg = re.sub(r'\r(?!\n)', CRLF, msg)

Проблема в том, что это не очень быстро. В больших сообщениях (около 80 тыс.) Отправка сообщения занимает около 30% времени!

Ты можешь сделать лучше? Я с нетерпением жду твоей гимнастики на Питоне.

Ответы [ 5 ]

2 голосов
/ 27 августа 2009

Это регулярное выражение помогло:

re.sub(r'\r\n|\r|\n', '\r\n', msg)

Но этот код в итоге выиграл:

msg.replace('\r\n','\n').replace('\r','\n').replace('\n','\r\n')

Первоначальным регулярным выражениям потребовалось 0,6 с, чтобы преобразовать / usr / share / dict / words из \ n в \ r \ n, новому регулярному выражению - 3 с, а функции replace () - 0,08 с

1 голос
/ 27 августа 2009

Заменяйте их на лету, когда вы пишете строку, куда бы она ни шла. Если вы используете регулярное выражение или что-то еще, вы будете делать два прохода: один, чтобы заменить символы, а другой, чтобы написать его. Извлечение нового класса Stream и наложение на него всего, что вы пишете, довольно эффективно; это то, как мы делаем это с System.Net.Mail, и это означает, что я могу использовать один и тот же кодировщик потока для записи как файлов, так и сетевых потоков. Я должен был увидеть часть вашего кода, чтобы дать вам действительно хороший способ сделать это. Кроме того, имейте в виду, что фактическая замена на самом деле не будет быстрее, однако общее время выполнения будет сокращено, поскольку вы делаете только один проход вместо двух (при условии, что вы на самом деле записываете вывод электронной почты где-то).

1 голос
/ 26 августа 2009

Может быть, это тот факт, что вставка дополнительного символа в середине строки убивает его.

Когда вы подставляете текст "hello \ r world", он должен фактически увеличить размер всей строки на один символ до "hello \ r \ n world".

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

Код в C # (преобразование в python должно быть тривиальным)

        string FixLineEndings(string input)
    {
        if (string.IsNullOrEmpty(input))
            return string.Empty;

        StringBuilder rv = new StringBuilder(input.Length);

        for(int i = 0; i < input.Length; i++)
        {
            char c = input[i];
            if (c != '\r' && c != '\n')
            {
                rv.Append(c);
            }
            else if (c == '\n')
            {
                rv.Append("\r\n");
            }
            else if (c == '\r')
            {
                if (i == input.Length - 1)
                {
                    rv.Append("\r\n"); //a \r at the end of the string
                }
                else if (input[i + 1] != '\n')
                {
                    rv.Append("\r\n");
                }

            }
        }

        return rv.ToString();
    }

Это было достаточно интересно, чтобы написать пример программы для тестирования. Я использовал регулярное выражение, приведенное в другом ответе, и код для использования регулярного выражения:

статическое только для чтения Regex _r1 = new Regex (@ "(?

Я пытался с кучей тестов. Выходы:

------------------------
Size: 1000 characters
All\r
        String: 00:00:00.0038237
        Regex : 00:00:00.0047669
All\r\n
        String: 00:00:00.0001745
        Regex : 00:00:00.0009238
All\n
        String: 00:00:00.0024014
        Regex : 00:00:00.0029281
No \r or \n
        String: 00:00:00.0000904
        Regex : 00:00:00.0000628
\r at every 100th position and \n at every 102th position
        String: 00:00:00.0002232
        Regex : 00:00:00.0001937
------------------------
Size: 10000 characters
All\r
        String: 00:00:00.0010271
        Regex : 00:00:00.0096480
All\r\n
        String: 00:00:00.0006441
        Regex : 00:00:00.0038943
All\n
        String: 00:00:00.0010618
        Regex : 00:00:00.0136604
No \r or \n
        String: 00:00:00.0006781
        Regex : 00:00:00.0001943
\r at every 100th position and \n at every 102th position
        String: 00:00:00.0006537
        Regex : 00:00:00.0005838

, которая показывает, что функция замены строк работает лучше в случаях, когда число \ r и \ n велико. Для регулярного использования, хотя оригинальный подход регулярных выражений намного быстрее (см. Последний набор тестов - те, которые без \ r \ n и с небольшим количеством \ r и \ n)

Это, конечно, было написано на C #, а не на Python, но я предполагаю, что во времени выполнения между языками будут сходства

0 голосов
/ 26 августа 2009

Как то так? Скомпилируйте свое регулярное выражение.

CRLF = '\r\n'
cr_or_lf_regex = re.compile(r'(?:(?<!\r)\n)|(?:\r(?!\n))')

Затем, когда вы хотите заменить вещи, используйте это:

cr_or_lf_regex.sub(CRLF, msg)

РЕДАКТИРОВАТЬ: Поскольку выше на самом деле медленнее, позвольте мне сделать еще один удар в этом.

last_chr = ''

def fix_crlf(input_chr):
    global last_chr
    if input_chr != '\r' and input_chr != '\n' and last_chr != '\r':
        result = input_chr
    else:
        if last_chr == '\r' and input_chr == '\n': result = '\r\n'
        elif last_chr != '\r' and input_chr == '\n': result = '\r\n'
        elif last_chr == '\r' and input_chr != '\n': result = '\r\n%s' % input_chr
        else: result = ''

    last_chr = input_chr
    return result

fixed_msg = ''.join([fix_crlf(c) for c in msg])
0 голосов
/ 26 августа 2009

Вы можете начать с предварительной компиляции регулярных выражений, например,

FIXCR = re.compile(r'\r(?!\n)')
FIXLN = re.compile(r'(?<!\r)\n')

Затем используйте FIXCR.sub и FIXLN.sub. Затем вы можете попытаться объединить регулярные выражения в один, с | вещь, которая также должна помочь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...