テキストファイルがUTF-8符号化されているかどうかを判断する

11242 ワード

utf-8符号化された文書ドキュメントには、BOM(Byte Order Mark、バイト順フラグ)、すなわち0 xEF、0 xBB、0 xBFがあり、ないものがある.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バイト:0 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
例えば、最初の文字の最初のバイトの最初のbitは0であり、ANSII文字であることを示すutf-8符号化規則に従って検証する.2番目の文字を引き続き表示し、1番目のビットが1であれば2番目のビットを表示し、2番目のビットが1であれば、1でなければutf-8符号化されたテキストではないことを示す.2番目のビットが1の場合、3番目のビットが0であることを確認し、0でない場合はutf-8符号化ではないことを示し、0の場合は文字ケンが2バイトのutf-8であることを示す.この文字の2バイト目を表示し、最初の2ビットが10に一致する場合、utf-8符号化文字であることを示します.順次類推して、1 bitがUTF-8符号化要件を満たさない場合、テキストはANSI(GBK)と判定し、テキストが終了するまでutf-8符号化ルールに合致する場合、テキストはUTF-8符号化であることを示す.上記の説明から分かるように、文字の1バイト目が0 x 80と0 xC 0の間または0 xF 0より大きいとutf-8の符号化規則に合致せず、utf-8の符号化されたテキストではないと直接判断することができる.1バイト目がutf-8ルールに合致し、0 xC 0未満であれば2バイト目を判断し、2バイト目と0 xC 0であれば操作結果が0 x 80でなければutf-8符号化のテキストではないと判断することができる.順に類推すると、1バイト目が0 xE 0、0 xF 0の間にあり、2バイト目がルールに合致している場合、3バイト目が2バイト目と同様に判断し、ルールに合致している場合、その文字はutf-8文字であり、次の文字がテキストが終わるまで判断する.具体的なコード実装は、DelphiおよびC/C++の2つの言語の実装結果を示す.
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符号化規則解釈