用VBS判断无BOM头的文件是否UTF-8编码

标签: , , , ,

在 VBS 贴吧看到吧主发的一个《如何区别无BOM头的UTF-8和GBK?》的贴子,故为此文。这种问题都解决不了,VBS 吧主的水平也不过如此。

字节顺序记号(英语:byte-order mark,BOM)是位于码点U+FEFF的统一码字符的名称。当以UTF-16或UTF-32来将UCS/统一码字符所组成的字符串编码时,这个字符被用来标示其字节序。它常被用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的记号。

更多关于 BOM 的资料请自己阅读维基百科

批处理之家有个《VBS版文件编码识别、转换工具(GB2312、UTF-8、Unicode、BIG5)》的帖子,其中检测文件编码的 CheckCode.vbs 是这么写的:

Function CheckCode (Usage)
Dim slz
set slz = CreateObject("Adodb.Stream") 
slz.Type = 1
slz.Mode = 3
slz.Open
slz.Position = 0
slz.Loadfromfile file
Bin=slz.read(2)
if AscB(MidB(Bin,1,1))=&HEF and AscB(MidB(Bin,2,1))=&HBB Then
Codes="UTF-8"
elseif AscB(MidB(Bin,1,1))=&HFF and AscB(MidB(Bin,2,1))=&HFE Then
Codes="Unicode"
else
Codes="GB2312"
end if
WScript.echo file,Usage,Codes
slz.Close
set slz = Nothing
End Function

这个代码只检测了 BOM,把没有 BOM 的文件都认为是 GB2312 编码,很显然是错误的。并不是所有 UTF-8 编码的文件都带 BOM 标记的,事实上,绝大部分 UTF-8 文件都不带 BOM。

理论上,要准确地判断一个文件的编码是很困难的,但是判断 一个文件是否为 UTF-8 编码却相对比较简单,在《构建可扩展的Web站点》一书中就有很好的 PHP 代码:

<?php
function is_valid_utf8(&$input){
    $rx = '[\xC0-\xDF]([^\x80-\xBF]|$)';
    $rx .= '|[\xE0-\xEF].{0,1}([^\x80-\xBF]|$)';
    $rx .= '|[\xF0-\xF7].{0,2}([^\x80-\xBF]|$)';
    $rx .= '|[\xF8-\xFB].{0,3}([^\x80-\xBF]|$)';
    $rx .= '|[\xFC-\xFD].{0,4}([^\x80-\xBF]|$)';
    $rx .= '|[\xFE-\xFE].{0,5}([^\x80-\xBF]|$)';
    $rx .= '|[\x00-\x7F][\x80-\xBF]';
    $rx .= '|[\xC0-\xDF].[\x80-\xBF]';
    $rx .= '|[\xE0-\xEF]..[\x80-\xBF]';
    $rx .= '|[\xF0-\xF7]...[\x80-\xBF]';
    $rx .= '|[\xF8-\xFB]....[\x80-\xBF]';
    $rx .= '|[\xFC-\xFD].....[\x80-\xBF]';
    $rx .= '|[\xFE-\xFE]......[\x80-\xBF]';
    $rx .= '|^[\x80-\xBF]';
    return preg_match("!$rx!", $input) ? 0 : 1;
}
?>

我们要做的就是改写成 VBS:

Function read(path)
    '将Byte()数组转成String字符串
    Dim ado, a(), i, n
    Set ado = CreateObject("ADODB.Stream")
    ado.Type = 1 : ado.Open
    ado.LoadFromFile path
    n = ado.Size - 1
    ReDim a(n)
    For i = 0 To n
        a(i) = ChrW(AscB(ado.Read(1)))
    Next
    read = Join(a, "")
End Function

'Author: Demon
'Date: 2011/11/10
'Website: http://demon.tw

Function is_valid_utf8(ByRef input) 'ByRef以提高效率
    Dim s, re
    Set re = New Regexp
    s = "[\xC0-\xDF]([^\x80-\xBF]|$)"
    s = s & "|[\xE0-\xEF].{0,1}([^\x80-\xBF]|$)"
    s = s & "|[\xF0-\xF7].{0,2}([^\x80-\xBF]|$)"
    s = s & "|[\xF8-\xFB].{0,3}([^\x80-\xBF]|$)"
    s = s & "|[\xFC-\xFD].{0,4}([^\x80-\xBF]|$)"
    s = s & "|[\xFE-\xFE].{0,5}([^\x80-\xBF]|$)"
    s = s & "|[\x00-\x7F][\x80-\xBF]"
    s = s & "|[\xC0-\xDF].[\x80-\xBF]"
    s = s & "|[\xE0-\xEF]..[\x80-\xBF]"
    s = s & "|[\xF0-\xF7]...[\x80-\xBF]"
    s = s & "|[\xF8-\xFB]....[\x80-\xBF]"
    s = s & "|[\xFC-\xFD].....[\x80-\xBF]"
    s = s & "|[\xFE-\xFE]......[\x80-\xBF]"
    s = s & "|^[\x80-\xBF]"
    re.Pattern = s
    is_valid_utf8 = (Not re.Test(input))
End Function

s = read("utf-8.txt") '读取文件
WScript.Echo is_valid_utf8(s) '判断是否UTF-8

随机文章:

  1. 用C语言实现PHP的basename函数
  2. COMRaider
  3. Perl常用的内置特殊变量
  4. RasEnumConnections函数返回632错误
  5. 在C语言程序中使用cURL库(libcurl)

11 条评论 发表在“用VBS判断无BOM头的文件是否UTF-8编码”上

  1. PopEye说道:

    测试了一下,使用3个文本文件,一个无BOM的UTF8,一个有BOM的UTF8,一个ANSI,程序给出的结果都是-1,貌似有些问题。

  2. PopEye说道:

    文本1是中英文混合,文本2和3都是纯英文。

  3. PopEye说道:

    文本1(Without BOM):
    [宽带连接]
    Encoding=1
    Type=5
    AutoLogon=0
    UseRasCredentials=0
    ……

    文本2(With BOM):
    SWADSDA2

    文本3(ANSI):
    sfsdfsdf

    ————-兔年和龙年的分隔线————-

    祝新年快乐!

    • Demon说道:

      这个结果有什么问题吗?

      ANSI(GB2312)在ASCII范围内和UTF-8是兼容的,所以纯英文文本既可以认为是ANSI,也可以认为是UTF-8。

      至于有BOM头的文件,这并不是该函数的功能。

      • thysea说道:

        多谢Demon,收益匪浅
        顺便问下:

        “至于有BOM头的文件,这并不是该函数的功能。”

        后面判断utf-8的代码只能判断无BOM头的文件么?

  4. yangcer说道:

    你好,我用java写了下,并不能判断出来,你能不能把这段代码更为详尽的解释下,多谢了!

  5. yu2n说道:

    [code]
    ‘ 检察文件是否为UTF-8,有BOM/无BOM皆可,读取文件BOM头/前4Kbit判读
    Function is_valid_utf8(ByVal file)
    is_valid_utf8 = False
    ‘将Byte()数组转成String字符串
    Dim ado, a(), i, n, Bin, s, re
    Set ado = CreateObject(“ADODB.Stream”)
    ado.Type = 1 : ado.Open
    ado.LoadFromFile file
    n = ado.Size – 1
    ‘ 检查空文件/限制读取4Kbit
    If n = 1024*4-1 Then n = 1024*4-1 ‘4Kbit
    ‘ 使用BOM判断
    Bin = ado.read(2)
    If AscB(MidB(Bin,1,1)) = &HEF And AscB(MidB(Bin,2,1)) = &HBB Then
    is_valid_utf8 = True : Exit Function
    End If
    ‘将Byte()数组转成String字符串
    ReDim a(n) : ado.Position = 0
    For i = 0 To n
    a(i) = ChrW(AscB(ado.Read(1)))
    Next
    ‘使用正则表达式判断
    Set re = New Regexp
    s = “[\xC0-\xDF]([^\x80-\xBF]|$)”
    s = s & “|[\xE0-\xEF].{0,1}([^\x80-\xBF]|$)”
    s = s & “|[\xF0-\xF7].{0,2}([^\x80-\xBF]|$)”
    s = s & “|[\xF8-\xFB].{0,3}([^\x80-\xBF]|$)”
    s = s & “|[\xFC-\xFD].{0,4}([^\x80-\xBF]|$)”
    s = s & “|[\xFE-\xFE].{0,5}([^\x80-\xBF]|$)”
    s = s & “|[\x00-\x7F][\x80-\xBF]”
    s = s & “|[\xC0-\xDF].[\x80-\xBF]”
    s = s & “|[\xE0-\xEF]..[\x80-\xBF]”
    s = s & “|[\xF0-\xF7]…[\x80-\xBF]”
    s = s & “|[\xF8-\xFB]….[\x80-\xBF]”
    s = s & “|[\xFC-\xFD]…..[\x80-\xBF]”
    s = s & “|[\xFE-\xFE]……[\x80-\xBF]”
    s = s & “|^[\x80-\xBF]”
    re.Pattern = s
    is_valid_utf8 = (Not re.Test(Join(a, “”)))
    End Function
    [/code]

    • Crow说道:

      我对代码作了一些修改,,希望扩展一下功能,,但是发现对utf8的检测仍然不是很理想,,有时候会把含中文的gbk(ANSI)文件当成utf8,,把我测试的文件打包上传了,,希望大大们能帮我完善一下,,多谢!!
      http://www.funp.net/796264

留下回复