shell 如何最好地将混合编码的邮箱文件转换为UTF-8?

2sbarzqh  于 2023-02-09  发布在  Shell
关注(0)|答案(3)|浏览(182)

我有一个.mbox邮箱文件,其中包含数以千计的不同语言的电子邮件,这些电子邮件采用ASCII、ISO-8859-1和UTF-8编码。我想将该文件“扁平化”为UTF-8。
我的第一个尝试是遍历文件,对每个字符执行file -b --mime-encoding,对检测为ISO-8859-1的任何字符执行iconv -f ISO-8859-1 -t UTF-8。我知道UTF-8是ASCII的超集,因此只有ISO-8859-1需要转换。
这花了很长时间,由于某种原因并没有像预期的那样工作。问题人物仍然存在。
在一行程序中是否有一种明显的方法可以实现这一点,或者是否有必要求助于formail来逐个消息地转换文件消息?

nr7wwzry

nr7wwzry1#

据我所知,MIME邮件及其容器**. mbox文件总是以ASCII格式编码,非ASCII源字符以QP**形式呈现。

这意味着你遇到的任何原始的非ascii字符都不会在iso-8859 - 1或其他格式中,但是已经转换成了符合以下正则表达式的字符:=[0-9A-F]{2}
可以以这种方式简单地使用sedecho -e来转换QP编码

sed -re 's/=([0-9A-F]{2})/\\\\u00\1/g' | while read L ; do echo -e $L ; done

说明:

  • sed会将所有QP形式的两个十六进制数字(如"= E9")替换为unicode代码点(如"\u00E9")
  • echo-e可以将后者转换为它们的字符形式(从bash 4.2开始)
fzsnzjdm

fzsnzjdm2#

recode支持从 * 表面 * 解码,例如Quoted-PrintableBase64以及字符集。

recode CP1252/QP..UTF-8 < filein > fileout

现在有一个"真正的"问题(着重号是我的):
数千封不同语言的电子邮件,采用ASCII、ISO-8859 - 1和UTF-8进行了各种编码
这些文件之间的重新编码请求是不同的。ASCII和UTF-8文件不需要重新编码。您需要检查所有这些文件并找出,比方说,iso-8859 - 1文件:

find . -name "*.mbox" -exec file -i "{}" ";" \
   | grep -v "\(us-ascii\|utf-8\)$" \
   | sed -e 's/^\([^:]*\): .*; charset=\([^=]*\)$/recode \2\/QP..utf-8 < "\1" > "\1.tmp" && mv "\1.tmp" "\1"/g' \
   > recode-script.sh

另一个问题是,至少在我有限的经验中,很大一部分文件 * 可能 * 没有在Quoted-Printable表面中编码(您会注意到file可以识别ISO-8859 - 1,即使Quoted-Printable实际上为您提供了一个ASCII7文件),您需要识别它们,这需要 * 解析 * mbox(还因为,虽然不太可能,但您 * 可能 * 甚至在同一消息 * 中具有不同字符集和/或表面 * 的不同多部分节,并且直接使用单个矩阵解码整个文件将解码一些节并损坏其他节)。
因此,为了获得最佳效果,除非您确定只有ISO-8859 - 1(5)文件,formail是你的朋友,你可以用上面脚本的变体来预过滤文件,把注意力集中在真正需要转换的文件上(结果为ascii或utf-8的文件不需要修改).如果你发现需要重新编码的文件都在同一个表面上,那么recode将可能具有最好的性能。

    • 注**:我记得看到过一个实用程序,它可以从输入中获取文本文件列表,并将这些文件输出到一个流中,以"〉〉〉filename〈〈"分隔。(我的谷歌福现在还不能胜任再次找到它的任务)。同样的实用程序将获得这样的流,并将其拆分回原始的单独文件,这样ls *.txt | stitch | stitch -u就不会损坏文件本身。人们可以使用这种方法对许多小文件高效地运行单个recode进程。
1yjd4xko

1yjd4xko3#

我无法实现一个快速的一行程序,但这应该涵盖所有相关情况,如不同的字符集,甚至base64编码字符串:

while read -r encoded; do
  # obtain charset, encoding and content
  IFS="?" read -r tmp charset encoding content tmp <<< "$encoded"
  echo "$encoding" | grep -iqF "b" && encoding="BASE64" || encoding="QP"
  # decode content
  decoded=$(echo "${content//_/ }" | recode "$charset/$encoding..utf8")
  # replace encoded string against decoded string
  encoded=$(echo "$encoded" | sed -e 's/[]\/$*.^[]/\\&/g')
  decoded=$(echo "$decoded" | sed -e 's/[]\/$*.^[]/\\&/g')
  sed -i "s/$encoded/$decoded/g" "$mail_file"
done < <(grep -o "=?.*?=" "$mail_file" )

也许有人可以用awk来修改这个,使它更快。
here借用的转义。

相关问题