ホーム >プログラム >Delphi 6 ローテクTips

UTF-7で簡単Unicode

UTF-7利用で楽してTStringsでUnicode。

始めに

TStringsでUnicodeを扱おうとURLエンコードもどきの独自変換方法のUnicodeとAnsiStringの可逆変換を思いつきあれこれやってきたところ、1行BBSにご指摘をいただきました

×○_○×:urlエンコードよりBASE64かurl-safe base64にしといたほうがお得 08/6/3 10:47 (火)

BASE64は確かメールの添付ファイルに使うエンコード方式だったような、、で確か結構面倒くさい変換方法だったような…と思いDelphi 6のヘルプを見てみたらちゃんとBase64Encodeなんていう関数があるんですね。
でもこれは引数がStringなのでUnicode文字の変換には使えませんし、ソースコードをあたろうと思ってもソースはついてないようでした。
ではというのでそれに関連するものを探していたらUTF-7というものがあることを知りました。
これ、exeファイルなどのバイナリーだろうがUnicodeの文字列だろうがとにかくなんでもすべて英数字と記号に変換してしまうんですね。
英数字と記号ということは完全にAnsiStringの範囲に入ってしまうわけで、これならUTF-8に変換で失敗したようなこともなくTListBoxやTComboBoxに持たせても文字化けせずにいけます。
で、これがWindows APIのWideCharToMultiByteやMultiByteToWideCharで変換できてしまうことも分かりました。
速度の面でもURLエンコードもどきの独自変換方法のUnicodeとAnsiStringの可逆変換より30倍くらい速いです。

function gfnsWideToUtf7(sSrc: WideString): AnsiString;
//WideStringをUTF-7にエンコードして返す
var
  li_Len:  Integer;
  lp_Buff: PAnsiChar;
begin
  li_Len  := WideCharToMultiByte(CP_UTF7, 0, PWideChar(sSrc), -1, nil, 0, nil, nil);
  lp_Buff := AllocMem(li_Len + 1);
  try
    WideCharToMultiByte(CP_UTF7, 0, PWideChar(sSrc), -1, lp_Buff, li_Len, nil, nil);
    Result := AnsiString(lp_Buff);
  finally
    FreeMem(lp_Buff);
  end;
end;

function gfnsUtf7ToWide(sSrc: AnsiString): WideString;
//UTF-7でエンコードされている文字列をWideStringにして返す
var
  li_Len:  Integer;
  lp_Buff: PWideChar;
begin
  li_Len  := MultiByteToWideChar(CP_UTF7, 0, PAnsiChar(sSrc), -1, nil, 0);
  lp_Buff := AllocMem((li_Len + 1) * 2);
  try
    MultiByteToWideChar(CP_UTF7, 0, PAnsiChar(sSrc), -1, lp_Buff, li_Len);
    Result := WideString(lp_Buff);
  finally
    FreeMem(lp_Buff);
  end;
end;

TStringListなどに入れるときにgfnsWideToUtf7を使い取り出すときにgfnsUtf7ToWideを使えば良いと。

制限

StringGridを使う時などはこのUTF-7を利用したやり方ではうまくないこともあります。
それは

  StringGrid1.Cells[1, 0] := '項目名';
  StringGrid1.Cells[2, 0] := '内容';

などとグリッドにタイトルをつけたい場合です。
OnDrawCellイベントでCellの内容をUTF-7からUnicodeに変換させて表示させるような場合このままでは文字化けします。

  StringGrid1.Cells[1, 0] := gfnsWideToUtf7('項目名');
  StringGrid1.Cells[2, 0] := gfnsWideToUtf7('内容');

としなければなりません。
UTF-7は漢字を含む2バイト文字と一部の記号も変換してしまうからです。
またメニューにUnicodeを表示させたい場合もUTF-7を利用しようとするとメニューエディタでCaptionをセットするときにUTF-7に変換した文字列をセットしなければならなくなるので使い勝手が悪くなります。
こういう場合はUnicodeでしか表せない文字だけを変換するURLエンコードもどきの独自変換方法UnicodeとAnsiStringの可逆変換を使った方が楽になります。

使い方

Unicode対応のドラッグアンドドロップを例として。
WindowsMediaPlayerを取り込んで貼り付け。

//ドラッグアンドドロップ
procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
var
  i, li_Buff: Integer;
  ls_PWFile:  PWideChar;
  lsl_List:   TStrings;
begin
  lsl_List := TStringList.Create;
  try
    //ドロップされたファイルの数だけ処理を行う
    for i := 0 to DragQueryFileW(Msg.Drop, $FFFFFFFF, nil, 0) -1 do begin
      li_Buff   := DragQueryFileW(Msg.Drop, i, nil, 0);
      ls_PWFile := AllocMem((li_Buff + 1) * 2);
      try
        DragQueryFileW(Msg.Drop, i, ls_PWFile, li_Buff +1);
        //リストへWideStringを追加
        lsl_List.Add(gfnsWideToUtf7(WideString(ls_PWFile)));
      finally
        FreeMem(ls_PWFile);
      end;
    end;
    DragFinish(Msg.Drop);  //ドラッグアンドドロップ処理の終了

    //--------------------------------
    //リストのファイルをランダムに取り出して再生
    Randomize;
    //リストからWideStringでランダムに取り出し
    WindowsMediaPlayer1.URL := gfnsUtf7ToWide(lsl_List[Random(lsl_List.Count)]);
    WindowsMediaPlayer1.controls.play;
    //--------------------------------
  finally
    lsl_List.Free;
  end;
end;

2008-06-15:1行BBSのアドバイスを糧にUTF-7に変換させるやり方に変更。