Universal Character Set Detector C LibraryをDelphi XEから使用する

C++Builderで試したMozillaエンコーディング自動判別ライブラリである「universalchardet」をDLL化したのをDelphiで動作させてみました。バイナリはhttp://a7m.sakura.ne.jp/SOURCE/universalchardet-CB.7zに用意したもので、C++Builder XE(BCC32.EXE 6.31)でコンパイル

Delphiでuniversalchardet.dllを使うためには、DLLの宣言を記述したユニットを作成しなければならない。

unit universalchardet;

interface

type
  chardet_t = Pointer;

const
  CHARDET_RESULT_OK = 0;
  CHARDET_RESULT_NOMEMORY = -1;
  CHARDET_RESULT_INVALID_DETECTOR = -2;
  CHARDET_MAX_ENCODING_NAME = 64;

  CHARDET_ENCODING_ISO_2022_JP = 'ISO-2022-JP';
  CHARDET_ENCODING_ISO_2022_CN = 'ISO-2022-CN';
  CHARDET_ENCODING_ISO_2022_KR = 'ISO-2022-KR';
  CHARDET_ENCODING_ISO_8859_5 = 'ISO-8859-5';
  CHARDET_ENCODING_ISO_8859_7 = 'ISO-8859-7';
  CHARDET_ENCODING_ISO_8859_8 = 'ISO-8859-8';
  CHARDET_ENCODING_BIG5 = 'BIG5';
  CHARDET_ENCODING_GB18030 = 'GB18030';
  CHARDET_ENCODING_EUC_JP = 'EUC-JP';
  CHARDET_ENCODING_EUC_KR = 'EUC-KR';
  CHARDET_ENCODING_EUC_TW = 'EUC-TW';
  CHARDET_ENCODING_SHIFT_JIS = 'SHIFT_JIS';
  CHARDET_ENCODING_IBM855 = 'IBM855';
  CHARDET_ENCODING_IBM866 = 'IBM866';
  CHARDET_ENCODING_KOI8_R = 'KOI8-R';
  CHARDET_ENCODING_MACCYRILLIC = 'MACCYRILLIC';
  CHARDET_ENCODING_WINDOWS_1251 = 'WINDOWS-1251';
  CHARDET_ENCODING_WINDOWS_1252 = 'WINDOWS-1252';
  CHARDET_ENCODING_WINDOWS_1253 = 'WINDOWS-1253';
  CHARDET_ENCODING_WINDOWS_1255 = 'WINDOWS-1255';
  CHARDET_ENCODING_UTF_8 = 'UTF-8';
  CHARDET_ENCODING_UTF_16BE = 'UTF-16BE';
  CHARDET_ENCODING_UTF_16LE = 'UTF-16LE';
  CHARDET_ENCODING_UTF_32BE = 'UTF-32BE';
  CHARDET_ENCODING_UTF_32LE = 'UTF-32LE';
  CHARDET_ENCODING_HZ_GB_2312 = 'HZ-GB-2312';
  CHARDET_ENCODING_X_ISO_10646_UCS_4_3412 = 'X-ISO-10646-UCS-4-3412';
  CHARDET_ENCODING_X_ISO_10646_UCS_4_2143 = 'X-ISO-10646-UCS-4-2143';

  // Unused
  CHARDET_ENCODING_ISO_8859_2 = 'ISO-8859-2';
  CHARDET_ENCODING_WINDOWS_1250 = 'WINDOWS-1250';
  CHARDET_ENCODING_TIS_620 = 'TIS-620';

function chardet_create(var pdet: chardet_t): integer; stdcall;
  external 'universalchardet.dll' name '_chardet_create';

procedure chardet_destroy(det: chardet_t); stdcall;
  external 'universalchardet.dll' name '_chardet_destroy';

function chardet_handle_data(det: chardet_t; const data: PAnsiChar;
  len: Cardinal): integer; stdcall;
  external 'universalchardet.dll' name '_chardet_handle_data';

function chardet_data_end(det: chardet_t): integer; stdcall;
  external 'universalchardet.dll' name '_chardet_data_end';

function chardet_reset(det: chardet_t): integer; stdcall;
  external 'universalchardet.dll' name '_chardet_reset';

function chardet_get_charset(det: chardet_t; namebuf: PAnsiChar;
  buflen: Cardinal): integer; stdcall;
  external 'universalchardet.dll' name '_chardet_get_charset';

implementation

end.

このファイルをuniversalchardet.pasとして保存する。
実際に使用する場合は、users節にuniversalchardetを追加する。サンプルコードは以下の通り。

procedure TForm1.Button1Click(Sender: TObject);
var
  ms: TMemoryStream;
  enc: TEncoding;
  encname: array[0..CHARDET_MAX_ENCODING_NAME] of AnsiChar;
  det: chardet_t;
  res: Integer;
begin
  ms := TMemoryStream.Create;
  // ファイルのロード
  ms.LoadFromFile(filename);
  // オフセットをストリームの先頭に
  ms.Position := 0;

  // エンコーディングの判別をする
  det := nil;
  chardet_create(det);
  res := chardet_handle_data(det, ms.Memory, ms.Size);
  chardet_data_end(det);

  // エンコーディング名の取得
  chardet_get_charset(det, encname, CHARDET_MAX_ENCODING_NAME);
  chardet_destroy(det);

  // 自動判別結果を元にエンコーディングを判別して読み込み
  enc := nil;
  try
    enc := TEncoding.GetEncoding(encname);
  except
    on EEncodingError do
      enc := TEncoding.Default;
  end;

  Memo1.Lines.LoadFromStream(ms, enc);
  ms.Free;
end;