2 Stimmen

Was ist der schnellste Weg, um Zeilenumbrüche für den SMTP-Versand zu korrigieren?

Ich programmiere eine E-Mail-Anwendung, die Nachrichten für den Versand über SMTP erstellt. Das bedeutet, dass ich alle einzelnen \n- und \r-Zeichen in die kanonische \r\n-Sequenz ändern muss, die wir alle kennen und lieben. Hier ist der Code, den ich jetzt habe:

CRLF = '\r\n'
msg = re.sub(r'(?

`

Das Problem ist, dass es nicht sehr schnell ist. Bei großen Nachrichten (ca. 80 k) nimmt es fast 30% der Zeit in Anspruch, um eine Nachricht zu senden!

Kannst du es besser machen? Ich erwarte gespannt deine Python-Akrobatik.

`

2voto

Diese Regex hat geholfen:

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

Aber dieser Code hat letztendlich gewonnen:

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

Die ursprünglichen Regexes dauerten 0.6s, um /usr/share/dict/words von \n nach \r\n umzuwandeln, die neuen Regexe dauerten 0.3s und die replace()-Funktionen dauerten 0.08s.

1voto

obelix Punkte 988

Vielleicht liegt es daran, dass das Einfügen eines zusätzlichen Zeichens in der Mitte des Strings ihn zerstört.

Wenn Sie den Text "hello \r world" ersetzen, muss die Größe des gesamten Strings tatsächlich um ein Zeichen auf "hello \r\n world" erhöht werden.

Ich würde vorschlagen, den String zu durchlaufen und Zeichen einzeln zu betrachten. Wenn es kein \r oder \n ist, fügen Sie es einfach dem neuen String hinzu. Wenn es ein \r oder \n ist, fügen Sie dem neuen String die richtigen Werte hinzu.

Code in C# (die Umwandlung in Python sollte trivial sein)

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();
    }

Das war interessant genug, um ein Beispielprogramm zum Testen zu schreiben. Ich habe den in einer anderen Antwort gegebenen Regex verwendet und der Code für die Verwendung des Regex war:

static readonly Regex _r1 = new Regex(@"(?

Ich habe mit einer Reihe von Testfällen experimentiert. Die Ausgaben lauten:

\------------------------
Größe: 1000 Zeichen
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
Kein \\r oder \\n
        String: 00:00:00.0000904
        Regex : 00:00:00.0000628
\\r an jeder 100. Position und \\n an jeder 102. Position
        String: 00:00:00.0002232
        Regex : 00:00:00.0001937
------------------------
Größe: 10000 Zeichen
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
Kein \\r oder \\n
        String: 00:00:00.0006781
        Regex : 00:00:00.0001943
\\r an jeder 100. Position und \\n an jeder 102. Position
        String: 00:00:00.0006537
        Regex : 00:00:00.0005838

was zeigt, dass die String-Ersetzungsfunktion in Fällen, in denen die Anzahl der \r und \n hoch ist, besser abschneidet. Für den regulären Gebrauch ist jedoch der ursprüngliche Regex-Ansatz viel schneller (siehe den letzten Satz von Testfällen - diejenigen ohne \r\n und mit wenigen \r's und \n's)

Natürlich wurde dies in C# und nicht in Python programmiert, aber ich vermute, dass es Ähnlichkeiten in den Laufzeiten über die Sprachen hinweg gibt

1voto

Jeff Tucker Punkte 3412

Ersetzen Sie sie einfach, während Sie den String schreiben, wohin auch immer er geht. Wenn Sie einen regex oder etwas anderes verwenden, werden Sie zwei Durchläufe machen: einen zum Ersetzen der Zeichen und dann einen zum Schreiben. Das Ableiten einer neuen Stream-Klasse und das Umwickeln um das, wohin Sie schreiben, ist ziemlich effektiv; so machen wir es mit System.Net.Mail und das bedeutet, dass ich den gleichen Stream-Encoder zum Schreiben sowohl von Dateien als auch von Netzwerkstreams verwenden kann. Ich müsste jedoch etwas von Ihrem Code sehen, um Ihnen einen wirklich guten Weg zu geben, dies zu tun. Außerdem bedenken Sie, dass der tatsächliche Ersatz nicht wirklich schneller sein wird, jedoch die Gesamtausführungszeit verringert wird, da Sie nur einen Durchlauf anstelle von zwei machen (vorausgesetzt, Sie schreiben tatsächlich die Ausgabe der E-Mail irgendwohin).

0voto

djc Punkte 11265

Sie könnten damit beginnen, die Regexes vorzukompilieren, z.B.

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

`

Dann verwenden Sie FIXCR.sub und FIXLN.sub. Als nächstes könnten Sie versuchen, die Regexes zu kombinieren, mit einem | Ding, was auch helfen sollte.

`

-1voto

ntownsend Punkte 7122

Etwas in dieser Art? Kompilieren Sie Ihren Regex.

CRLF = '\r\n'
cr_or_lf_regex = re.compile(r'(?:(?

`Dann, wenn Sie etwas ersetzen möchten, verwenden Sie dies:

cr_or_lf_regex.sub(CRLF, msg)

BEARBEITEN: Da das obige tatsächlich langsamer ist, werde ich es nochmals versuchen.

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])`

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X