读写文本文件时出现了乱码,找到了ADODB.Stream,可以指定字符集读取文本

Function ReadUTF()

    Filename = "F:\vba\2018 - new\2018-10-29 cad打桩\3 - cad打桩\代码\1.txt"

    With CreateObject("ADODB.Stream")
.Type = '读取文本文件
.Mode = '读写
.Open
.LoadFromFile Filename
' .Charset = "UTF-8"
' .Charset = "Unicode"
.Charset = "GB2312" 'GB2312 = ANSI
.Position =
ReadUTF = .ReadText
.Close
End With Debug.Print ReadUTF End Function

发现ANSI竟然就是GB2312,不过百度一下才发现不仅仅是简单的等于GB2312,有个网址讲的非常的到位

http://club.excelhome.net/thread-998747-1-1.html
三、ANSI编码(ISO8859、GB2312、Big5、JIS等等)

      在二楼的介绍中,我们讲解了计算机是如何显示键盘字符的(通常我们称为ASCII字符)。那么,我们伟大的中华民族的汉字多达十万个,常用的也有两三万,这又该如何显示呢?ASCII标准编码只有0~127,
扩展编码128~255,完全放不下这么多汉字嘛。 莫非,电脑发明的时候,就是给英语国家用的? 是的,你猜对了,最早电脑发明的时候,完全没有考虑这个世界上还会有其他的民族也会用到电脑,而这些民族的字符集会多达数万!有人问了,为什么当年的
科学家不考虑地球的其他民族呢?俺猜想,这与当年存储器的容纳能力有关,如果采用一个字节存储英文字符,完全足够了。如果考虑其他民族,就要用两个甚至更多的存储空间来存放英文字符(以便兼容多民族字符),
而为了这种兼容需要多支付的存储空间,其代价是当年的技术所不能承受的。 后来,随着技术的进步,单位存储空间的费用已经降低到了一个可以接受的地步,为地球多民族的字符集做编码,终于变为了可能。ANSI(美国国家标准学会)在这样的背景下,出台了一系列字符集,这些字符
集再加上其他国家为本国语言所设计的早期字符集,统称为ANSI编码。 所以,提到ANSI字符集,你一定要知道,这是一个由许多不同国家的字符集所构成的一个字符集集合的统称,而不是一个特定的字符集。同一个操作系统在不同的国家的ANSI编码,就是这个国家的字符集编码。
例如,欧洲国家用ISO8859(包括15个不同国家的子集)、中国大陆用GB2312、台湾用Big5、日本用JIS等等 ANSI的子集,在不同的操作系统下是不一样的,这取决于操作系统所支持的字符集。例如,同样是简体中文,Win32下的ANSI是GB2312,Windows95是GBK。Windows7是GB18030(完整支持需要安装扩展字体)等 补充内容 (2013-4-27 00:08):
GB2312、GBK、 GB18030等都属于双字节字符集 (DBCS double-byte character set)。 DBCS是亚洲字符集,包括简中、繁中、日、韩。在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节
作为一个双字节编码。

后又发现 position如果设置为0,竟然会有一个乱码出现,但是文件的内容为什么不是从0开始的,而是从2开始的(position即光标的位置,就是设置从第几个字符开始读取文件内容),原来前两个字节用来定义文件的编码方式了,不过也不能简单的说是前两个字节,有些编码方式不是前两个字节

Function checkcode(path)

    Set objstream = CreateObject("adodb.stream")
objstream.Type =
objstream.Mode =
objstream.Open
objstream.Position =
objstream.LoadFromFile path
bintou = objstream.read() If bintou() = &HEF And bintou() = &HBB Then
checkcode = "utf-8" ElseIf bintou() = &HFF And bintou() = &HFE Then
checkcode = "unicode" ElseIf bintou() = And bintou() = Then
checkcode = "gb2312" Else
checkcode = "其他"
End If ' If AscB(MidB(bintou, 1, 1)) = &HEF And AscB(MidB(bintou, 2, 1)) = &HBB Then
' checkcode = "utf-8"
' ElseIf AscB(MidB(bintou, 1, 1)) = &HFF And AscB(MidB(bintou, 2, 1)) = &HFE Then
' checkcode = "unicode"
' Else
' checkcode = "gb2312"
' End If objstream.Close
Set objstream = Nothing End Function
AscB(MidB(bintou, , )) = &HEF

bintou() = &HEF

经过测试,两种方式都是可以的,但是个人更喜欢简洁明了一点的方式
UTF8文件有BOM和No BOM两种,其区别在于BOM多了三个字节(EFBBBF),对比了二进制文件,发现有这个区别,如果是NoBOM的文件就无法通过前两个字节判断是否为UTF-8文件。

对于没有BOM的UTF-8文件,可以通过遍历判断是否为UTF-8编码。遍历方法可以是二进制的移位判断或者是正则。

https://www.w3.org/International/questions/qa-forms-utf-8

例如,当使用Perl时,可用以下的表达式测试页面是否使用了UTF-8编码:

$field =~
m/\A(
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong -byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{} # straight -byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{} # planes -
| [\xF1-\xF3][\x80-\xBF]{} # planes -
| \xF4[\x80-\x8F][\x80-\xBF]{} # plane
)*\z/x; 以上表达式可转换成其他编程语言。这样就能处理各类问题,例如过长的错误编码以及非法的代理使用。$field 属于UTF-8编码时就会顺利返回,否则失败。

关于 BOM 和 NO BOM

http://club.excelhome.net/thread-998747-2-1.html

十、网络文本的传输标准(UTF-)

很多时候,我们不能直接传输Unicode编码的文本字符。因为Unicode编码不是为传输而设计,其编码为硬性规定。对接收方来说,接到了Unicode编码的数据,很难判断这是什么编码格式,会造成识别上的混乱。
为了兼容ASCII字符,并避免遇到\0造成字符串中断,以及解决英文传输体积被填零放大一倍的问题,提出了UTF-8编码规范。UTF-8的每一个字符按规则编码为1~4任一字节组合。中文大部分为为3字节长,部分四字节。 为了避免在文本传输中发生混乱,互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。 此外,在 Unix 世界中使用 UCS-( 或 UCS-) 会导致非常严重的问题。使用这两种编码方式的字符串,可能会包含诸如 字节 "/0" 或 "/" 作 为很多宽字符的组成部 分,
而这样的字节在文件名和其它 C 语言库函数中有特殊的意义。此外,大多数 Unix 工具都被设计用来处理 ASCII 文件的,不进行大 幅 度的修改是无法读取 -bit 字符的。
基于这些原因,在文件名、文本文件、环境变量等地方, UCS- ( 或 UCS- ) 不是一种合适的 Unicode 外部编码。
在 ISO -: 附 录 D 、 RFC3629 以 及 Unicode 4.0 标准的 3.9 节中均有定义的 UTF- 编码方法则不存在上述问题。所以在类 Unix 风格的操作系统中使用UTF-8来应用 Unicode编码。 UTF-8编码字节含义 对于UTF-8编码中的任意字节B,如果B的第一位为0,则B为ASCII码,并且B独立的表示一个字符;
如果B的第一位为1,第二位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的一个字节,并且不为字符的第一个字节编码;
如果B的前两位为1,第三位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由两个字节表示;
如果B的前三位为1,第四位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由三个字节表示;
如果B的前四位为1,第五位为0,则B为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由四个字节表示; 因此,对UTF-8编码中的任意字节, 根据第一位,可判断是否为ASCII字符; 根据前二位,可判断该字节是否为一个字符编码的第一个字节;
根据前四位(如果前两位均为1),可确定该字节为字符编码的第一个字节,并且可判断对应的字符由几个字节表示;根据前五位(如果前四位为1),可判断编码是否有错误或数据传输过程中是否有错误。 UTF-8的设计有以下的多字符组串行的特质:
单字节字符的最高有效比特永远为0。
多字节串行中的首个字符组的几个最高有效比特决定了串行的长度。最高有效位为110的是2字节串行,而1110的是三字节串行,如此类推。
多字节串行中其余的字节中的首两个最高有效比特为10。
UTF-8的这些特质,保证了一个字符的字节串行不会包含在另一个字符的字节串行中。这确保了数据接收方可以从任意一个位置来判断字符的起始字节,适用于在文字中搜索字或词。
另一方面,由于其字节串行设计,如果一个疑似为字符串的串行被验证为UTF-8编码,那么我们可以有把握地说它是UTF-8字符串。一段两字节随机串行碰巧为合法的UTF-8而非ASCII的机率为32分1。
对于三字节串行的机率为256分1,对更长的串行的机率就更低了。这样从概率上能避免识别的错误。 UCS和 Unicode标准中对UTF-8的定义稍有不同,因为在UCS中,UTF-8字节序列的最大可能长度为6,以表示所有码值不大于U+7FFFFFFF的 字符;而在Unicode中,UTF-8字节序列的最大可能长度为4,
以表示所有码值不大于U+0010FFFF的字符。(这一差别在本质上与UCS- 和UTF-32之间的相同)。 如何识别文本传输的编码:
最标准的途径是检测文本最开头的几个字节(BOM),
开头字节 Charset/encoding,
EF BB BF UTF-
FF FE UTF-/UCS-, little endian -- Windows标准(低位在前),Unicode编码
FE FF UTF-/UCS-, big endian
FF FE UTF-/UCS-, little endian.
FE FF UTF-/UCS-, big-endian. 如果传输的文本没有Bom,那就需要采用正则或者其他方式来遍历文本,判断编码规范了。 这里有个有趣的现象。如果你打开记事本,输入联通两个字,然后把文件保存成ANSI格式。当你再次打开的时候,会发现文本乱码。
这是因为"联通"的ANSI/GB2312编码是:
c1
aa
cd
a8
恰好符合UTF-8的编码规范,因此造成识别错误。如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,
而会用ANSI的方式解读之,这时乱码就不出现了。 对于添加UTF- BOM( 字节串 :0XFF 0XBB 0XBF) 的做法,不被Unicode组织推荐,特别是是不能在 POSIX 系统中采用这种做法。此外,在文件头部添加 UTF- 签名会对很多已有惯例产生妨碍,例如处理一个纯文本程序。 正则表达式判断数据是否为UTF-8编码, 参考 http://www.w3.org/International/questions/qa-forms-utf-8,后面有参考代码 最后,总结一下各种文本编码:
ANSI/GB2312/GBK/GB18030 这些都是区域性编码,没有BOM
UNICODE 目前Windows操作系统使用的内码(低位在前),得到大部分编程语言的支持。标准数据有BOM头。
UTF-/UTF- 通常我们把这个看作传输编码(Unicode的实现方式),用于互联网数据传输。标准数据可能有BOM头。

最后提供一个详细说明 adodb.stream 对象的属性和方法的网址

https://blog.csdn.net/icanlove/article/details/39394701
05-25 23:49