16

为了解决这个问题,我试图围绕 Python 标准库中旨在支持RFC 2231的各种函数。该 RFC 的主要目的似乎有三个方面:允许在标头参数中使用非 ASCII 编码,注意给定值的语言,以及允许标头参数跨越多行。该email.util提供了几个函数来处理这方面的各个方面。据我所知,它们的工作方式如下:

decode_rfc2231仅将此类参数的值拆分为其部分,如下所示:

>>> email.utils.decode_rfc2231("utf-8''T%C3%A4st.txt")
['utf-8', '', 'T%C3%A4st.txt']

decode_params负责检测 RFC2231 编码的参数。它收集属于一起的部分,并将 url 编码的字符串解码为字节序列。然而,这个字节序列随后被编码为 latin1。并且所有值都用引号引起来。此外,对第一个参数有一些特殊处理,它仍然必须是两个元素的元组,但是这两个元素无需修改就被传递给结果。

>>> email.utils.decode_params([
...   (1,2),
...   ("foo","bar"),
...   ("name*","utf-8''T%C3%A4st.txt"),
...   ("baz*0","two"),("baz*1","-part")])
[(1, 2), ('foo', '"bar"'), ('baz', '"two-part"'), ('name', ('utf-8', '', '"Täst.txt"'))]

collapse_rfc2231_value可用于将这三元组的编码、语言和字节序列转换为适当的 unicode 字符串。然而,让我感到困惑的是,如果输入是这样一个三元组,那么引号将被转移到输出中。另一方面,如果输入是单引号字符串,则这些引号将被删除。

>>> [(k, email.utils.collapse_rfc2231_value(v)) for k, v in
...  email.utils.decode_params([
...   (1,2),
...   ("foo","bar"),
...   ("name*","utf-8''T%C3%A4st.txt"),
...   ("baz*0","two"),("baz*1","-part")])[1:]]
[('foo', 'bar'), ('baz', 'two-part'), ('name', '"Täst.txt"')]

所以似乎为了使用所有这些机制,我必须再添加一个步骤来取消引用我遇到的任何元组的第三个元素。这是真的,还是我在这里遗漏了一些观点?我不得不在源代码的帮助下弄清楚上述很多内容,因为文档对细节有点模糊。我无法想象这种选择性取消引用背后的意义是什么。有什么意义吗?

关于如何使用这些功能的最佳参考是什么?

到目前为止,我发现的最好的是实施。在那里,该过程似乎与上面概述的大致相同,但是每个字段都通过, 之后没有引用,并且仅折叠它们的值,所有其他字段都返回一个元组。我希望有更有用的东西。email.message.Message _unquotevaluedecode_paramsget_filenameget_boundary

4

2 回答 2

4

目前 from 的函数email.utils除了 within 之外很少使用email.message。大多数用户似乎更喜欢email.message.Message直接使用。即使我不确定它email.util.

我发现的一个简短示例是这篇博文,但是,它不包含超过一次的句子和一些关于 RFC2231 解析的信息的 SLOC。然而,作者指出,许多 MTA 使用RFC2047代替。根据您的用例,这也可能是一个问题。

从我能找到的几个例子来看,我假设你的解析方式email.util是唯一的方法,即使长列表理解有点难看。

由于在某些方面缺乏示例,编写一个新的 RFC2231 解析器可能是明智之举(如果您真的需要更好、可能更快或更漂亮的代码库)。出于兼容性原因,新的实现可以基于现有的实现,例如Dovecot RFC2231 解析器(您甚至可以使用Dovecot 单元测试email.util。因为 C 代码对我来说似乎很复杂,而且除了Python2 反向移植之外我找不到任何 python 实现移植到 Python的email.util任务并不容易(注意 Dovecot 是LGPL 许可的,这可能是您项目中的一个问题)

我认为email.utilRFC2231 API 的设计目的不是为了方便独立使用,而是作为一堆实用方法用于email.message.Message.

于 2014-01-04T21:38:29.383 回答
0

老问题,但我找不到适用于此的完整答案。所以这就是我最终要做的(在 Python 2.7 上):

def decode_rfc2231_header(header):
    """Decode a RFC 2231 header"""
    # Remove any quotes
    header = email.utils.unquote(header)
    encoding, language, value = email.utils.decode_rfc2231(header)
    value = urllib.unquote(value)
    return email.utils.collapse_rfc2231_value((encoding, language, value))

例如:

>>> name = u'èéêëēėęûüùúūàáâäæãåāāîïíīįì test ôöòóœøōõssśšłžźżçćčñń'
>>> encoded_header = email.utils.encode_rfc2231(name.encode("utf8"), 'utf8', 'en')
>>> print encoded_header 
utf8'en'%C3%A8%C3%A9%C3%AA%C3%AB%C4%93%C4%97%C4%99%C3%BB%C3%BC%C3%B9%C3%BA%C5%AB%C3%A0%C3%A1%C3%A2%C3%A4%C3%A6%C3%A3%C3%A5%C4%81%C4%81%C3%AE%C3%AF%C3%AD%C4%AB%C4%AF%C3%AC%20test%20%C3%B4%C3%B6%C3%B2%C3%B3%C5%93%C3%B8%C5%8D%C3%B5ss%C5%9B%C5%A1%C5%82%C5%BE%C5%BA%C5%BC%C3%A7%C4%87%C4%8D%C3%B1%C5%84
>>> print decode_rfc2231_header(encoded_header)
èéêëēėęûüùúūàáâäæãåāāîïíīįì test ôöòóœøōõssśšłžźżçćčñń
于 2016-08-04T12:03:49.640 回答