找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 287|回复: 10

扫描1段数据是否为UTF8编码

[复制链接]
发表于 2024-11-5 19:21:11 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 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:  [00-7F]
                Case &HE0 To &HEF: w = 2                    '3字节: [E0-EF]
                Case &HC2 To &HDF: w = 1                    '2字节: [C2-DF]
                Case &HF0 To &HF4: w = 3                    '4字节: [F0-F4]
                Case &HF8 To &HFB: w = 4                    '5字节: [F8-FB]
                Case &HFC To &HFD: w = 5                    '6字节: [FC-FD]
                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 '[E0] [A0-BF] [80-BF]
                        If (Utf8(i - 1) = &HED) And (Utf8(i) > &H9F) Then Exit For '[ED] [80-9F] [80-BF]
                    Case 1: Exit For   '2字节 只承认3-6字节的UTF8
                    Case (4 - 1)       '4字节 特例情况
                        If (Utf8(i - 1) = &HF0) And (Utf8(i) < &H90) Then Exit For '[F0] [90-BF] [80-BF] [80-BF]
                        If (Utf8(i - 1) = &HED) And (Utf8(i) > &H8F) Then Exit For '[F4] [80-8F] [80-BF] [80-BF]
                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


回复

使用道具 举报

发表于 2024-11-12 09:50:14 | 显示全部楼层

gbk联通

gbk联通

此方法不能正确识别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嘛)。
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2024-11-14 15:13:30 | 显示全部楼层
哈哈 确实你说的有道理 联通 怎么找出来的 泰牛辣
回复 赞! 靠!

使用道具 举报

发表于 2024-11-15 17:53:26 | 显示全部楼层
tlwh163 发表于 2024-11-14 15:13
哈哈 确实你说的有道理 联通 怎么找出来的 泰牛辣

大概十年前,看到有人说:Windows的记事本输入“联通”然后保存,再打开就会乱码,而“移动”不会,由此证明联通比移动辣鸡
后来,我测试了确实是这样的,因为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了。
回复 赞! 靠!

使用道具 举报

发表于 2024-11-15 19:24:08 | 显示全部楼层
比较有趣的是 "潜水", gbk和 utf8都不乱码
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2024-11-15 19:24:17 | 显示全部楼层
有趣!!!

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

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

使用道具 举报

发表于 2024-11-16 10:43:50 | 显示全部楼层
tlwh163 发表于 2024-11-15 19:24
有趣!!!

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

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

使用道具 举报

发表于 2024-11-18 15:34:25 | 显示全部楼层
tlwh163 发表于 2024-11-15 19:24
有趣!!!

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

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

使用道具 举报

发表于 2024-11-18 18:49:40 | 显示全部楼层
单独判断编码用处不大
屏幕截图 2024-11-18 184944.png
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2024-11-19 09:50:26 | 显示全部楼层
"潜水"是BUG的存在 管不了了 哈哈
回复 赞! 靠!

使用道具 举报

发表于 2024-11-19 12:17:05 | 显示全部楼层
tlwh163 发表于 2024-11-19 09:50
"潜水"是BUG的存在 管不了了 哈哈

这种很多,最好的办法还是我之前说的那种,判断字符域
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-12-12 08:36 , Processed in 0.037271 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表