关于文件在选择编码保存之后,让我们在读取时如何知道它是什么编码,可以这么做,让我们明白需要根据什么来判断.
使用记事本选择不同的编码保存文件,
接着使用uedit32使用二进制方式打开这个文件.
观察打开后内容前面几个byte位(如ff是一个,点8bit)不同,即可发现这些位置原来就是保存着编码标志(正常的说,有某些编辑器不按常规来办话那就没办法);
基于这个规则,我们可以使用读取这些byte位来判断即可知道编码是什么了,就能正常显示内容了.
而fso是不能干这事的,只能用adodb.stream,或者xmlhttprequest也行
本来我想使用ado的二进制的read(2)得到文件的前面二个编码的标志位来判断文件的编码,却发现js无法获取它的值,直接提示出错.虽然可以这部分使用vbs,但是不太想用,今天经过测试与查找资料,终于明白编码一些事情.
失败尝试结果:
var bin2 = ado.read(2);
在ie9调试中得到的值是和出错信息是
![js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客 js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客](https://img1.php1.cn/3cd4a/24d65/711/411df020478db787.jpeg)
![js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客 js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客](https://img1.php1.cn/3cd4a/24d65/711/de9eae17028fbd49.jpeg)
连alert都不允许,更不用说用bin2 & 0xfffe=0xfffe比较了.
经过测试得知:
![js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客 js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客](https://img1.php1.cn/3cd4a/24d65/711/e10aa8048baf39bd.jpeg)
像上图,文本编辑器实际只输入一个感叹号!(unicode唯一对应号是u+2100),选择unicode保存(bom是有标志意思, 在win的记事本中是没有bom一名),
在ue中使用二进制查看,发现实际的硬盘空间中前面二byte放了unicode的标志(ff fe)
那么设置ado.type=2//text数据,1是bin数据时
然后ado也会像记事本一样遵守"标志"规则,前面二byte是标志,并不是内容,判断符合,读取后面的二个byte(既是21 00)输出,即position=0;ado.readText(1) == !,
所以你设置成charset是unicode话,它会按"规则"读取,你永远得不到前面二个.byte.
怎么办?
而ibm的codepage 437编码却是这样解析:它刚好是一个字占8bit,且没有编码标志位的规定(这很重要,关键到你是否能得到全部的内容),所以read(1)和position = 0;是从硬盘储存结构意义上的第一位开始读取的,这跟二进制读取是一样的(即position=0, readText(1) == ff, 而不是unicode规则所得到的 21 00).完全跟unicode处理方式不同,且很好一点就是它一个字刚好就是一byte,
接着把字进行转unicode(js的charcodeat方法返回就是了)码再做比较
,437不是国际通用代码,它会在不同地方每个字对应关系不一样,
有可能在美国,硬盘中一byte位00对应美国字a,而在中国, 00对应的字是 我
,那么你用var c = ado.read(1) 与 我 比较,在中国电脑是对应的,因为byte的00 与我的 unicode是相同的;
,而在美国却跟a不是相同的,a的unicode并不是00
,因为字符比较在js中是需要转成unicode对应编码来比较的,a与我在unicode并不相同,所以结果就不同了.
unicode的编码却是在全球是固定意义的.且ado.read返回的就是unicode码,显示时再转成对应的本地系统的对应字来的,
而它在硬盘中的二进制保存值跟unicode有绝对的对应关系,通过这关系,就能得到直正的硬盘中保存的二进制值了.这是这代码的关键
然后我们得知每种编码使用多少个位置byte做标志,且值是什么,我们进行比较就知道本文件是什么编码了(肯定这些编码标志不会冲突的)
至于某些程序能智能分析,有些是根据后缀再查找里面内容的设置,如html文件,它可以查找 content-type charset=什么来确定应该按什么来解析字符,但是如果你保存时并不是charset=指定相同的编码话,在浏览器中查看时肯定也是乱码的.
下面是ansi与unicode绝对的对应关系.因为一个byte位刚好就是8bit,只能是0-ff,使用cp437来读取肯定不会一个byte会是个100(ff + 1)
表的查找方法是:如要找ef话,就是左边是e0,上边是0f的交点,小框中上面是437对应的字,下面 是对应的unicode码.是16进制的需要使用0x表示
![js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客 js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客](https://img1.php1.cn/3cd4a/24d65/711/836538c7adec2afc.jpeg)
-------------------js+ado代码----
var cPath = 'G:/sys/qidizi/desktop/s.css';
var ado = new ActiveXObject("adodb.stream");
ado.Type = 2;//指定返回数据类型是text
ado.Mode = 3;//指定控件对象是读写模式
ado.CharSet = '437';//ibm的cp437编码是0-255[0-ff],刚好是每个字点8bit位,且没有文件头标志位,会让ado从绝对意义上的第一个byte开始读取
ado.Open();//准备缓存
ado.Position = 0;//初始化指针
ado.LoadFromFile(cPath);//把文件读入缓存
var byte1 = ado.ReadText(1).charCodeAt(0);//读入第一个字|第一个byte
var byte2 = ado.ReadText(1).charCodeAt(0);//读入二个字节|第二个byte位值
var byte3 = ado.ReadText(1).charCodeAt(0);//读入三个字节
var byte4 = ado.ReadText(1).charCodeAt(0);//读入四个字节
if (byte1 == 0x00A0 && byte2 == 0x25a0) {//unicode标志码是二个 ff = u+00A0 fe = U+25A0
var charset = 'unicodeFFFe';//在注册表中是这个名字
} else {//没有标志位,那么按默认来处理吧,中国人使用gbk
var charset = 'gbk';//中文系统的ansii
}
ado.Position = 0;//重设读取指针,让ado重新开始
ado.CharSet = charset;//指定真正的文件保存字符编码集,请看注册表中:HKEY_CLASSES_ROOT\MIME\Database\Charset\
var css = ado.ReadText();//把整个文件内容按保存编码正确读取出来
ado.Close();
ado = null;
alert(css);
------------运行结果
![js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客 js版本的文本文件文件保存编码自动检测功能实现与检测原理 - qidizi - qidizi 的博客](https://img1.php1.cn/3cd4a/24d65/711/c3d99d97d0054b7f.jpeg)
如果使用ansi与无标志utf8,是无法从标志区别,估计一般做法是先使用utf8来判断,如果出现了无法输入的可打印字符,就应该是ansi.