最近写了一个小工具,主要实现自动搜索指定路径下所有文本文件内容,并替换指定字符串,记录下几个容易犯错误的地方。
1,fread读出来的buffer存放到string里,如果要搜索指定的字符串,比如“中华人民共和国”,首先要确定文件的编码格式是什么?根据文件的编码格式,对目标串进行编码转换后,才能用string.find去进行查找。
而如何判断文件的编码格式呢,windows下文本文件一般有两种字符集,unicode和gbk,根据文件的前三个字节来决定该文件的编码格式,这三个字节是 “EF BB BF”,如果前三个字节为这三个值,就默认为utf8编码的,否则默认为gbk。
题外话:当然上面的判断比较粗略,因为windows下也可以使用utf16来编码存储文件,打开记事本,另存的时候,使用unicode来编码,或者unicode big endian来编码。windows的记事本会在文件前面用BOM来存储文件的编码格式
UTF8编码的BOM为 EF BB BF
utf16编码的BOM 为FF FE
utf16 big endian为 FE FF ,
通常uft16也被叫做UCS编码,通常在windows上我们讲unicode编码,就是指utf16编码。也有一些术语称之为宽字符,因为他使用两个字节来存储文字,即便是ascii码,也使用两个字节来存储。
那么有人会问,难道单纯根据前三个字节来判断文件编码格式不会有问题,当然会有问题了,比如你用notepad新建一个文本文件,写入“联通”两个字,然后保存,再次打开,你会发现文件成乱码了。
为啥呢,因为windows记事本把它当做了utf8编码来识别了,到了这里有人会问,不是说utf8都有BOM头来标识这个是utf8编码的文件么?没错,utf8并没有强制使用BOM头,有BOM头的utf8文件,也可以转换为utf8无bom编码的格式,可以使用notpad++测试下,因此不能notepad不能单纯的依靠BOM头来判断是否是utf8编码的,而要对文件中的数据进行简单的编码分析来确定,正是这个原因,才导致识别出错。
首先来看下utf8来的编码结构,utf8是采用1-3个字节对字符进行编码,编码字节数和unicode字符集有严格的对应关系。看下面的对应关系表
Unicode 编码集 utf8编码结构
U0001 — U007E 0XXXXXXX
U0080 — U07FF和U0000 110XXXXX 10XXXXXX
U0800 — UFFFF 1110XXXX 10XXXXXX
联通的gbk字符集是“C1 AA CD A8”
C1 AA 对应的二进制: 1100 0001, 1010 1010
CD AB 对应的二进制: 1100 1101, 1010 1000
请注意红色部分,它和UTF8的编码结构完全一致,因此才误将其认为是utf8无BOM编码的文件。可以使用notepad++打开来查看这个文件的编码格式
2,在windows下,换行使用的\r\n来换行,也就是 0D 0A,所以使用二进制来查看文件的时候,会看到换行的地方有很多0D 0A,但是如果fopen的时候仅仅使用 “r”标记来读文件,会读不出来0D,必须使用“rb”标记来读,同样在写文件的时候,也要用“wb”来写,才能吧0D写进去。