tlwh163 发表于 2024-11-5 19:21:11

扫描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


YY菌 发表于 2024-11-12 09:50:14


此方法不能正确识别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:30

哈哈 确实你说的有道理 联通 怎么找出来的 泰牛辣

YY菌 发表于 2024-11-15 17:53:26

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了。

AyalaRs 发表于 2024-11-15 19:24:08

比较有趣的是 "潜水", gbk和 utf8都不乱码

tlwh163 发表于 2024-11-15 19:24:17

有趣!!!

"联通"的二进制是:11000001 10101010 11001101 10101000
好巧不巧构成了2个2字节的UTF8....

应该是有更进一步的检查 能查出它不符合UTF8规则 可惜咱就不清楚了 还是你的办法 靠谱!

AyalaRs 发表于 2024-11-16 10:43:50

tlwh163 发表于 2024-11-15 19:24
有趣!!!

"联通"的二进制是:11000001 10101010 11001101 10101000


没所谓的进一步规则,就算是乱码也是有效编码,只是无意义编码,如果想进一步判断的最好方式是字符域的判断,判断目标字符是哪种组合的字符,比如纯英文,纯中文,纯数字,其他语言,特殊字符组合,无法显示的字符等

YY菌 发表于 2024-11-18 15:34:25

tlwh163 发表于 2024-11-15 19:24
有趣!!!

"联通"的二进制是:11000001 10101010 11001101 10101000


我不是说了,WinAPI自带的MultiByteToWideChar能识别gbk联通吗?这不就已经说明了还是有进一步的方法可以检测出来嘛。

AyalaRs 发表于 2024-11-18 18:49:40

单独判断编码用处不大

tlwh163 发表于 2024-11-19 09:50:26

"潜水"是BUG的存在 管不了了 哈哈

AyalaRs 发表于 2024-11-19 12:17:05

tlwh163 发表于 2024-11-19 09:50
"潜水"是BUG的存在 管不了了 哈哈

这种很多,最好的办法还是我之前说的那种,判断字符域
页: [1]
查看完整版本: 扫描1段数据是否为UTF8编码