最快的SMTP发送行尾修复方法是什么?

2 投票
5 回答
869 浏览
提问于 2025-04-15 13:53

我正在编写一个电子邮件应用程序,用来生成通过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%!

你能做得更好吗?我期待你们用Python的高超技巧来解决这个问题。

5 个回答

1

在你写字符串的时候,直接把它们替换掉,这样就能省事。如果你用正则表达式或者其他方法,就得分两步走:第一步替换字符,第二步再写入。这时候,创建一个新的流类,把它包裹在你要写入的地方是个不错的办法;我们在System.Net.Mail中就是这么做的,这样我就可以用同一个流编码器同时写入文件和网络流。不过,要想给你一个更好的建议,我需要看看你的代码。另外,记住实际的替换速度不会变快,但总的执行时间会减少,因为你只需要走一遍,而不是两遍(前提是你确实把邮件的输出写到某个地方)。

1

可能是因为在字符串中间插入一个额外的字符导致了问题。

当你要替换文本“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();
    }

这个问题让我觉得很有趣,于是我写了一个示例程序来测试。我使用了另一个答案中给出的正则表达式,使用正则表达式的代码是:

static readonly 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,但我猜在不同语言之间运行时间会有相似之处。

2

这个正则表达式帮了我:

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

但最后这个代码更有效:

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

最开始的正则表达式把/usr/share/dict/words里的换行符从\n转换成\r\n花了0.6秒,而新的正则表达式只用了0.3秒,使用replace()方法则只用了0.08秒。

撰写回答