Gnus的编码设定

Gnus 中的编码设定

用了一段时间 Gnus,对 Gnus 中的各种编码设定有了些了解,写出来与大家分享。

<!-- 广告开始

自古以来(其实也没有那么古,从 mule 被纳入 Emacs 算起,还不到30年),Emacs 就以其超强的多国语言编码支持闻名于世。

--> 广告结束

<!-- 闲话开始

Emacs 中的多国语言支持部分,也叫 mule (MULti-language Environment 的缩 写),是日本国家高级工业科技研究所 (National Institute of Advanced Industrial Science and Technology, 缩写为AIST) 编写的,AIST 拥有这部分代 码的专利权。换句话说,mule 的版权是日本政府的,AIST 授权自由软件基金会 (FSF) 使用这部分代码。

Emacs23 中的 mule 部分,主要作者是 Kenichi Handa. 我上五道口那家卖生鱼的 日本餐馆打听了一下,据说日本的 AIST 相当于中国的科学院,这么说,这位 Handa san 不是院士也得是个研究员啊。

Emacs 支持的编码系统之多,简直令人惊叹。不信你用 M-x list-coding-systems 命令把 Emacs 支持的所有编码系统都列出来看看,只要这个世界上曾经存在过编 码系统你都能找到。

其实,有时候我很好奇为什么日本政府的科研机构要研究世界各地的各种编码系 统,后来转念一想也就明白了,广义上可以冠冕堂皇地说,这样可以促进世界各国 之间的文化交流;狭义上也可以悄悄地说,这是情报工作的基础之一,连别人的编 码系统都搞不明白,就算是明码的情报你看到的也是乱码。不知道中科院有没有类 似的机构,如果中科院肯请我去做院士的话,说不定我可以考虑一下。

--> 闲话结束

Gnus 是构建在 Emacs 之上的,自然也就利用了 Emacs 超强的多国语言编码支持。 所以如果你有一封乱码的邮件,如果 Gnus 看不了的话,那么估计卖糕的也不会有办 法了。

为了处理各种编码的邮件,gnus 提供了很多可以设置的变量。不过变量多了也会带 来麻烦,如果你设置不好的话,就有可能开枪打中自己的脚。

先谈谈邮件编码

邮件可以分为两个部分: header 和 body。比较文明的邮件会在 header 的 `Content-Type' 这一行中用 MIME 说明邮件采用了什么编码。而不文明的邮件则缺 少这种说明,对于只有英文的邮件,这样做不会有问题,而对于包含中文的邮件, 就有可能这是导致乱码,小比尔的 Outlook Express 就喜欢发出这种邮件,一些 webmail 也经常干这种事。

另外,在邮件头中,还可以指定邮件的传输编码。如果邮件的内容都是7位的 us-ascii字符,就不会出现什么传输编码的问题,但是因为汉字的编码一般都是8 位的,而有些邮件网关只能传输7bit字符,最高位作其它用途了,所以就出现了 base64, quoted-printable 等把8位字符转换成7位编码进行传输的编码方案。 邮件的传输编码可以在邮件头中用 `Content-Transfer-Encoding' 指出来。

我们可以让 Gnus 把 `Content-Type' 和 `Content-Transfer-Encoding' 字段显示 出来:

(add-hook 'gnus-startup-hook
          '(lambda ()
             (setq gnus-visible-headers
                   (concat "^User-Agent:\\|^Content-Type:\\|"
                           "Content-Transfer-Encoding:\\|"
                           "^X-mailer:\\|^X-Newsreader:\\|^X-Sender:\\|"
                           gnus-visible-headers))))

这样我们就可以看到邮件采用了什么编码(顺便还可以看到对方用的是什么客户端 软件),如果邮件指定了编码,你就会在邮件头部看到这一行:

 Content-Type: text/plain; charset=GB18030

如果邮件指定了传输编码,就可以看到这样一行:

 Content-Transfer-Encoding: base64

Gnus 中与编码有关的变量

需要说明的是,大多数情况下,邮件都是比较文明的,在 Content-Type 字段已经 说明了邮件采用的编码,那么 Gnus 将自动按指定的编码显示邮件。除非 Emacs 本 身就不支持这种编码,否则是不会出现乱码的。

一个常见情况就是使用 Emacs21 或 Emacs22,而不安装 mule-gbk,要知道 Emacs21/22 本身就不支持 GBK 和GB18030,这样对于 charset=gbk, 或 charset=gb18030 的邮件出现乱码就不奇怪了。所以,如果你使用的是 Emacs21 或 Emacs22, 赶紧去下载 mule-gbk 装上吧。注意,对于 Emacs22,mule-gbk 的设置 中一定要加上这一句:

 (utf-translate-cjk-load-tables)

否则 Emacs22 无法进行 gbk <--> utf-8 的转换,而 gnus 是先用 utf-8 编写邮 件,再转换成指定的编码发出去,如果不加上这句设置,就会出现只能发 utf-8 的邮件,不能发 gbk 邮件的怪现象。

下面讨论的设置适用于 Emacs22+mule-gbk 和 Emacs23。

如果邮件中已经用 Content-Type 和 Content-Transfer-Encoding 指定了编码, 那么 gnus 将忠实地采用这些编码处理邮件,这时是不应该出现乱码的。所以,我 们所做的设置基本上都是为了对付那些捣乱的,不指明编码的邮件。

现在我们来看看 gnus 中与编码设置有关的变量。这些变量可以分为两种类型,一 种是与邮件内容有关的,另一种是与组名(group name)有关的。

与邮件内容有关的变量

gnus-default-charset

这个变量指定查看邮件所用编码的默认值(对于未指定编码的邮件)。但是这 个变量的会被下一个变量(gnus-group-charset-alist)覆盖。如果不设置这个 变量,它的值将由 `current-language-environment' 确定。例如:

 (setq gnus-default-charset 'gbk)

gnus-group-charset-alist

这个变量根据组名确定本组的默认编码。设置这个变量时我们要给出一个 (regexp charset) 对,其中 regexp 是匹配组名的正则表达式,charset 是指 定的编码。在这个变量的默认设置中,我们可以查到如下内容:

    ("\\(^\\|:\\)hk\\>\\|\\(^\\|:\\)tw\\>\\|\\<big5\\>" cn-big5)
    ("\\(^\\|:\\)cn\\>\\|\\<chinese\\>" cn-gb-2312)

可以看出,对于以 hk 或 tw 开头(或者组名中包含 :hk 或 :tw)的组,采用 big5编码。而对于以 cn 开头或组名中包含 :cn 的组采用 cn-gb-2312 的编 码。

因为现在 gbk 比 gb2312 应用更广泛,所以我们需要更改这个变量的设置:

    (add-to-list 'gnus-group-charset-alist
        '("\\(^\\|:\\)cn\\>\\|\\<chinese\\>" gbk))

这个变量也可以在 group parameter 中以 (charset . gbk) 的方式指定。

gnus-summary-show-article-charset-alist

有时候,默认的编码还不能解决问题,例如,有人把 big5 编码的邮件投递到 了 cn 开头的组里,而且邮件头中又没有编码设定(插一句,这些邮件一般 都是垃圾邮件),这时就需要手工指定编码。

    (setq gnus-summary-show-article-charset-alist
          '((1 . utf-8)
           (2 . big5)
           (3 . gbk)
           (4 . utf-7)))

进行了这种设定以后,我们看到乱码邮件时就可以用 `1 g' 指定采用 utf-8, `2 g' 指定big5等等,不过能不能正确解码就要看你自己猜的对不对了。

与组名有关的变量

我们订阅新闻组时,可以看到有些服务器上的组名都是英文的,比如在 `news.cn99.com' 这个服务器中组名都是这样的:

gnu.emacs.help
cn.comp.os.linux
tw.bbs.os.linux

而有些服务器的组名却是包含中文的,比如新帆`news.newsfan.net'的组名:

计算机.软件.操作系统.FreeBSD
计算机.软件.操作系统.linux
休闲娱乐.游戏天地.Diablo

下面两个变量是为了让gnus能正确地处理非ascii组名的。

gnus-group-name-charset-group-alist

这个变量根据组名确定组名采用的编码,默认值是`((".*" utf-8))',也就是 默认用 utf-8 处理所有组名。我们可以这样这样设置:

          (setq gnus-group-name-charset-group-alist
              '(("\\.com\\.cn:" . gbk)
                ("news\\.newsfan\\.net" . gbk)))

这样所有组名中含有 .com.cn 或 news.newsfan.net 的组,其名称都采用 gbk 解码。

不过如果我们把 news.newsfan.net 设置为 native method, 那么组名中就不 会出现 news.newsfan.net,那么这个变量就发挥不了作用,怎么办呢?可以采 用下面这个变量。

gnus-group-name-charset-method-alist

还记得吗?我们选择新闻服务器时是怎么设定的?对了,我们是通过设置 method 来选择服务器的,比如:

      (setq gnus-select-method '(nntp "news.newsfan.net"))
      (setq gnus-secondary-select-methods '((nnml "")))

这个变量可以根据我们选择的 method,为来自这个 method 的组设置组名的 编码。

          (setq gnus-group-name-charset-method-alist
              '(((nntp "news.newsfan.net") . gbk)))

这样,所有来自新帆服务器的组名都采用gbk来解码。

设置发出邮件的编码: mm-coding-system-priorities

当我们向外发送邮件时,也可以指定编码,比如我们希望发出的邮件采用gb2312编 码,就可以这样设置:

 (setq mm-coding-system-priorities '(iso-8859-1 gb2312 utf-8))

这样,gnus将先试这采用 iso-8859-1 编码邮件,如果不行就采用 gb2312,实在 不行再采用 utf-8 编码。

这样如果你写了一封纯英文的信件,将会采用 iso-8859-1 发出;如果你写了一封 中文信件,但其中的汉字都在 gb2312 的范围内,则采用gb2312发出;如果你的信 件中含有gb2312以外的字符,则会被以utf-8编码发出。

那么如果想对不同的组采用不同的编码发信,有办法实现吗?可以,通过设置 posting-style 就可以实现。

(setq gnus-posting-styles
      '((".*"
         (name "Brep")
         (address "brep@bogus.com")
         (eval (setq mm-coding-system-priorities
                     '(iso-8859-1 utf-8))))
        ("^cn\\.comp"
         (name "Brep")
         (address "brep@smth.org")
         (eval (setq mm-coding-system-priorities
                     '(iso-8859-1 gb2312 utf-8))))
        ("^tw\\.comp"
         (name "Brep")
         (address "brep@ptt.cc")
         (eval (setq mm-coding-system-priorities
                     '(iso-8859-1 big5 utf-8))))))

这样对于 cn.comp 开头的组,gnus会先尝试采用gb2312发送邮件,不行再用utf-8, 而对于 tw.comp 开头的组,会先尝试采用big5发送邮件,不行再用utf-8。

处理有问题的邮件: gnus-newsgroup-ignored-charsets

有些客户端发出的邮件没有指定正确的MIME类型,例如本来这封邮件是用 gbk 编 码的,但是 MIME 类型却设置成了 x-gbk:

 Content-Type: text/plain; charset=x-gbk

这时gnus解码时会遇到困难,我们可以把这种 MIME 类型加入到 gnus-newsgroup-ignored-charsets 列表中,让 gnus 采用默认的编码处理它。

再比如,有些邮件的 MIME 类型是 charset=gb18030, 对于 emacs23,这是没问题 的,因为 Emacs23 支持 gb18030 编码。但是 emacs22+mule-gbk 根本就不支持 gb18030,那么该怎么办呢?同样我们可以把 gb18030 加入 gnus-newsgroup-ignored-charsets 列表中:

 (setq gnus-newsgroup-ignored-charsets
       '(unknown-8bit x-unknown x-gbk gb18030))

ignored-charsets 也可以在 group parameters 中这样指定:

 (ignored-charsets x-unknown iso-8859-1)

指定传输编码:gnus-group-posting-charset-alist

这个变量可以用来设置邮件头中的 Content-Transfer-Encoding 字段,为邮件指 定传输编码。这个变量的默认值已经设置的很好了,我从来没有遇到需要设置这个 变量的情况。

不要让自己发出乱码的邮件(指定附件文件名和subject的编码方式)

gnus 默认采用 RFC2231 对附件文件名进行编码,有些 MUA 无法识别这种编码。现 在比较流行的方式是采用 RFC2047 对附件文件名进行编码。可以采用如下设定,让 gnus 也采用这种方式对文件名进行编码:

 (defalias 'mail-header-encode-parameter 'rfc2047-encode-parameter)

有很多差劲的邮件客户端无法解码 quoted-printable 编码(看到过 subject 中有 很多 `=' 号的乱码邮件吗?就是由于这个原因产生的。)为了保证我们发出的邮件 subject 采用 base64 编码,而不是采用quoted-printable 编码,最好加上这两句:

 (add-to-list 'rfc2047-charset-encoding-alist '(gbk . B))
 (add-to-list 'rfc2047-charset-encoding-alist '(gb18030 . B))

参考设置

最后给出一个参考设置吧,适用于 Emacs22+mule-gbk 或者 Emacs23:

(setq gnus-default-charset 'gbk)
(add-to-list 'gnus-group-charset-alist
             '("\\(^\\|:\\)cn\\>\\|\\<chinese\\>" gbk))
(setq gnus-summary-show-article-charset-alist
      '((1 . utf-8)
        (2 . big5)
        (3 . gbk)
        (4 . utf-7)))
(setq gnus-group-name-charset-group-alist
      '(("\\.com\\.cn:" . gbk)
        ("news\\.newsfan\\.net" . gbk)))
(setq gnus-group-name-charset-method-alist
      '(((nntp "news.newsfan.net") . gbk)))
(setq gnus-newsgroup-ignored-charsets
      '(unknown-8bit x-unknown x-gbk gb18030))

(defalias 'mail-header-encode-parameter 'rfc2047-encode-parameter)
(add-to-list 'rfc2047-charset-encoding-alist '(gbk . B))
(add-to-list 'rfc2047-charset-encoding-alist '(gb18030 . B))

(setq gnus-posting-styles
      '((".*"
         (name "Brep")
         (address "brep@bogus.com")
         (eval (setq mm-coding-system-priorities
                     '(iso-8859-1 utf-8))))
        ("^cn\\.comp"
         (name "Brep")
         (address "brep@smth.org")
         (eval (setq mm-coding-system-priorities
                     '(iso-8859-1 gb2312 utf-8))))
        ("^tw\\.comp"
         (name "Brep")
         (address "brep@ptt.cc")
         (eval (setq mm-coding-system-priorities
                     '(iso-8859-1 big5 utf-8))))))

(add-hook 'gnus-startup-hook
          '(lambda ()
             (setq gnus-visible-headers
                   (concat "^User-Agent:\\|^Content-Type:\\|"
                           "Content-Transfer-Encoding:\\|"
                           "^X-mailer:\\|^X-Newsreader:\\|^X-Sender:\\|"
                           gnus-visible-headers))))

因为这篇文章主要是讨论Gnus的编码问题,所以这里列出的仅仅是与编码有关的设置。

返回主页

返回主页

Author: Brep <Brep@smth.org>

Date: 2006/11/02 02时20分31秒