Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2008年04月03日] [更新 2014年04月12日] Delphi サンプルプログラム集
   
886_MLang.dll の関数を使用した文字コードの変換
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
886_ChangeCodePage.zip [3,396 KB] 2012年04月12日版 (EXE 同梱)



  • 2014年04月12日
  • Windows 7 U64(SP1) + Delphi XE Pro で動作確認
  • 言語名の取得のサンプルを Vista 以降に対応
  • URL の文字列のデコード,エンコードのサンプルを,HTTPApp 内の関数を使用するコードから,TIdURL を使用するコードに変更




文字コードの変換関係のテストプログラム


テキストファイルの文字のコードページの変換のサンプルです.
主に IE (Web ブラウザ Internet Explorer) で使用されている MLang.dll の機能を使用した方法をテストしています.
本ページで使用している MultiLanguage_TLB.pas は [IDL コンパイラを使用したタイプライブラリの作成と取り込み] 記事の説明に従って作成したものです.このユニットを,プロジェクトに追加して使用しています.


Delphi 2009 以降では,テキストファイルの保存の時に TEncoding を使用して,コードページを指定することができます.また,以下のリンクの記事のように,指定コードページ (文字コード) の文字列型を定義して文字列を処理てきるようになり,文字コードの変換が簡単にできます.
本ページのようなサンプルコードを利用する必要性はほとんどないでしょう.





01_文字コード ( コードページ ) の言語名一覧


文字コードに対応する言語名を取得するサンプルです.
言語名は,下図のように,レジストリの以下の場所にあります.その中の Description の値が言語名です.

    HKEY_CLASSES_ROOT\MIME\Database\Codepage


  • 図1
  • Windows XP の文字コードとその言語名

Windows Vista 以降では,下図のように,この Description キーに言語名はありません.


  • 図2
  • Windows 7 の文字コードとその言語名.実際には言語名はない

そこで,本サンプルでは,Vista 以降とそれよりも前とで処理を分岐しています. Windows XP 以前ではレジストリの値から言語名を取得します.一方,Vista 以降では IMultiLanguage インターフェイス の GetCodePageInfo 関数で取得するようにしています.
下図はその実行結果です.システムで使用可能な言語名をリストします.


図3
設計時画面

図4
実行結果
  • 左が文字コードページの値
  • 右は,それに対応する言語名

リスト1
システムで使用可能な文字コードと言語名をリストする
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    CodeList       : TStringList;
    DistributeList : TStringList;
    procedure GetCodeAndDistributeList(slCode, slDistribute : TStringList);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Registry, MultiLanguage_TLB, ComObj;

{$R *.dfm}

//=============================================================================
//  システムで使用可能な文字コードページ(文字セット)をリスト表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  StrText : String;
  i       : Integer;
begin
  CodeList       := TStringList.Create;
  DistributeList := TStringList.Create;

  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;
  try
    GetCodeAndDistributeList(CodeList, DistributeList);

    for i := 0 to CodeList.Count - 1 do begin
      StrText := Format('%5s', [CodeList[i]]);
      Memo1.Lines.Add(StrText + '  ' + DistributeList[i]);
      Application.ProcessMessages;
    end;
  finally
    Memo1.Lines.EndUpdate;
    FreeAndNil(CodeList);
    FreeAndNil(DistributeList);
  end;
end;

//=============================================================================
//  文字コードと文字コード名のリストを作成
//  slCodeList   : 文字コードの格納リスト
//  slDistribute : 文字コード名の格納リスト
//=============================================================================
procedure TForm1.GetCodeAndDistributeList(slCode, slDistribute : TStringList);
var
  Registry       : TRegistry;
  RegKey         : String;
  slGCodePage    : TStringList;
  MLang          : IMultiLanguage;
  MIMECPInfo     : TMIMECPINFO;
  i              : Integer;
  strDescription : String;
begin
  slGCodePage := TStringList.Create;
  slCode.Clear;
  slDistribute.Clear;

  //レジストリから文字コードの情報を取得してリストに格納
  Registry := TRegistry.Create(KEY_READ);
  try
    Registry.RootKey := HKEY_CLASSES_ROOT;

    //レジストリから文字コードの数値のリストを取得して配列に格納
    RegKey := '\mime\database\codepage\';
    Registry.OpenKey(RegKey, False);
    Registry.GetKeyNames(slGCodePage);
    Registry.CloseKey;

    //mlangのオブジェクトを生成
    MLang := CoCMultiLanguage.Create;

    //レジストリから取得した文字コードのリストの説明文字を順番に取得
    for i := 0 to slGCodePage.Count - 1 do begin
      Registry.OpenKey(RegKey + slGCodePage[i], False);

      //Windows Vista以降はレジストリから取得できないのでmlangの関数を使用
      if CheckWin32Version(6, 0) then begin
        MLang.GetCodePageInfo(StrToInt(slGCodePage[i]), MIMECPInfo);
        strDescription := MIMECPInfo.wszDescription;
      end else begin
        strDescription := Registry.ReadString('Description');
      end;
      Registry.CloseKey;

      slCode.Add(slGCodePage[i]);
      slDistribute.Add(strDescription);
      Application.ProcessMessages;
    end;
  finally
    Registry.Free;
    FreeAndNil(slGCodePage);
    MLang := nil;
  end;
end;

end.




02_MLang.dll の関数を使用した文字コード変換


Mlang.dll 内の ConvertINetString を使用して,コードページを変換するサンプルです.
ConvertINetString 関数の実行が成功しても,必ずしも変換に成功するとは限りません.


図5
設計時画面
図6
実行中の画面
  • [Read File] ボタンをクリックして,テキストファイルをメモリストリームに読み込む
  • ボタンをクリックして文字コードを変換
図7
添付のテスト用テキストファイル
図8
実行結果例
  • EUC.txt を選択し,[to_Shift-S] を実行した結果できるファイル

リスト2
MLang.dll の関数を使用して,テキストファイルの文字のコードページを変換する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    SpeedButton4: TSpeedButton;
    SpeedButton5: TSpeedButton;
    SpeedButton6: TSpeedButton;
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
    procedure SpeedButton4Click(Sender: TObject);
    procedure SpeedButton5Click(Sender: TObject);
    procedure SpeedButton6Click(Sender: TObject);
  private
    { Private 宣言 }
    ReadFileName : String;
    SaveFileName : String;
    ReadMemStream : TMemoryStream;
    SaveMemStream : TMemoryStream;
    function CharCodeConvert(SrcCode,DstCode: Integer; SrcStream,
      DstStream: TMemoryStream): Boolean;
  public
    { Public 宣言 }
  end;

  //ConvertINetStringはDelphi XEにはないので定義
  function ConvertINetString (
              lpdwMode: LPDWORD;
              dwSrcEncoding: DWORD;
              dwDstEncoding: DWORD;
              lpSrcStr: PChar;
              lpnSrcSize: pointer;
              lpDstStr: PChar;
              lpnDstSize: pointer): HRESULT; stdcall; external 'mlang.dll';

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム表示開始時の処理
//
//  ReadStream : テキストファイルから読出した内容を保持
//  SaveStream : 文字コード変換後の内容を保持
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  SpeedButton1.Enabled := False;
  SpeedButton2.Enabled := False;
  SpeedButton3.Enabled := False;
  SpeedButton4.Enabled := False;
  SpeedButton5.Enabled := False;
  SpeedButton6.Enabled := False;

  ReadMemStream := TMemoryStream.Create;
  SaveMemStream := TMemoryStream.Create;
end;

//=============================================================================
//  フォームを閉じる時の処理
//  使用したメモリストリームを解放
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(ReadMemStream);
  FreeAndNil(SaveMemStream);
end;

//=============================================================================
//  OpenDialogでテキストファイルを選択し
//  変換元のテキストをメモリストリームに読込む
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenDialog1.InitialDir := ExpandFileName('../BeforeFile');

  if OpenDialog1.Execute then begin
    ReadFileName := OpenDialog1.FileName;
    ReadMemStream.Clear;
    ReadMemStream.LoadFromFile(ReadFileName);

    SpeedButton1.Enabled := True;
    SpeedButton2.Enabled := True;
    SpeedButton3.Enabled := True;
    SpeedButton4.Enabled := True;
    SpeedButton5.Enabled := True;
    SpeedButton6.Enabled := True;
  end;
end;

//=============================================================================
//  ECUへの変換
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_ECUを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_ECU.txt';

  Screen.Cursor := crHourGlass;
  try
    CharCodeConvert(50932, 51932, ReadMemStream, SaveMemStream);
    SaveMemStream.SaveToFile(SaveFileName);
  finally
    Screen.Cursor := crDefault;
  end;
end;

//=============================================================================
//  JISへの変換
//=============================================================================
procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_JISを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_JIS.txt';

  Screen.Cursor := crHourGlass;
  try
    CharCodeConvert(50932, 50222, ReadMemStream, SaveMemStream);
    SaveMemStream.SaveToFile(SaveFileName);
  finally
    Screen.Cursor := crDefault;
  end;
end;

//=============================================================================
//  Shit-JISへの変換
//=============================================================================
procedure TForm1.SpeedButton3Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_S-JISを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_S-JIS.txt';

  Screen.Cursor := crHourGlass;
  try
    CharCodeConvert(50932, 932, ReadMemStream, SaveMemStream);
    SaveMemStream.SaveToFile(SaveFileName);
  finally
    Screen.Cursor := crDefault;
  end;
end;

//=============================================================================
//  UTF-16 Unicode(Big-Endian)への変換
//=============================================================================
procedure TForm1.SpeedButton4Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_UNI-BEを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_UNI-BE.txt';

  Screen.Cursor := crHourGlass;
  try
    CharCodeConvert(50932, 1201, ReadMemStream, SaveMemStream);
    SaveMemStream.SaveToFile(SaveFileName);
  finally
    Screen.Cursor := crDefault;
  end;
end;

//=============================================================================
//  UTF-16 Unicode(Little-Endian)への変換
//=============================================================================
procedure TForm1.SpeedButton5Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_UNI-LEを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_UNI-LE.txt';

  Screen.Cursor := crHourGlass;
  try
    CharCodeConvert(50932, 1200, ReadMemStream, SaveMemStream);
    SaveMemStream.SaveToFile(SaveFileName);
  finally
    Screen.Cursor := crDefault;
  end;
end;

//=============================================================================
//  UTF-8Nへの変換
//=============================================================================
procedure TForm1.SpeedButton6Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_UTF-8Nを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_UTF-8N.txt';

  Screen.Cursor := crHourGlass;
  try
    if CharCodeConvert(50932, 65001,ReadMemStream, SaveMemStream) then begin
      SaveMemStream.SaveToFile(SaveFileName);
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

//=============================================================================
//  文字コード変換関数
//
//  [引数]
//  SrcCode    : 変換元の文字コードを指定
//  DstCode    : 変換後の文字コードを指定
//  SrcStream  : 変換元のテキストを格納したメモリストリーム
//  DstStream  : 変換後のテキストを格納するメモリストリーム
//
//  特に変換元の文字コードを日本語自動選択(SrcCode=50932)にした場合,結果がTrue
//  でも正常に変換されているとは限らない.現在のシステムに依存する
//
//  (1) 文字化けはないが,元の文字コードのまま
//  (2) 別の文字コードになってしまう
//  (3) 結果の文字コードは変化しているが,文字化けしている
//
//  等の場合がある
//  他の文字コード変換を経由する.また,変換元と変換後の文字コードを指定すると
//  変換がうまくいくこともある
//=============================================================================
function TForm1.CharCodeConvert(SrcCode, DstCode: Integer; SrcStream,
  DstStream: TMemoryStream): Boolean;
var
  SrcSize  : Integer;
  DstSize  : Integer;
  hr       : HRESULT;
begin
  SrcSize := SrcStream.Size;
  DstSize := SrcSize * SizeOf(Char) + 2;
  DstStream.Clear;
  DstStream.SetSize(DstSize);

  hr := ConvertINetString(nil,
                          SrcCode,
                          DstCode,
                          SrcStream.Memory,
                          @SrcSize,
                          DstStream.Memory,
                          @DstSize);
  if (hr<>S_OK) or (DstSize=0) then begin
    Result := False;
  end else begin
    Result := True;
  end;
  DstStream.SetSize(DstSize);
end;

end.




03_MultiLanguage_TLB.pas を使用した文字コードの変換


タイプライブラリの取り込みで作成した MultiLanguage_TLB.pas を使用した文字のコードページの変換サンプルです.IMLangConvertCharset インターフェイスの DoConversion 関数を使用します.

このサンプルも,コードページの組み合わせによっては,必ずしも文字のコードページに変換に成功するとは限りません.


図9
設計時画面
図10
実行中の画面
  • [Read File] ボタンをクリックして,テキストファイルをメモリストリームに読み込む
  • ボタンをクリックして文字コードを変換
図11
添付テキストファイル
  • 添付のテキストファイル
図12
実行結果例
  • EUC.txt を選択して [to_Shift-JIS] を実行した結果

リスト3
MultiLanguage_TLB.pas を使用した文字のコードページの変換
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    SpeedButton4: TSpeedButton;
    SpeedButton5: TSpeedButton;
    SpeedButton6: TSpeedButton;
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
    procedure SpeedButton4Click(Sender: TObject);
    procedure SpeedButton5Click(Sender: TObject);
    procedure SpeedButton6Click(Sender: TObject);
  private
    { Private 宣言 }
    ReadMemStream : TMemoryStream;
    SaveMemStream : TMemoryStream;
    ReadFileName : String;
    SaveFileName : String;
    function CharCodeConvert(SrcCode,DstCode: Cardinal; SrcStream,
      DstStream: TMemoryStream): Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses MultiLanguage_TLB;

{$R *.dfm}

//=============================================================================
//  フォーム表示開始時の処理
//
//  ReadStream : テキストファイルから読出した内容を保持
//  SaveStream : 文字コード変換後の内容を保持
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  ReadMemStream := TMemoryStream.Create;
  SaveMemStream := TMemoryStream.Create;

  SpeedButton1.Enabled := False;
  SpeedButton2.Enabled := False;
  SpeedButton3.Enabled := False;
  SpeedButton4.Enabled := False;
  SpeedButton5.Enabled := False;
  SpeedButton6.Enabled := False;
end;

//=============================================================================
//  フォームを閉じる時の処理
//  使用したメモリストリームを解放
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(ReadMemStream);
  FreeAndNil(SaveMemStream);
end;

//=============================================================================
//  OpenDialog1でテキストファイルを選択し
//  変換元のテキストをメモリストリーム(ReadMemStream)に読込む
//  変換元のテキストファイルは,このアプリのフォルダの下のBeforeFileフォルダ
//  保存先は同様にすぐ下のChangedFileフォルダ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenDialog1.InitialDir := ExtractFilePath(Application.ExeName) + '../BeforeFile';

  if OpenDialog1.Execute then begin
    ReadFileName := OpenDialog1.FileName;
    ReadMemStream.Clear;
    ReadMemStream.LoadFromFile(ReadFileName);

    SpeedButton1.Enabled := True;
    SpeedButton2.Enabled := True;
    SpeedButton3.Enabled := True;
    SpeedButton4.Enabled := True;
    SpeedButton5.Enabled := True;
    SpeedButton6.Enabled := True;
  end;
end;

//=============================================================================
//  ECUへの変換
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_ECUを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_ECU.txt';

  Screen.Cursor := crHourGlass;
  CharCodeConvert(50932, 51932, ReadMemStream, SaveMemStream);
  SaveMemStream.SaveToFile(SaveFileName);
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  JISへの変換
//=============================================================================
procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_JISを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_JIS.txt';

  Screen.Cursor := crHourGlass;
  CharCodeConvert(50932, 50222, ReadMemStream, SaveMemStream);
  SaveMemStream.SaveToFile(SaveFileName);
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  Shit-JISへの変換
//=============================================================================
procedure TForm1.SpeedButton3Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_S-JISを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_S-JIS.txt';

  Screen.Cursor := crHourGlass;
  CharCodeConvert(50932, 932, ReadMemStream, SaveMemStream);
  SaveMemStream.SaveToFile(SaveFileName);
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  UTF-16 Unicode(Big-Endian)への変換
//=============================================================================
procedure TForm1.SpeedButton4Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_UNI-BEを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_UNI-BE.txt';

  Screen.Cursor := crHourGlass;
  CharCodeConvert(50932, 1201, ReadMemStream, SaveMemStream);
  SaveMemStream.SaveToFile(SaveFileName);
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  UTF-16 Unicode(Little-Endian)への変換
//=============================================================================
procedure TForm1.SpeedButton5Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_UTF-LEを付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                   + ChangeFileExt(ExtractFileName(ReadFileName), '')
                   + '_to_UNI-LE.txt';

   Screen.Cursor := crHourGlass;
   CharCodeConvert(50932, 1200, ReadMemStream, SaveMemStream);
   SaveMemStream.SaveToFile(SaveFileName);
   Screen.Cursor := crDefault;
end;

//=============================================================================
//  UTF-8Nへの変換
//=============================================================================
procedure TForm1.SpeedButton6Click(Sender: TObject);
begin
  //保存ファイル名を作成
  //読出したファイル名の後ろに_to_UTF-8を付加
  SaveFileName := ExpandFileName('../ChangedFile') + PathDelim
                  + ChangeFileExt(ExtractFileName(ReadFileName), '')
                  + '_to_UTF-8N.txt';

  Screen.Cursor := crHourGlass;
  CharCodeConvert(50932, 65001, ReadMemStream, SaveMemStream);
  SaveMemStream.SaveToFile(SaveFileName);
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  文字コード変換関数
//
//  [引数]
//  SrcCode    : 変換元の文字コードを指定
//  DstCode    : 変換後の文字コードを指定
//  SrcStream  : 変換元のテキストを格納したメモリストリーム
//  DstStream  : 変換後のテキストを格納するメモリストリーム
//
//  特に変換元の文字コードを日本語自動選択(SrcCode=50932)にした場合,結果がTrue
//  でも正常に変換されているとは限らない.現在のシステムに依存する
//
//  (1) 文字化けはないが,元の文字コードのまま
//  (2) 別の文字コードになってしまう
//  (3) 結果の文字コードは変化しているが,文字化けしている
//
//  等の場合がある
//  他の文字コード変換を経由する.また,変換元と変換後の文字コードを指定する
//  あるいはInitializeのフラグを変えてみると変換がうまくいくこともある
//
//  FMLang.InitializeのdwPropertyに使用可能な値以下の値
//  const
//    MLCONVCHARF_NONE           =  0;  デフォルト
//    MLCONVCHARF_AUTODETECT     =  1;  自動検出?
//    MLCONVCHARF_NCR_ENTITIZE   =  2;  Unicodeマルチバイト専用
//    MLCONVCHARF_NAME_ENTITIZE  =  4;  Unicodeマルチバイト専用
//    MLCONVCHARF_USEDEFCHAR     =  8;  IMLangConvertCharsetでは未使用
//    MLCONVCHARF_NOBESTFITCHARS = 16;
//    MLCONVCHARF_DETECTJPN      = 32;  Shift-JISと日本語(EUC)
//=============================================================================
function TForm1.CharCodeConvert(SrcCode, DstCode: Cardinal; SrcStream,
  DstStream: TMemoryStream): Boolean;
type
  TCharArray = array of Byte;
var
  MLang      : IMLangConvertCharset;
  dwProperty : Cardinal;
  SrcSize    : Cardinal;
  DstSize    : Cardinal;
  SrcArray   : TCharArray;
  DstArray   : TCharArray;
begin
  Result := True;

  MLang := CoCMLangConvertCharset.Create;

  dwProperty := 0;
  SrcSize    := SrcStream.Size;
  DstSize    := SrcSize * SizeOf(Char) + 2;

  SetLength(SrcArray, SrcSize);
  SetLength(DstArray, DstSize);

  SrcStream.Position := 0;
  SrcStream.Read(SrcArray[0], SrcSize);
  MLang.Initialize(SrcCode, DstCode, dwProperty);
  MLang.DoConversion(SrcArray[0], SrcSize, DstArray[0], DstSize);

  DstStream.Clear;
  DstStream.Write(DstArray[0], DstSize);
  if DstSize = 0 then begin
    Result := False;
  end;

  DstStream.SetSize(DstSize);
  DstStream.Position := 0;

  MLang := nil;
end;

end.




04_MultiLanguage_TLB.pas を使用した文字コードの判別


上のサンプルと同じく,MultiLanguage_TLB.pas に実装されている IMultiLanguage2 インターフェイスの関数を使用して,文字のコードページの判定を行うサンプルです.
DetectInputCodepage という関数を使用します.このサンプルで取得する数値は,最初のサンプルで取得した文字コードページの値です.UTF-8 と UTF-8N の区別はつかないようです.


Watercolor City で提供している EncodeDetect.pas 内の CheckEncoding 関数を利用すると,かなり高い精度でコードページが取得できるようです.


図13
設計時画面
図14
添付のテキストファイル
  • ここでは Shift-JIS のテキストファイルを選択
図15
実行結果
  • Shit-JISのコードページの値である 932 を取得

リスト4
MultiLanguage_TLB.pas を使用した文字のコードページの判別
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    function DetectCodepage(SrcStream: TMemoryStream; dwFlag: Cardinal): Integer;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  MultiLanguage_TLB,
  ComObj;

{$R *.dfm}

const
  MLDETECTCP_NONE   =  0;  //デフォルト
  MLDETECTCP_7BIT   =  1;  //7bitデータ
  MLDETECTCP_8BIT   =  2;  //8bitデータ
  MLDETECTCP_DBCS   =  4;  //ダブルバイト
  MLDETECTCP_HTML   =  8;  //HTMLページ
  MLDETECTCP_NUMBER = 16;  //未実装

//=============================================================================
//  OpenDialog1でテキストファイルを選択し
//  調査対象のテキストをメモリストリーム(ReadMemStream)に読込む
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  ReadMemStream : TMemoryStream;
  iCode         : Integer;
begin
  OpenDialog1.InitialDir := ExpandFileName('../BeforeFile');

  if OpenDialog1.Execute then begin
    ReadMemStream := TMemoryStream.Create;
    try
      ReadMemStream.Clear;
      ReadMemStream.LoadFromFile(OpenDialog1.FileName);
      iCode := DetectCodepage(ReadMemStream, MLDETECTCP_NONE);
      MessageBox(Handle, PChar(IntToStr(iCode)), '文字コード', MB_ICONINFORMATION);
    finally
      FreeAndNil(ReadMemStream);
    end;
  end;
end;

//=============================================================================
//  文字コード(コードページ)検出関数
//  引数のSrcStreamに格納されている文字コードを判定する関数
//
//  このDetectCodePageでは結果を1つだけ返すようにしているが,複数の候補を結果と
//  して返すことも可能.そのためには,FMLang.DetectInputCodepage関数の該当引数
//  を配列として定義する必要がある
//=============================================================================
function TForm1.DetectCodepage(SrcStream: TMemoryStream;
  dwFlag: Cardinal): Integer;
var
  hr         : HRESULT;
  MultiLang2 : IMultiLanguage2;
  SrcArray   : array of Char;
  SrcSize    : Integer;
  Encoding   : TDetectEncodingInfo;
  scores     : Integer;
begin
  //0だとShift-JIS(システムのデフォルト)も0で返すことがある
  Result := -1;

  //MultiLanguageオブジェクトの作成
  MultiLang2 := CreateComObject(CLASS_CMultiLanguage) as IMultiLanguage2;

  //使用するインターフェースをサポートしていれば実行
  if MultiLang2 <> nil then begin
    SrcSize := SrcStream.Size;
    SetLength(SrcArray, SrcSize);
    SrcStream.Position :=0;
    SrcStream.Read(SrcArray[0], Length(SrcArray));

    //取得したいコードページの候補数
    //ここでは最初の候補のみ(つまり1つだけ)検出
    scores := 1;
    hr := MultiLang2.DetectInputCodepage(dwFlag,
                                         0,
                                         SrcArray[0],
                                         SrcSize,
                                         Encoding,

                                         scores);
    if hr = S_OK then begin
      Result := Encoding.nCodePage;
    end;
    MultiLang2 := nil;
  end;
  SrcStream.Position := 0;
end;

end.




05_Delphi の関数を使用した文字コード変換


Delphi 2009 より前の Delphi のバージョンにも文字コード変換用の関数が用意されていました.
それらの 1 つ,UTF8ToString を使用したサンプルです.
UTF-8 または UTF-8N の文字コードのテキストを Shift-JIS のテキストに変換します.


図16
設計時画面

図17
添付のテキストファイル
図18
実行結果
  • 実行結果は,UTF8Dst.txt という名前で保存される
  • これは Shift-JIS コードのテキストファイル

リスト5
UTF8ToString 関数を使用した文字コード変換
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  UTF-8(UTF-8N)の文字列を現在の文字コードに変換(Shift-JIS)
//  UTF-8(BOMなし)は変換に失敗するようである
//  ここでは1行づつ実行しているが,1行づつ実行する必然性はない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  SrcStrList   : TStringList;
  DstStrList   : TStringList;
  StrText      : String;
  i            : Integer;
  SaveFileName : String;
begin
  OpenDialog1.InitialDir := ExtractFilePath(Application.ExeName);

  if OpenDialog1.Execute then begin
    SrcStrList := TStringList.Create;
    DstStrList := TStringList.Create;
    try
      SrcStrList.LoadFromFile(OpenDialog1.FileName);
      for i := 0 to SrcStrList.Count - 1 do begin
        StrText := UTF8ToString(SrcStrList[i]);
        DstStrList.Add(StrText);
      end;
      SaveFileName := ExtractFilePath(Application.ExeName) + 'UTF-8Dst.txt';
      DstStrList.SaveToFile(SaveFileName);
    finally
      FreeAndNil(SrcStrList);
      FreeAndNil(DstStrList);
    end;
  end;
end;

end.




06_日本語を含む URL 文字列のエンコードとデコード


ファイルのテキストの文字コード変換ではありませんが,URL 文字列のエンコードとデコードの変換サンプルです.
HTTPApp.pas にもこの変換用の関数がありますが,文字コードの指定はできません.
下のサンプルは,Indy コンポーネントの TIdURI の関数を使用した変換です.
デコードには TIdURI.URLDecode 関数を,エンコードには TIdURI.ParamsEncode 関数を使用しています.これらはプロトコルの文字列が含まれる URL 文字列が対象です.TIdURI.URLEncode 関数は,プロトコルの文字列がないと変換に失敗します.
プロトコルの文字列がない場合のエンコードには,TIdURI.ParamsEncode を使用します.


図19
設計時画面
図20
デコード
  • コード内の文字列をデコードした結果
図21
エンコード
  • コード内の文字列をエンコードした結果

リスト6
TIdURI の関数を使用した,URL 文字列のエンコードとデコード
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses HTTPApp, IdURI;

{$R *.dfm}

// =============================================================================
//  TIdURI.URLDecodeを使用したURL文字列のデコード
//  usesにIdURIが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  SrcStr : String;
  DstStr : String;
begin
  SrcStr := '%E5%8D%B0%E5%88%B7%E3%80%80%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BF';
  DstStr := TIdURI.URLDecode(SrcStr);

  // 結果は'印刷 プリンタ'
  MessageBox(Handle, PChar(DstStr), '結果', MB_ICONINFORMATION);
end;

// =============================================================================
//  TIdURI.URLEncodeを使用したURL文字列のエンコード
//  TIdURI.URLEncodeはプロトコルがある文字列をエンコードする
//  プロトコルがないと変換に失敗する
//  usesにIdURIが必要
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  SrcStr : String;
  DstStr : String;
  Enc    : TEncoding;
begin
  SrcStr := 'http://www.test.com/印刷 プリンタ';
  Enc    := TEncoding.UTF8;
  DstStr := TIdURI.URLEncode(SrcStr, Enc);

  //漢字部分の結果は'%E5%8D%B0%E5%88%B7%E3%80%80%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BF'
  MessageBox(Handle, PChar(DstStr), '結果', MB_ICONINFORMATION);
end;

// =============================================================================
//  TIdURI.ParamsEncodeを使用したURL文字列のエンコード
//  usesにIdURIが必要
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  SrcStr : String;
  DstStr : String;
  Enc    : TEncoding;
begin
  SrcStr := '印刷 プリンタ';
  Enc    := TEncoding.UTF8;
  DstStr := TIdURI.ParamsEncode(SrcStr, Enc);

  //結果は'%E5%8D%B0%E5%88%B7%E3%80%80%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BF'
  MessageBox(Handle, PChar(DstStr), '結果', MB_ICONINFORMATION);
end;

end.




07_改行コード ( 改行文字 ) の扱い


文字コードの変換ではありませんが,異なる OS の間でテキストファイル等をやりとりする場合は,改行形式の違いにも注意する必要があります.また,アプリケーションによっても扱いが違う場合もあります.改行文字は,AdjustLineBreaks 関数を使用して変更することができます.


OS 改行形式
UNIX LF
Windows CR + LF
Machintosh CR