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メソッドの引数で適切なコードページを指定すればいい。
サンプルアプリケーションを作成してみた。
しばらく使用して検証してみる。