2007年8月5日星期日

关于自动识别文本流的编码方式

上 周遇到一件很讨厌的事情,我设计的那个WEB项目,按计划是在中文系统下进行开发的,但是突然起了点变故要回到日文系统下开发。本来就是一个中文的应用, 一开始设计Demo的时候几十个HTML页面文件用的都是GB2312编码,就为了照顾公司的一些狗屁规定不得不又将它们改成UTF-8编码。
如 果说一个一个用UltraEdit来转码的话,简直是把人当驴子用了,同时考虑到另一项目的源代码文件需要经常在EUC和SHIFT-JIS编码间来回转 换,一狠心,自己用C#写了一个批量文本文件编码转换工具,原理很简单,就是递归遍历一个指定的目录,将所有指定扩展名的文件列表取回,再分别在循环里将 每个文件按照指定的编码读入为字符串,然后将该字符串转换为目标的编码格式并存盘。所用API非常简单,分别有:
  • System.Text.Encoding
  • System.IO.StreamReader(指定Encoding)
  • System.IO.StreamWriter(指定Encoding)
期 间考虑到一个问题,自然就是现在讨论的自动识别源文件的编码方式了。当时没有时间做深入调出,我想,要做这个工作算法定然比较复杂,因为文本文件所用的编 码格式并不是明文记录在文件头或文件尾信息的(UTF例外),如果要做的话,似乎也只能根据字节排列特征来估算出所用的编码格式,既然是估算,那么必然就 有一个相似度的问题导致估算结果不可能百分之百准确无误。平常用Word打开一些非ASCII文本文件的时候就发现过,Word的自动识别功能只能提供一 个最接近的答案,具体则让用户自己选择,还有IE自动识别网页流的时候也是一样不可能完全正确,但是两者的正确率毕竟是非常高的。
今天周末闲来无事,突然想调查一下这个问题。Google了一下,首先发现有人将Mozilla的编码识别组件源代码移植到了Java上,项目名叫做[jchardet],在http://sourceforge.net/上可以找到。另外http://www.codeproject.com/上有人借助IE的IMultiLanguage2接口提供的编码识别功能,使用C#实现了这一愿望,文章标题[Detect Encoding for in- and outgoing text]。真是世上无难事只怕有心人了。
另 外,期间还有两个衍生出来的问题,其一是UNIX<==>DOS文件格式的互相转换,对于这个小工具来说非常容易,只需要使用 StreamReader按行读取文本文件,这样就会丢弃原有的换行格式(DOS的0D0A或UNIX的0A),在使用StringBuilder拼接字 符串的时候给每行按照要求追加0D0A或0A作为换行格式就可以了。
其 二是识别二进制文件或文本文件,这个问题的解决方案很让人无奈,因为归根到底文本文件依然是二进制文件,算法上来说,必须按字节遍历整个文件的每一个字 节,如果发现有字节值小于32并且不是[\r\n\t\p]四字符中任何一个字符的文件,就可以认为是二进制文件。算法很简单,不过考虑到遍历文件的效率 问题,在该工具里还是放弃了这个功能,因为在文件输入的时候已经按照指定的扩展名作了过滤而且对文件大小作了限制,除非有人故意捣蛋的话是不会去把一个二 进制文件当成文本文件处理的。
总的来说,发现文件的编码格式居然也是一个很有趣的课题。

没有评论: