这是一篇转帖共同学习共同讨论
两天前看到你这个问题,那个时候我也一样遇到了,本打算等有大侠来解决的,结果等到花都谢了,废话少说,说说我这几天调研的结果: 1.少数编辑器,比如记事本、Eclipse等,会在保存utf-8编码的文件时,在文件头加上BOM标记。就是网上说的那种方法。 2.有的编辑器,比如Editplus,默认编码是UTF-8,对于未标记的BOM头,就认为是UTF-8的。当然,这个也和编辑器的设置有关系。所以,有的没有BOM头的文件,在记事本中看到的编码是ANSI,而在Editplus等编辑器中看到的是UTF-8 3.关于这个问题,没有特别完美的解决方案,任何编辑器都会面临这个问题,也都没有完美方案,这点是肯定的。因为文件的编码并不是文件的某种属性,文件本身存储的时候只是二进制串,程序完全可以无视BOM头的“潜规则”而把它当做一个想当然的编码文本来解读。 4.说了这么多,上我的代码。这不是我写的,是找到java的方案来移植的: 01.var file:File = e.target as File; 02.var fs:FileStream = new FileStream(); 03.var charset:String = File.systemCharset; 04.fs.open(file, FileMode.READ); 05.var bytes:ByteArray = new ByteArray(); 06.fs.readBytes(bytes, 0, file.size); 07.if(bytes[0] === 0xff && bytes[1] === 0xfe){ 08. charset = 'unicode'; 09.} else if(bytes[0] === 0xfe && bytes[1] === 0xff){ 10. charset = 'UTF-16BE'; 11.} else if(bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf){ 12. charset = 'UTF-8'; 13.} else { 14. //从这里开始,就是你可能没看到的解决方案。 15. var read:uint; 16. for(var i:int = 0, len:int = bytes.length; i < len; i++){ 17. read = bytes[i]; 18. if(read >= 0xf0) { 19. charset = 'GBK'; 20. break; 21. } 22. if(0x80 <= read && read <= 0xbf) { 23. charset = 'GBK'; 24. break; 25. } 26. if(0xc0 <= read && read <= 0xdf){ 27. read = bytes[++i]; 28. if(0x80 <= read && read <= 0xbf) 29. continue; 30. else { 31. charset = 'GBK'; 32. break; 33. } 34. } else if(0xe0 <= read && read <= 0xef){ 35. read = bytes[++i]; 36. if(0x80 <= read && read <= 0xbf) { 37. read = bytes[++i]; 38. if(0x80 <= read && read <= 0xbf) { 39. charset = 'UTF-8'; 40. break; 41. } else { 42. charset = 'GBK'; 43. break; 44. } 45. } else { 46. charset = 'GBK'; 47. break; 48. } 49. } 50. } 51.} 复制代码相信这段代码的前半部分你很熟悉了,后面,是我找到的java某程序的实现思路,判断整个序列中是否有GBK编码的某些特征序列。类词法分析,效率还是可以接受的。此方法基本上对于几种常用的编码基本上都涵盖了。 5.咨询了一些客户端的同学,还有一种思路仅供参考,就是判断形如ANSI -> Unicode -> ANSI这种转换,通过判断转后得到的ANSI序列和转换前的是否一致来决定这个文件是不是ANSI编码的,诚如你所料,这需要一个白名单,然后根据名单,按图索骥的把所有编码二次转码的试一遍。直到成功match。当然,你也可以使用一个默认的编码格式,优先对照,并将剩余的编码做个按概率排序,性能会有提示。 6.总结:根据这些猥琐的方法和这几天的调研,判断文件编码不是一件十分靠谱的事,不可能完美,软件要做的就是尽可能的“智能”匹配出编码,实现这个目的,有这么几种思路: 1.判断BOM头,虽然这也不精确,比如“联通问题”,但挺方便。 2.判断是否存在只有特定编码才有的16进制序列,就好像我上面代码注释后面给出的部分一样。 3.做二次转码,黑盒测试,比对转码前和转码后。 4.如果以上三种方法还有漏网之鱼,就给个输入框,让用户自己填这个神秘又该死的编码吧。
//var bytes:ByteArray = new ByteArray; //filestream.readBytes(bytes,0,file.size); // 读取不同 编码的文档 private function transEncodingText(bytes:ByteArray):String { var str:String = ""; // 1. unicode 文档 开头 16进制码为 FF FE ,对应 十进制 数 为 255,254 if (bytes[0]==255 && bytes[1]==254){ str = bytes.readMultiByte(bytes.length,"unicode"); } // 2.unicode big endian 开头 16进制 为 FE FF,对应十进制数 为 254,255 if (bytes[0]==254 && bytes[1]==255){ str = bytes.readMultiByte(bytes.length,"UTF-16BE"); } // 3.utf-8 开头 16进制 为 EF BB ,对应 十进制 数 为 239,187 if (bytes[0]==239 && bytes[1]==187){ str = bytes.readMultiByte(bytes.length,"utf-8"); } if(bytes[0]==73 && bytes[1]==68){ str = bytes.readMultiByte(bytes.length,"ANSI"); } return str; }