Delphi XE3でZIPファイルを解凍するときにファイル名のエンコーディングをバイト列から判別するようにしたい

ZIPファイルを解凍するとき、ZIPファイル中のファイル名に使用されているエンコーディングがわかっているなら、そのエンコーディングを使えばいい。
エンコーディングが不明であるなら、ファイル名を格納しているバイト列からエンコーディングを推測したい。

System.Zip.TZipFile を改変して Shift_JIS な Zip ファイルを扱えるようにする (XE3) 」を改変して、エンコーディングを推測できるようにしてみた。

System.ZipExユニットにTBytesToStringEncodingEvent型を追加する。
TBytesToStringEncodingEvent型はZIPファイル中のバイト列をユニコード文字列に変換するときに呼ばれるイベントである。

/// <summary>
///   ZIPファイル中のバイト列をユニコード文字列に変換するためのエンコーディングを取得する関数
/// </summary>
/// <param name="Bytes">
///   ファイル名を表すバイト列
/// </param>
/// <param name="CodePage">
///   バイト列のコードページが不明の時に使用する標準コードページ
/// </param>
/// <returns>
///   文字列に変換するためのエンコーディング
/// </returns>
TBytesToStringEncodingEvent = reference to function(Bytes: TBytes; CodePage: Word): TEncoding;

TZipFileExクラスに次のフィールドを追加する。

private
  FBytesToStringEncoding: TBytesToStringEncodingEvent;  //ADD

public
  property BytesToStringEncoding: TBytesToStringEncodingEvent
    read FBytesToStringEncoding write FBytesToStringEncoding; // ADD

TZipFileEx.TBytesToStringの実装部にコードを追加する。

function TZipFileEx.TBytesToString(B: TBytes) : string;
var
  E: TEncoding;
begin
  if FUTF8Support then
    E := TEncoding.GetEncoding(65001)
  else if Assigned(FBytesToStringEncoding) then //ADD
    E := FBytesToStringEncoding(B, FCodePage)   //ADD
  else
    E := TEncoding.GetEncoding(FCodePage);

これで、ZIPファイル中のファイル名を取得するときにBytesToStringEncodingプロパティが設定されていれば、登録されている処理が呼ばれる。

次に使用例である。

バイト列から文字コードを判定するのにはEncodeDetectユニットを使用した。

uses EncodeDetect;

フォームにBytesToStringEncoding関数の宣言と実装を追加する。
ZIPファイル中のファイル名を取得するときはこの関数が呼ばれる。

/// <summary>
///   ZIPファイル中のバイト列をユニコード文字列に変換するエンコーディングを取得する
/// </summary>
/// <param name="Bytes">
///   ファイル名を表すバイト列
/// </param>
/// <param name="CodePage">
///   バイト列のコードページが不明の時に使用する標準コードページ
/// </param>
function BytesToStringEncoding(Bytes: TBytes; CodePage: Word): TEncoding;

function TFormMain.BytesToStringEncoding(Bytes: TBytes;
  CodePage: Word): TEncoding;
begin
  case EncodeDetect.CheckEncoding(Bytes) of
    ENC_SJIS: Result := TEncoding.GetEncoding(932);
    ENC_EUC: Result := TEncoding.GetEncoding(20932);
    ENC_JIS: Result := TEncoding.GetEncoding(50220);
    ENC_UTF16LE: Result := TEncoding.GetEncoding(1200);
    ENC_UTF16BE: Result := TEncoding.GetEncoding(1201);
    ENC_UTF8: Result := TEncoding.GetEncoding(65001);
    ENC_UTF8BOM: Result := TEncoding.GetEncoding(65001);
    ENC_UTF32LE: Result := TEncoding.GetEncoding(12000);
    ENC_UTF32BE: Result := TEncoding.GetEncoding(12001);
    ENC_UTF7: Result := TEncoding.GetEncoding(65000);
  else
    Result := TEncoding.GetEncoding(CodePage);
  end;
end;

このBytesToStringEncoding関数の実装は、シフトJISとUTF-8の動作しか確認していない。
問題があれば教えてください。

ZIPファイルを開くときの処理は次のようになる。

var
  ZipFile: TZipFileEx;
begin
  ZipFile := TZipFileEx.Create;
  ZipFile.BytesToStringEncoding := BytesToStringEncoding;
  ZipFile.Open(FileName, zmRead, False, TEncoding.Default.CodePage);

当然のことではあるが、ファイル名のエンコーディングがあらかじめわかっているときは、BytesToStringEncodingプロパティを設定する必要はない。
Openメソッドの引数で適切なコードページを指定すればいい。

サンプルアプリケーションを作成してみた。
しばらく使用して検証してみる。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です