判断文本文件是否UTF-8编码

2017-01-13 19:11:16来源:CSDN作者:zb361419953人点击

  utf-8编码的文本文档,有的带有BOM (Byte Order Mark, 字节序标志),即0xEF, 0xBB,0xBF,有的没有。用Windows的notepad编辑的文本保存是会自动添加BOM,我们常用UE编辑器在保存utf-8编码的时候也会自动添加BOM,Notepad++保存utf-8编码时是没有BOM的。其它文本编辑器就没有尝试过,有兴趣的可以自己试试。
  utf-8是一种多字节编码的字符集,表示一个Unicode字符时,它可以是1个至多个字节。即在文本全部是ASCII字符时utf-8是和ASCII一致的(utf-8向下兼容ASCII)。utf-8字节流如下所示:

1字节:0xxxxxxx
2字节:110xxxxx 10xxxxxx
3字节:1110xxxx 10xxxxxx 10xxxxxx

  按照utf-8编码规则进行验证,例如:
  第一个字符的第一个字节的第一个bit为0,说明是个ANSII字符。继续查看第二个字符,若第一个比特是1,则查看第二个比特,若第二个比特为1,如果不为1说明这不是一个utf-8编码的文本。如果第二个比特为1,则查看第三个比特为0,不为0则说明不是utf-8编码,如果是0则说明该字符肯能是2字节的utf-8。查看该字符的第二个字节,如果前两个比特符合10则说明这是一个utf-8编码的字符。依次类推,若一旦有一个bit不满足UTF-8编码要求,就判定文本为ANSI(GBK),若直到文本结束都符合utf-8编码规则,则说明文本是UTF-8编码的。
  由上述描述可知字符的第一个字节如果介于0x80与0xC0之间或大于0xF0则不符合utf-8的编码规则,可直接判断不是utf-8编码的文本。如果第一个字节符合utf-8规则且小于0xC0则判断第二个字节,如果第二个字节和0xC0做与操作结果不是0x80则可判断不是utf-8编码的文本。依次类推,如果第一个字节介于0xE0、0xF0之间,且第二个字节符合规则,第三个字节与第二个字节做同样判断,如果符合规则则该字符是utf-8字符,判断下一个字符直到文本结束。具体的代码实现如下所示,这里列了Delphi及C/C++的两种语言的实现结果:

Delphi:

function IsUtf8Format(buffer: PChar; size: Int64): Boolean;var  ii: Integer;  tmp: Byte;begin  Result := True;  ii := 0;  while ii < size do  begin    tmp := Byte(buffer[ii]);    if tmp < $80 then        //值小于0x80的为ASCII字符       Inc(ii)    else if tmp < $C0 then   //值介于0x80与0xC0之间的为无效UTF-8字符    begin      Result := False;      Break;    end    else if tmp < $E0 then   //此范围内为2字节UTF-8字符    begin      if ii >= size - 1 then        Break;      if (Byte(buffer[ii + 1]) and $C0) <> $80 then      begin        Result := False;        Break;      end;      Inc(ii, 2);    end    else if tmp < $F0 then  //此范围内为3字节UTF-8字符    begin      if ii >= size - 2 then        Break;      if ((Byte(buffer[ii + 1]) and $C0) <> $80) or ((Byte(buffer[ii + 2]) and $C0) <> $80) then      begin        Result := False;        Break;      end;      Inc(ii, 3);    end    else    begin      Result := False;      Break;    end;   end;end;function IsUtf8File(fStream: TFileStream): string;var  fStream: TFileStream;  context: string;begin  fStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);  try    SetLength(context, fStream.Size);    fStream.Read(context[1], fStream.Size);    if isUtf8Format(PChar(context), fStream.Size) then      showMessage('是utf-8编码');    else      showMessage('其它编码');  finally    fStream.Free;  end;end;

C/C++

function IsUtf8Format(buffer: PChar; size: Int64): Boolean;var  ii: Integer;  tmp: Byte;begin  Result := True;  ii := 0;  while ii < size do  begin    tmp := Byte(buffer[ii]);    if tmp < $80 then        //值小于0x80的为ASCII字符       Inc(ii)    else if tmp < $C0 then   //值介于0x80与0xC0之间的为无效UTF-8字符    begin      Result := False;      Break;    end    else if tmp < $E0 then   //此范围内为2字节UTF-8字符    begin      if ii >= size - 1 then        Break;      if (Byte(buffer[ii + 1]) and $C0) <> $80 then      begin        Result := False;        Break;      end;      Inc(ii, 2);    end    else if tmp < $F0 then  //此范围内为3字节UTF-8字符    begin      if ii >= size - 2 then        Break;      if ((Byte(buffer[ii + 1]) and $C0) <> $80) or ((Byte(buffer[ii + 2]) and $C0) <> $80) then      begin        Result := False;        Break;      end;      Inc(ii, 3);    end    else    begin      Result := False;      Break;    end;   end;end;function Utf8StrToAnsi(fStream: TFileStream): string;var  headerStr, context:string;begin  fStream.Position := 0;  SetLength(HeaderStr, 3);  fStream.Read(HeaderStr[1], 3);  if HeaderStr = #$EF#$BB#$BF then  begin    SetLength(context, fStream.Size - 3);    fStream.Read(context[1], fStream.Size - 3);  end  else  begin    fStream.Position := 0;    SetLength(context, fStream.Size);    fStream.Read(context[1], fStream.Size);  end;  Result := Utf8ToAnsi(context);end;

本文参考资料:
  文本utf-8编码判断C/C++源码及utf-8编码解释
  utf-8编码规则解释

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台