扫描1段数据是否为UTF8编码
本帖最后由 tlwh163 于 2024-11-20 19:10 编辑经过高人指点 发现有BUG 重写为以下代码 以免误导!
''扫描1段数据是否为UTF8编码,参数nChar用于返回字符数
Public Function IS_UTF8(Utf8() As Byte, Optional ByRef nChar As Long = 0) As Boolean
Dim w As Integer, wj As Boolean, k As Long, i As Long
For i = LBound(Utf8) To UBound(Utf8)
If w = 0 Then '测试首字节
Select Case Utf8(i)
Case &H0 To &H7F: k = k + 1 'ANSI:
Case &HE0 To &HEF: w = 2 '3字节:
Case &HC2 To &HDF: w = 1 '2字节:
Case &HF0 To &HF4: w = 3 '4字节:
Case &HF8 To &HFB: w = 4 '5字节:
Case &HFC To &HFD: w = 5 '6字节:
Case Else: Exit For '不该出现的字节
End Select
Else '测试后续字节
If (Utf8(i) And &HC0) <> &H80 Then Exit For '必须 10xxxxxx
If Not wj Then
Select Case w
Case (3 - 1) '3字节 特例情况
If (Utf8(i - 1) = &HE0) And (Utf8(i) < &HA0) Then Exit For '
If (Utf8(i - 1) = &HED) And (Utf8(i) > &H9F) Then Exit For '
Case 1: Exit For '2字节 只承认3-6字节的UTF8
Case (4 - 1) '4字节 特例情况
If (Utf8(i - 1) = &HF0) And (Utf8(i) < &H90) Then Exit For '
If (Utf8(i - 1) = &HED) And (Utf8(i) > &H8F) Then Exit For '
End Select: wj = True
End If
w = w - 1: If w = 0 Then wj = False: k = k + 1
End If
Next
nChar = k: IS_UTF8 = (w = 0) '返回的nChar可能包含BOM头(多1字符)
End Function
此方法不能正确识别gbk"联通",存在误认为是UTF8的BUG。
其实最好的检测做法是直接把UTF8转码成UTF32或UTF16,这样就可以正确识别无BOM的UTF8了,大大减小了误判的概率。
至于UTF8转UTF32或UTF16的算法,我这里就不发了,我就发个使用WinAPI来转码的简单例子就行了:
int UTF16长度 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u8"UTF8字符串", UTF8长度, nullptr, 0); // 建议:UTF8长度 把\0结束标记一起算进去,比如 u8"UTF8字符串" 算14长,而不是13长,这样的话哪怕UTF8是空串,在转换成功时至少也会返回1,就可以保证返回0一定是转换失败的。
以上代码在第二个参数中加入了 MB_ERR_INVALID_CHARS 标记,该标记表示如果遇到不符合 UTF8 规则的字符就返回失败,因此该标记可以用来实现无BOM UTF8检测(经过测试,该方法检测gbk的"联通"是一定会失败的,因此可以正确区分UTF8和GBK)。
第5个参数是用来接收转换结果的,我们这里只需要检测字符串是不是UTF8,因此不需要接收传 nullptr 就行了。所以第6个缓冲区长度也相应的要填0(都没有缓冲区,裆燃长度得0嘛)。
哈哈 确实你说的有道理 联通 怎么找出来的 泰牛辣 tlwh163 发表于 2024-11-14 15:13
哈哈 确实你说的有道理 联通 怎么找出来的 泰牛辣
大概十年前,看到有人说:Windows的记事本输入“联通”然后保存,再打开就会乱码,而“移动”不会,由此证明联通比移动辣鸡:lol。
后来,我测试了确实是这样的,因为Windows记事本会把gbk的联通误判为无BOM的UTF8,从而导致乱码。于是引起了我的好奇心,就想研究一下Windows记事本是怎么识别的无BOM UTF8,我当时第一个想法就是Windows应该提供了相关的API,于是用depends.exe查看notepad.exe调用的API,发现其中有一个叫做IsTextUnicode,觉得跟这个API有关,结果查MSDN发现原来这API只能用来识别无BOM的UTF16,不能用来识别无BOM的UTF8,于是再查相关资料,发现了有个开源的IsTextUTF8,测试了一下果然跟Windows记事本一样,都会把gbk联通误判为UTF8。
再后来又发现一个大婶在回复别人的贴子里面说到MultiByteToWideChar的第二个参数设置MB_ERR_INVALID_CHARS就能识别无BOM UTF8,我当时大受震惊还有这种操作?于是赶紧去测试了一下,果然可以,最强的是它居然不会再误判gbk的联通为UTF8了。 比较有趣的是 "潜水", gbk和 utf8都不乱码 有趣!!!
"联通"的二进制是:11000001 10101010 11001101 10101000
好巧不巧构成了2个2字节的UTF8....
应该是有更进一步的检查 能查出它不符合UTF8规则 可惜咱就不清楚了 还是你的办法 靠谱! tlwh163 发表于 2024-11-15 19:24
有趣!!!
"联通"的二进制是:11000001 10101010 11001101 10101000
没所谓的进一步规则,就算是乱码也是有效编码,只是无意义编码,如果想进一步判断的最好方式是字符域的判断,判断目标字符是哪种组合的字符,比如纯英文,纯中文,纯数字,其他语言,特殊字符组合,无法显示的字符等 tlwh163 发表于 2024-11-15 19:24
有趣!!!
"联通"的二进制是:11000001 10101010 11001101 10101000
我不是说了,WinAPI自带的MultiByteToWideChar能识别gbk联通吗?这不就已经说明了还是有进一步的方法可以检测出来嘛。 单独判断编码用处不大
"潜水"是BUG的存在 管不了了 哈哈 tlwh163 发表于 2024-11-19 09:50
"潜水"是BUG的存在 管不了了 哈哈
这种很多,最好的办法还是我之前说的那种,判断字符域
页:
[1]