Delphi Programming / Object Pascal
[掲載 2010年02月16日] [更新 2017年04月21日] Delphi サンプルプログラム集
883_文字コードページとテキストファイルの保存と表示
動作確認等
Windows 7 U64(SP1) + Delphi XE(UP1) Pro + Indy 10.5.7
883_CodePageTextFile.zip [10,269 KB] 2017年04月21日版 (EXE 同梱)
テキストの保存と,保存したテキストを読み込んで表示するテストサンプルです.コードページの指定と自動判定関係のテストも行っています.
[備考]
文字のコードポイントの値の取得関係については以下に記事があります.
01_ 文字コード ( コードページ ) とテキストの表示
Delphi 2009 では,表示コントロールは,UTF-16 の文字コードのユニコード表示となりました.
そのため,テキストをファイルから読出して表示するには文字コードの変換が必要な場合があります.ただし,日本語環境の場合は,文字コードが (内部表現である) Shift-JIS の時は自動的に文字化けしないで表示してくれます.
また UTF-8 の時にも,BOM (バイトオーダーマーク) 付きであれば GetBufferEncoding の説明にあるように,文字化けしないで表示できます.一般に,BOM なしの場合,UTF-8N と表記します.
このサンプルは,[000.txt] という UTF-8N のテキストファイルを読み込んで 文字コードの変換,つまり,エンコードをしないで TMemo に表示すると,どうなるかを確認するテストです.
図1
設計時画面
図2
実行してボタンクリック
UTF-8N のファイルを読み込んで表示
どちらのボタンでも見事文字化けする
リスト1
UTF-8N (BOM なし) のテキストを読み出して,エンコードしないで表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// テキストファイルを読込んで表示
// 000.txtはUTF-8(BOMなし)のテキストファイル
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.LoadFromFile('000.txt' );
end ;
// =============================================================================
// テキストファイルを読込んで表示
// 000.txtはUTF-8(BOMなし)のテキストファイル
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
AStream : TMemoryStream;
begin
AStream := TMemoryStream.Create;
try
AStream.LoadFromFile('000.txt' );
Memo1.Lines.LoadFromStream(AStream);
finally
AStream.Free;
end ;
end ;
end .
02_ 文字化けを防止するにはコードページを指定する
文字化けなしで表示するには,コードページを指定して文字コードを変換します.このサンプルはそのテストコードです.
このテストでは保存もします.保存は文字コードを指定しないと Shift-JIS で保存されます.文字コードの変換には TEncoding クラスの GetEncoding メソッドが使用できます.
GetEncoding で生成したインスタンスは,Free メソッド等で解放する必要があります.
文字列や文字列配列の文字列が,正常な文字列ではない状態で,TMemo や TRichEdit に代入しても正常な文字列として表示できません.注意が必要です.
本サンプルに添付している UTF-8 のファイルは BOM なしになっています.テキストファイルの保存の際,明示的に BOM なしで保存するには TStringList (TStrings) の WirteBOM プロパティの値を False にします.
// 意識的にBOMなして保存
Memo1.Lines.WriteBOM := False;
Memo1.Lines.SaveToFile('003.txt' , TEncoding.Unicode);
[備考]
コードページによっては,表示用コントロールの Font.Charset と Font.Name の値を設定しないとテキストが正しく表示されないことがあります.
図3
設計時画面
図4
実行してボタンクリック
UTF-8N のファイルを読み込んで表示
どのボタンクリックでも文字化けしない
リスト2
UTF-8N のテキストファイルをエンコードして,TMemo に読込んで,保存する例
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
Enc : TEncoding;
begin
Enc := TEncoding.GetEncoding(CP_UTF8);
try
// ファイルの文字コードを設定して読み込み
Memo1.Lines.LoadFromFile('000.txt' , Enc);
// 保存
// 日本語環境の場合はShift-JISで保存される
Memo1.Lines.SaveToFile('001.txt' );
finally
Enc.Free;
end ;
end ;
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
SL : TStringList;
Enc : TEncoding;
begin
SL := TStringList.Create;
// ファイルの文字コードを設定して読み込み
Enc := TEncoding.GetEncoding(CP_UTF8);
try
SL.LoadFromFile('000.txt' , Enc);
// Memo1のテキストに代入
Memo1.Text := SL.Text;
// Shift-JISで保存する
SL.Clear;
SL.Text := Memo1.Text;
Enc.Free;
Enc := TEncoding.GetEncoding(932 );
SL.SaveToFile('002.txt' , Enc);
finally
SL.Free;
Enc.Free;
end ;
end ;
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
Enc : TEncoding;
begin
Enc := TEncoding.GetEncoding(CP_UTF8);
try
// ファイルの文字コードを設定して読み込み
Memo1.Lines.LoadFromFile('000.txt' , Enc);
// 保存
// Unicodeで保存される
Memo1.Lines.SaveToFile('003.txt' , TEncoding.Unicode);
finally
Enc.Free;
end ;
end ;
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
// =============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
AStream : TMemoryStream;
Enc : TEncoding;
begin
AStream := TMemoryStream.Create;
try
AStream.LoadFromFile('000.txt' );
// UTF-8 --> Unicode(UTF-16)変換
// AStreamがUTF-8の文字コードのバイナリであると解釈させる
Enc := TEncoding.GetEncoding(CP_UTF8);
Memo1.Lines.LoadFromStream(AStream, Enc);
Enc.Free;
// 保存
// Unicodeで保存される
AStream.Clear;
Enc := TEncoding.Unicode;
Memo1.Lines.SaveToStream(AStream, Enc);
AStream.SaveToFile('004.txt' );
finally
AStream.Free;
end ;
end ;
end .
03_ TRichEdit を書式なしで扱うには PlainText を True にする
テキストの表示用コントロールとして,TRichEdit があります.これに表示するサンプルです.
TRichEdit は文字の色やサイズを設定した書式付きの文字列を扱うことができますが,本ページのサンプルでは書式付きの文字列は扱っていません.プレーンテキストという書式なしのテキストだけを扱っています.
プレーンテキストだけを扱う時は,TRichEdit の PlainText プロパティの値を True にします.デフォルトの False のままでも,プレーンテキストを読み込んで表示するだけであれば場合は問題ありませんが,保存するとリッチテキスト形式で保存されてしまいます.
動作は,上のサンプルと同じです.表示用のコントロールを TRichEdit にしただけです.
図5
PlainText プロパティを True にする
リスト3
UTF-8N のテキストファイルをエンコードして TRichEdit に読込んで,保存する例
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
RichEdit1: TRichEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// フォーム生成時の処理
// TRichEditを書式なしのテキストだけを扱うため,PlainTextをTrueにする
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
// Clearしないと設計時の書式が適用される
RichEdit1.Lines.Clear;
RichEdit1.PlainText := True;
end ;
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
//
// GetEncodingを使用した場合はFreeが必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
Enc : TEncoding;
begin
Enc := TEncoding.GetEncoding(CP_UTF8);
try
// ファイルの文字コードを設定して読み込み
RichEdit1.Lines.LoadFromFile('000.txt' , Enc);
// 保存
// 日本語環境の場合はShift-JISで保存される
RichEdit1.Lines.SaveToFile('001.txt' );
finally
Enc.Free;
end ;
end ;
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
//
// GetEncodingを使用した場合はFreeが必要
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
SL : TStringList;
Enc : TEncoding;
begin
SL := TStringList.Create;
// ファイルの文字コードを設定して読み込み
Enc := TEncoding.GetEncoding(CP_UTF8);
try
SL.LoadFromFile('000.txt' , Enc);
// RichEdit1のテキストに代入
RichEdit1.Text := SL.Text;
// Shift-JISで保存する
SL.Clear;
SL.Text := RichEdit1.Text;
Enc.Free;
Enc := TEncoding.GetEncoding(932 );
SL.SaveToFile('002.txt' , Enc);
finally
SL.Free;
Enc.Free;
end ;
end ;
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
//
// GetEncodingを使用した場合はFreeが必要
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
Enc : TEncoding;
begin
Enc := TEncoding.GetEncoding(CP_UTF8);
try
// ファイルの文字コードを設定して読み込み
RichEdit1.Lines.LoadFromFile('000.txt' , Enc);
// 保存
// Unicodeで保存される
RichEdit1.Lines.SaveToFile('003.txt' , TEncoding.Unicode);
finally
Enc.Free;
end ;
end ;
// =============================================================================
// テキストファイルを読込んで表示,その後保存
// 000.txtはUTF-8(BOMなし)のテキストファイル
//
// GetEncodingを使用した場合はFreeが必要
// =============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
AStream : TMemoryStream;
Enc : TEncoding;
begin
AStream := TMemoryStream.Create;
try
AStream.LoadFromFile('000.txt' );
// UTF-8 --> Unicode(UTF-16)変換
// AStreamがUTF-8の文字コードのバイナリであると解釈させる
Enc := TEncoding.GetEncoding(CP_UTF8);
RichEdit1.Lines.LoadFromStream(AStream, Enc);
// 保存
// Unicodeで保存される
AStream.Clear;
Enc.Free;
Enc := TEncoding.Unicode;
RichEdit1.Lines.SaveToStream(AStream, Enc);
AStream.SaveToFile('004.txt' );
finally
AStream.Free;
end ;
end ;
end .
04_ ユニコード ( Unicode ) の恩恵 - 中国簡体字を表示
ユニコード化の意義はいろいろありますが,その 1 つは多くの漢字が扱えるようになったことです.よく引き合いに出されるのが,人名用漢字の例としての明治の文豪「森鴎外」です.正式な字体は図の右の文字です.このような漢字も外字登録することなく扱えるようになりました.
古文書や古代文字の研究家にとっても,また人名を扱うにもまだまだ不十分ですが,Unicode の世界は広がっていき,当たり前の時代がくることでしょう.将来的には世界中の文字が普通に扱える時代がくるかも知れません.すでにそのような機能を持つ「超漢字」というソフトも存在しています.
図6
扱い可能になった漢字の例
いろいろな国の文字が表示可能な例として,中国簡体字を表示してみます.プロジェクトには中国簡体字の 2 つのテキストファイルを添付しています.
たとえ文字のコードページが正しく選択されても,文字化けや表示できない文字があることもあります.日本語フォントの場合,Shift-JIS の文字範囲はほとんどのフォントセットで表示可能ですが,全てのフォント がユニコードの文字をサポートしているとは限りせん.フォントの Charset やフォント名も考慮する必要なことがあります.
このテストの文章は,http://zh.wikipedia.org/wiki/Delphi の一部を利用しました.
図7
設計時画面
図8
テスト用のテキストファイル
中国簡体字 _BG2312.txt のコードは 936
中国簡体字 _Unicode.txt は Unicode
図9
中国簡体字 _Unicode.txt を表示
Unicode なので文字化けしない
これは文字化けではなく,中国の簡体字であって,日本の漢字ではない
図10
中国簡体字 _GB2312.txt を表示
図11
エンコードすれば文字化けしない
これは文字化けではない.中国の簡体字.日本の漢字ではない
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, ComCtrls;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
OpenDialog1: TOpenDialog;
RichEdit1: TRichEdit;
procedure FormShow(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
GetStream : TMemoryStream;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses RichEdit;
{$R *.dfm}
// =============================================================================
// GetStreamは読込んだテキストを格納するメモリストリーム
// TParaFormat2の使用にはusesにRichEditが必要
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
ParaFormat2 : TParaFormat2;
begin
GetStream := TMemoryStream.Create;
// 漢字を表示すると行間が詰まってしまうので,標準の行間にする
// TMemoでは調整不可なのでTRichEditを使用
ParaFormat2.cbSize := SizeOf(ParaFormat2);
ParaFormat2.dwMask := PFM_LINESPACING;
ParaFormat2.bLineSpacingRule := 0 ;
SendMessage(RichEdit1.Handle, EM_SETPARAFORMAT, 0 , Longint(@ParaFormat2));
end ;
// =============================================================================
// GetStreamを解放
// =============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
GetStream.Free;
end ;
// =============================================================================
// テキストファイルを読込んで表示
//
// テストプロジェクトに以下のテキストファイルを用意してある
// 中国簡体字_GB2312.txt GB2312(コードページは936)の簡体字のテキスト
// 中国簡体字_Unicode.txt ユニコードの簡体字のテキスト
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
OpenDialog1.Filter := 'テキスト|*.txt' ;
OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0 ));
if OpenDialog1.Execute then begin
Screen.Cursor := crHourGlass;
RichEdit1.Clear;
RichEdit1.Update;
RichEdit1.Lines.BeginUpdate;
RichEdit1.Font.Charset := DEFAULT_CHARSET;
GetStream.Clear;
try
// テキストをGetMemoryに読込む
GetStream.LoadFromFile(OpenDialog1.FileName);
// それを表示
GetStream.Position := 0 ;
RichEdit1.Lines.LoadFromStream(GetStream);
except
RichEdit1.Lines.Add('読出しに失敗しました.' );
end ;
RichEdit1.Lines.EndUpdate;
Screen.Cursor := crDefault;
end ;
end ;
// =============================================================================
// エンコードして表示
//
// 中国の文字コードページ
// 936 gb2312 簡体字中国語 (GB2312)
// 950 big5 繁体字中国語 (Big5)
// 10008 x-mac-chinesesimp 簡体字中国語 (Mac)
// 20000 x-Chinese-CNS 繁体字中国語 (CNS)
// 51936 EUC-CN 簡体字中国語 (EUC)
// 52936 hz-gb-2312 簡体字中国語 (HZ)
// 54936 GB18030 簡体字中国語 (GB18030)
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
Enc : TEncoding;
begin
if GetStream.Size = 0 then Exit;
Screen.Cursor := crHourGlass;
RichEdit1.Clear;
RichEdit1.Update;
RichEdit1.Lines.BeginUpdate;
// 中国語の場合CHINESEBIG5_CHARSETかGB2312_CHARSETにした方が字がきれいになる
RichEdit1.Font.Charset := CHINESEBIG5_CHARSET;
try
// 936(簡体字中国語-GB2312)からエンコード(Unicodeに)して表示
Enc := TEncoding.GetEncoding(936 );
GetStream.Position := 0 ;
RichEdit1.Lines.LoadFromStream(GetStream, Enc);
Enc.Free;
except
on e: Exception do
begin
RichEdit1.Lines.Add('失敗しました.' + e.Message);
end ;
end ;
RichEdit1.Lines.EndUpdate;
Screen.Cursor := crDefault;
end ;
end .
05_ TIdHTTP でウェブページの HTML コードを取得して表示
いろいろな文字コードをテストすると言っても,中々手に入るものではありません.そこで掲示板等でよく質問がある,ネット上のページの HTML コードを取得して表示してみることにします.
本サンプルでは,コンボボックスで選択した URL の HTML ソースコードを取得して TMemo に表示します.文字化けした場合は,文字コードの選択コンボボックスから文字コードを選択して再表示します.選択する文字コードは,HTMLソースの Charset の記述が参考となりますが,必ずしもそれで文字化けが解消されるとは限りません.
HTML ではないテキストファイルの表示もそうですが,この手の文字化けは Delphi が Unicode 化されたから,Indy のコンポーネントを使用しているから発生するわけではありません.
このテストプログラムを Delphi 2009 よりも前のバージョンでコンパイルして実行してみればそのことを確認できます.
図12
設計時画面
図13
HTML コードを表示
図14
コードページ変更
下図は,URL が http://www.delphi.gr/ の場合です.
HTML コードの Content-Style-Type の Charset が windows-1253 となっています.筆者の環境には,この Charset はインストールされていません.そこで,ギリシャ語のどれかを選択して再表示してみましたが,文字化けしてしまいます.
UTF-8 にすると文字化けしなくなりました.
図15
charset は windows1253
図16
文字化けしている
図17
UTF-8 ならOK
リスト5
コンボボックスで選択したサイトの HTML コードを取得して表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdHTTP, Buttons;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
ComboBox1: TComboBox;
IdHTTP1: TIdHTTP;
SpeedButton1: TSpeedButton;
ComboBox2: TComboBox;
Label1: TLabel;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ComboBox2Click(Sender: TObject);
private
{ Private 宣言 }
GetStream : TMemoryStream;
CodePage : Integer;
procedure GetCodeAndDistributeList(slCode, slCharsetName,
slDistribute : TStringList);
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
ShellAPI,
Registry,
MultiLanguage_TLB,
ComObj;
{$R *.dfm}
// =============================================================================
// メモリストリームを破棄
// =============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
GetStream.Free;
end ;
// =============================================================================
// フォーム表示開始時の処理
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
CodeList : TStringList;
CharsetName : TStringList;
DistributeList : TStringList;
i : Integer;
StrText : String ;
begin
// HTMLソースを格納するメモリストリーム
GetStream := TMemoryStream.Create;
CodePage := -1 ;
// テスト用のURLをセット
ComboBox1.Items.Clear;
ComboBox1.Items.Add('http:// mrxray.on.coocan.jp/index.htm' );
ComboBox1.Items.Add('http:// www.videonews.com/' );
ComboBox1.Items.Add('http:// www.yomiuri.co.jp/' );
ComboBox1.Items.Add('http:// docwiki.embarcadero.com/RADStudio/ja/Main_Page' );
ComboBox1.Items.Add('http:// www.upload.co.il/stage/studio/category.asp?id=8' );
ComboBox1.Items.Add('http:// www.delphi.gr/' );
ComboBox1.Items.Add('http:// ru.wikipedia.org/wiki/Delphi' );
ComboBox1.Items.Add('http:// zh.wikipedia.org/wiki/Delphi' );
ComboBox1.Items.Add('http:// ko.wikipedia.org/wiki/%EC%9D%BC%EB%B3%B8' );
ComboBox2.Style := csDropDown;
ComboBox2.Clear;
ComboBox2.Items.BeginUpdate;
// 文字エンコーディングに使用するコードページのリスト
// CodePageが-1の時は文字コード変換しないようにするためにセット
ComboBox2.Items.AddObject('指定なし' , TObject(-1 ));
// 日本の場合のUnicodeのデフォルト値
ComboBox2.Items.AddObject('shift_jis 日本語 (シフト JIS)' , TObject(0 ));
CodeList := TStringList.Create;
CharsetName := TStringList.Create;
DistributeList := TStringList.Create;
try
GetCodeAndDistributeList(CodeList, CharsetName, DistributeList);
for i := 1 to CodeList.Count - 1 do begin
StrText := Format('%-16s ' , [CharsetName[i]]) + DistributeList[i];
ComboBox2.Items.AddObject(StrText, TObject(StrToInt(CodeList[i])));
Application.ProcessMessages;
end ;
finally
FreeAndNil(CodeList);
FreeAndNil(CharsetName);
FreeAndNil(DistributeList);
end ;
// エンコーディングの初期値は[指定しない]
ComboBox2.ItemIndex := 0 ;
ComboBox2.Items.EndUpdate;
end ;
// =============================================================================
// 文字コードと文字コード名のリストを作成
// slCodeList : 文字コードの格納リスト
// slCharsetName : 文字コードのセット名のリスト
// slDistribute : 文字コード名の格納リスト
//
// usesに Registry,MultiLanguage_TLB,ComObjが必要
// =============================================================================
procedure TForm1.GetCodeAndDistributeList(slCode, slCharsetName,
slDistribute: TStringList);
var
Registry : TRegistry;
RegKey : String ;
slGCodePage : TStringList;
slCharset : String ;
MLang : IMultiLanguage;
MIMECPInfo : TMIMECPINFO;
i : Integer;
strDescription : String ;
begin
slGCodePage := TStringList.Create;
slCode.Clear;
slCharsetName.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);
MLang.GetCodePageInfo(StrToInt(slGCodePage[i]), MIMECPInfo);
strDescription := MIMECPInfo.wszDescription;
slCharset := Registry.ReadString('BodyCharset' );
Registry.CloseKey;
slCode.Add(slGCodePage[i]);
slCharsetName.Add(slCharset);
slDistribute.Add(strDescription);
Application.ProcessMessages;
end ;
finally
Registry.Free;
FreeAndNil(slGCodePage);
MLang := nil ;
end ;
end ;
// =============================================================================
// [HTML GET]ボタンクリック
// コードページ指定なしでComboBox1で表示中のURLのHTMLソースを取得して表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AURL : String ;
begin
Screen.Cursor := crHourGlass;
ComboBox2.ItemIndex := 0 ;
GetStream.Clear;
// コンボボックスで選択中のURL文字列
AURL := Trim(ComboBox1.Text);
try
IdHTTP1.HandleRedirects := True;
// URLのソースをGetStreamに取得
IdHTTP1.Get(AURL, GetStream);
// それを表示
GetStream.Position := 0 ;
Memo1.Lines.LoadFromStream(GetStream);
except
on e: EIdHTTPProtocolException do
begin
Memo1.Lines.Add('取得に失敗しました.' + e.Message);
end ;
on e: Exception do
begin
Memo1.Lines.Add('失敗しました.' + e.Message);
end ;
end ;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
// =============================================================================
// どんなサイトか確認するためにコンボボックスで選択したサイトを表示
// ShellExecuteの使用にはusesにShellAPIが必要
// =============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
AURL : string ;
begin
Screen.Cursor := crHourGlass;
AURL := Trim(ComboBox1.Text);
ShellExecute(Handle,'' ,PChar(AURL), '' , nil , SW_SHOW);
Screen.Cursor := crDefault;
end ;
// =============================================================================
// ComboBox2のItemをクリックしたらエンコーディングしてHTMLソースを再表示
//
// エンコードが正しく行われても,必ずしもすべての文字が正常に表示されるとは限
// らない.該当する国の文字がそのフォントには含まれていない場合がある.含まれ
// ていても体裁が良くない場合もある.
// したがって,フォントのCharsetやNameの変更が必要なことがある.
// =============================================================================
procedure TForm1.ComboBox2Click(Sender: TObject);
var
Enc : TEncoding;
begin
if GetStream.Size = 0 then exit;
Screen.Cursor := crHourGlass;
Memo1.Clear;
Memo1.Update;
Memo1.Lines.BeginUpdate;
// 選択Itemからコードページの数値を取り出す
CodePage := Integer(ComboBox2.Items.Objects[ComboBox2.ItemIndex]);
try
GetStream.Position := 0 ;
if CodePage < 0 then begin
Memo1.Lines.LoadFromStream(GetStream);
end else begin
// エンコードして表示
if (CodePage = 0 ) or (CodePage = 1200 ) then begin
Memo1.Lines.LoadFromStream(GetStream);
end else begin
if CodePage = 51932 then CodePage := 20932 ;
Enc := TEncoding.GetEncoding(CodePage);
Memo1.Lines.LoadFromStream(GetStream, Enc);
Enc.Free;
end ;
end ;
except
on e: Exception do
begin
Memo1.Lines.Add('失敗しました.' + e.Message);
end ;
end ;
Memo1.Lines.EndUpdate;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
end .
06_ 文字コード ( コードページ ) の自動変換表示
MLang の機能を使用して,文字コードの自動変換をテストするサンプルです.
タイプライブラリの取り込みで作成した MultiLanguage_TLB.pas を使用します.文字コードの変換には,IMultiLanguage2 インターフェイスの DetectInputCodepage メソッドを使用しています.
上のテストコード同様,ギリシャ語のサイトでコードページの値がうまく取得できす,文字化けしています.IE や FireFox では文字化けするが,このサンプルプログラムでは文字化けしないサイトもあります.日本語のサイトでは今のところ問題なく変換できているようです.
IE 等のブラウザでは,この windows-XXXX や他のタグ情報も利用してコードページの判定を行っているのかも知れません.
ブラウザはともかく,テキスト文書の文字コードを自動判定して文字化けしないように表示し,適切なフォントを選択するのは,現段階では完全に行うのは難しそうです.言語や文字コード (コードページ) が既知であれば,その設定で表示するのが一番いいような気がします.
対象となる文書内の文字の種類,例えば英数字だけとか,または短い文書だと判定に失敗することがあるようです.
[備考 2]
下図の実行結果で,HTTPS への接続のため失敗している例があります.近年,サイトの HTTPS 化が進んでいます.そのサイトも,本サンプル掲載当時は HTTP のサイトでした.
図18
設計時画面
図19
自動変換成功
図20
自動変換に失敗したウェブページ
このページは https サーバにリダイレクトされている
したがって,正確には,自動変換に失敗したのではなく,元のファイルのダウンロードに失敗していることになる
リスト6
コンボボックスで選択したサイトの HTML コードの文字コードを自動変換して表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdHTTP;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
ComboBox1: TComboBox;
IdHTTP1: TIdHTTP;
SpeedButton1: TSpeedButton;
Memo1: TMemo;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
private
{ Private 宣言 }
function DetectCodepage(SrcStream: TMemoryStream; dwFlag: Cardinal): Integer;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
ShellAPI, MultiLanguage_TLB, ActiveX, ComObj;
const
MLDETECTCP_NONE = 0 ; // デフォルト
MLDETECTCP_7BIT = 1 ; // 7bitデータ
MLDETECTCP_8BIT = 2 ; // 8bitデータ
MLDETECTCP_DBCS = 4 ; // ダブルバイト
MLDETECTCP_HTML = 8 ; // HTMLページ
MLDETECTCP_NUMBER = 16 ; // 未実装
{$R *.dfm}
// =============================================================================
// フォーム表示開始時の処理
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
// テスト用のURLをセット
with ComboBox1.Items do begin
Clear;
Add('http://mrxray.on.coocan.jp/index.htm' );
Add('http://www.videonews.com/' );
Add('http://www.yomiuri.co.jp/' );
Add('http://docwiki.embarcadero.com/RADStudio/ja/Main_Page' );
Add('http://www.upload.co.il/stage/studio/category.asp?id=8' );
Add('http://www.delphi.gr/' );
Add('http://ru.wikipedia.org/wiki/Delphi' );
Add('http://zh.wikipedia.org/wiki/Delphi' );
Add('http://ko.wikipedia.org/wiki/%EC%9D%BC%EB%B3%B8' );
end ;
end ;
// =============================================================================
// [HTML GET]ボタンクリック
// コードページを自動検出してComboBox1で表示中のURLのHTMLソースを表示
//
// TEncodintではUnicode(同じページコードへのエンコードは失敗するようだ
// EUC-JP(コードページ51932)を検出した場合,
// 20932(マイクロソフト版日本語EUCらしい)を使用しないと文字化けする
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AURL : String ;
GetStream : TMemoryStream;
CodePage : Integer;
Enc : TEncoding;
begin
Screen.Cursor := crHourGlass;
// HTMLソースを格納するメモリストリーム
GetStream := TMemoryStream.Create;
GetStream.Clear;
Memo1.Clear;
Memo1.Update;
Label1.Caption := '' ;
// コンボボックスで選択中のURL文字列
AURL := Trim(ComboBox1.Text);
try
try
IdHTTP1.HandleRedirects := True;
// URLのソースをGetStreamに取得
IdHTTP1.Get(AURL, GetStream);
// コードページを取得して表示
// 引数のMLDETECTCP_HTMLは対象をHTMLとするもの
// 一般のテキストファイル等は他の値がいいかも知れない
GetStream.Position := 0 ;
CodePage := DetectCodepage(GetStream, MLDETECTCP_HTML);
Label1.Caption := IntToStr(CodePage);
if (CodePage = 0 ) or (CodePage = 1200 ) then begin
Memo1.Lines.LoadFromStream(GetStream);
end else begin
if CodePage = 51932 then CodePage := 20932 ;
Enc := TEncoding.GetEncoding(CodePage);
GetStream.Position := 0 ;
Memo1.Lines.LoadFromStream(GetStream, Enc);
Enc.Free;
end ;
except
on e: EIdHTTPProtocolException do
begin
Memo1.Lines.Add('取得に失敗しました.' + e.Message);
end ;
on e: Exception do
begin
Memo1.Lines.Add('失敗しました.' + e.Message);
end ;
end ;
finally
GetStream.Free;
end ;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
// =============================================================================
// どんなサイトか確認するためにコンボボックスで選択したサイトを表示
// ShellExecuteの使用にはusesにShellAPIが必要
// =============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
AURL : string ;
begin
Screen.Cursor := crHourGlass;
AURL := Trim(ComboBox1.Text);
ShellExecute(Handle,'' ,PChar(AURL), '' , nil , SW_SHOW);
Screen.Cursor := crDefault;
end ;
// =============================================================================
// 引数のSrcStreamに格納されている文字コードを判定する関数
// サンプルプログラム集[886]で解説しているMLang.dllの機能を使用
// 戻り値がコードページの数値
//
// SYSINT(実際にはInteger)の使用にはusesにActiveXが必要
// =============================================================================
function TForm1.DetectCodepage(SrcStream: TMemoryStream;
dwFlag: Cardinal): Integer;
var
hr : HRESULT;
MultiLang2 : IMultiLanguage2;
SrcArray : array of Char;
SrcSize : SYSINT;
Encoding : TDetectEncodingInfo;
scores : SYSINT;
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 .
07_ EncodeDetect, UCS4Encoding ユニットを使用した自動変換
Watercolor City でコードページの変換用のライブラリを提供しています.
これを使用したサンプルです.以下からファイルをダウンロードして,解凍したフォルダ内に含まれている EncodeDetect, UCS4Encoding の 2 つのユニットを利用します
以下がテストに使用したコードです.コードそのものは Watercolor City のサンプルとほとんど同じです.実行結果は MLang を使用した上のサンプルと同じです.
ただし,この結果は,今回テストに使用したウェブページの範囲内のことです.
リスト7
EncodeDetect, UCS4Encoding ユニットを使用したコードページの自動変換
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdHTTP;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
ComboBox1: TComboBox;
IdHTTP1: TIdHTTP;
SpeedButton1: TSpeedButton;
Memo1: TMemo;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
ShellAPI, EncodeDetect, UCS4Encoding;
{$R *.dfm}
// =============================================================================
// フォーム表示開始時の処理
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
// テスト用のURLをセット
with ComboBox1.Items do begin
Clear;
Add('http://mrxray.on.coocan.jp/index.htm' );
Add('http://www.videonews.com/' );
Add('http://www.yomiuri.co.jp/' );
Add('http://docwiki.embarcadero.com/RADStudio/ja/Main_Page' );
Add('http://www.upload.co.il/stage/studio/category.asp?id=8' );
Add('http://www.delphi.gr/' );
Add('http://ru.wikipedia.org/wiki/Delphi' );
Add('http://zh.wikipedia.org/wiki/Delphi' );
Add('http://ko.wikipedia.org/wiki/%EC%9D%BC%EB%B3%B8' );
end ;
end ;
// =============================================================================
// [HTML GET]ボタンクリック
// コードページを自動検出してComboBox1で表示中のURLのHTMLソースを表示
// EncodeDetect, UCS4Encoding ユニットを使用する例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
// ---------------------------------------------------------------------------
// Watercolor City で提供しているサンプルコードのコピペ
// ---------------------------------------------------------------------------
function GetEncodingInstance(ENC: Integer): TEncoding;
begin
// 判定結果に応じたTEncodingインスタンスの生成
case ENC of
ENC_SJIS: Result := TMBCSEncoding.Create(932 );
ENC_EUC: Result := TMBCSEncoding.Create(20932 );
ENC_JIS: Result := TMBCSEncoding.Create(50220 );
ENC_UTF16LE: Result := TUnicodeEncoding.Create;
ENC_UTF16BE: Result := TBigEndianUnicodeEncoding.Create;
ENC_UTF8: Result := TMBCSEncoding.Create(65001 );
ENC_UTF8BOM: Result := TUTF8Encoding.Create;
ENC_UTF32LE: Result := TUCS4Encoding.Create; // 要UCS4Encodingユニット
ENC_UTF32BE: Result := TUCS4Encoding.Create(True); // 要UCS4Encodingユニット
else Result := TMBCSEncoding.Create(0 );
end ;
end ;
// ---------------------------------------------------------------------------
var
AURL : String ;
GetStream : TMemoryStream;
CodePage : Integer;
Enc : TEncoding;
begin
Screen.Cursor := crHourGlass;
// HTMLソースを格納するメモリストリーム
GetStream := TMemoryStream.Create;
GetStream.Clear;
Memo1.Clear;
Memo1.Update;
Label1.Caption := '' ;
// コンボボックスで選択中のURL文字列
AURL := Trim(ComboBox1.Text);
try
try
IdHTTP1.HandleRedirects := True;
// URLのソースをGetStreamに取得
IdHTTP1.Get(AURL, GetStream);
// コードページを取得して表示
CodePage := EncodeDetect.CheckEncoding(GetStream);
Enc := GetEncodingInstance(CodePage);
GetStream.Position := 0 ;
Memo1.Lines.LoadFromStream(GetStream, Enc);
Enc.Free;
except
on e: EIdHTTPProtocolException do
begin
Memo1.Lines.Add('取得に失敗しました.' + e.Message);
end ;
on e: Exception do
begin
Memo1.Lines.Add('失敗しました.' + e.Message);
end ;
end ;
finally
GetStream.Free;
end ;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
// =============================================================================
// どんなサイトか確認するためにコンボボックスで選択したサイトを表示
// ShellExecuteの使用にはusesにShellAPIが必要
// =============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
AURL : string ;
begin
Screen.Cursor := crHourGlass;
AURL := Trim(ComboBox1.Text);
ShellExecute(Handle,'' ,PChar(AURL), '' , nil , SW_SHOW);
Screen.Cursor := crDefault;
end ;
end .
08_ TIdHTTP の Get メソッドの文字コードページ自動判定機能
TIdHTTP コンポーネントの Get メソッドには多くのオーバライドな関数があります.
その中に,引数が URL だけで,戻り値が String 型の関数があります.この関数を使用すると,ウェブページの文字コードページを自動判定してくれます.ただし,全ての HTML コードに対して有効なわけではないようです.
下図は,http://www.upload.co.il/stage/studio/category.asp?id=8 を表示した結果です.このページも,UTF-8 を指定して表示すれば正常に表示できます.
図21
変換に失敗した例
UTF-8 にすれば正常表示可能
これはヘブライ語のページ
リスト8
TIdHTTP の Get メソッドを使用した文字コードページの自動変換
copy code
// =============================================================================
// [HTML GET]ボタンクリック
//
// TIdTHHPコンポのGet関数は,引数がURLだけの場合,文字コードを自動判定する
// 全てのHTMLコードの文字コードページを判定できるわけではない
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AURL : String ;
begin
Screen.Cursor := crHourGlass;
Memo1.Clear;
Memo1.Update;
Label1.Caption := '' ;
// コンボボックスで選択中のURL文字列
AURL := Trim(ComboBox1.Text);
try
IdHTTP1.HandleRedirects := True;
// URLのソースを取得
Memo1.Lines.Text := IdHTTP1.Get(AURL);
Label1.Caption := IdHTTP1.Response.CharSet;
except
on e: EIdHTTPProtocolException do begin
Memo1.Lines.Add('取得に失敗しました.' + e.Message);
end ;
on e: Exception do begin
Memo1.Lines.Add('失敗しました.' + e.Message);
end ;
end ;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
09_ universalchardet を使用したコードページの自動判別
universalchardet (Universal Character Set Detector C Library) を使用したコードページの自動判別のテストです.universalchardet については A7M さんのサイトの記事を参考にしてください.
A7M さんが提供している DLL はランタイムライブラリが必要になっているので,ライタイムライブラリが必要ない DLL を作成して添付しています.DLL の作成は,A7M さんが提供している DLL のプロジェクトを C++Builder 10.2 Tokyo Stater でコンパイルしました.
ここまでのサンプルと同じく,指定ウェブページの HTML コードを取得して表示します.MLang を使用した時に取得できなかったギリシャ語のページも,UTF-8 として検出され,文字化けなしに HTML コードが取得できます.
HTTPS のウェブページの HTML コードも取得できるように,Indy の TIdSSLIOHandlerSocketOpenSSL コンポーネントを使用してみました.
A7M さんが提供している universalchardet.pas を利用しています.このユニットには,DLL 内の関数類を使用するための定義があります.
[備考]
本サンプルのテストで使用した Indy のバージョンは 10.6.2 です.
図22
設計時の画面
TIdSSLIOHandlerSocketOpenSSL コンポーネントを配置
図23
実行結果の例
リスト9
universalchardet を使用したコードページの自動判別
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdHTTP, IdIOHandler, IdIOHandlerSocket,
IdIOHandlerStack, IdSSL, IdSSLOpenSSL;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
ComboBox1: TComboBox;
IdHTTP1: TIdHTTP;
SpeedButton1: TSpeedButton;
Memo1: TMemo;
Label1: TLabel;
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
ShellAPI,
universalchardet;
{$R *.dfm}
// =============================================================================
// フォーム表示開始時の処理
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
// テスト用のURLをセット
with ComboBox1.Items do begin
Clear;
Add('http://mrxray.on.coocan.jp/index.htm' );
Add('http://www.videonews.com/' );
Add('http://www.yomiuri.co.jp/' );
Add('http://docwiki.embarcadero.com/RADStudio/ja/Main_Page' );
Add('http://www.upload.co.il/stage/studio/category.asp?id=8' );
Add('http://www.delphi.gr/' );
Add('http://ru.wikipedia.org/wiki/Delphi' );
Add('http://zh.wikipedia.org/wiki/Delphi' );
Add('http://ko.wikipedia.org/wiki/%EC%9D%BC%EB%B3%B8' );
end ;
end ;
// =============================================================================
// universalchardetを使用したコードページの自動判別
// コードページを自動検出してComboBox1のURLのHTMLソースを表示
// 処理はA7M氏が提供しているコードと同じ
//
// EUC-JP(コードページ51932)を検出した場合,
// 20932(マイクロソフト版日本語EUCらしい)を使用しないと文字化けする
// この処理はしていない.他のサンプルを参照
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LPageURL : string ;
LGetStream : TMemoryStream;
Ldetect : chardet_t;
LEncName : array [0 ..CHARDET_MAX_ENCODING_NAME] of AnsiChar;
LEnc : TEncoding;
begin
Screen.Cursor := crHourGlass;
// HTMLソースを格納するメモリストリーム
LGetStream := TMemoryStream.Create;
LGetStream.Clear;
Memo1.Clear;
Memo1.Update;
Label1.Caption := '' ;
// コンボボックスで選択中のURL文字列
LPageURL := Trim(ComboBox1.Text);
try
try
IdHTTP1.HandleRedirects := True;
// URLのソースをメモリストリームに取得
IdHTTP1.Get(LPageURL, LGetStream);
// universalchardet.dllでエンコーディングの判別
Ldetect := nil ;
chardet_create(Ldetect);
chardet_handle_data(Ldetect, LGetStream.Memory, LGetStream.Size);
chardet_data_end(Ldetect);
// エンコーディング名の取得
chardet_get_charset(Ldetect, LEncName, CHARDET_MAX_ENCODING_NAME);
chardet_destroy(Ldetect);
Label1.Caption := string (LEncName);
// エンコーディング名からTEncodigを取得
try
LEnc := TEncoding.GetEncoding(string (LEncName));
except
on EEncodingError do begin
LEnc := TEncoding.Default ;
end ;
end ;
// オフセットをストリームの先頭に
LGetStream.Position := 0 ;
Memo1.Lines.LoadFromStream(LGetStream, LEnc);
except
on e: EIdHTTPProtocolException do begin
Memo1.Lines.Add('取得に失敗しました.' + e.Message);
end ;
on e: Exception do begin
Memo1.Lines.Add('失敗しました.' + e.Message);
end ;
end ;
finally
LGetStream.Free;
end ;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
// =============================================================================
// どんなサイトか確認するためにコンボボックスで選択したサイトを表示
// ShellExecuteの使用にはusesにShellAPIが必要
// =============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
LURL : string ;
begin
Screen.Cursor := crHourGlass;
LURL := Trim(ComboBox1.Text);
ShellExecute(Handle,'' ,PChar(LURL), '' , nil , SW_SHOW);
Screen.Cursor := crDefault;
end ;
end .
テキストの文字コードの扱い例として,ウェブページの HTML ソースコードの取得をテストしました.この時,URL に日本語が含まれる場合,URL の文字列をエンコードしないと,別の URL と解釈されてしまいます.
本テストは,このことを確認するためのサンプルです.
ウェブブラウザで表示する場合は,ブラウザが URL の文字列をエンコードしてくれます.ブラウザによっては,エンコードしたアドレスを表示するものもあります.
図24
設計時画面
図25
URL をエンコードしない場合
タイトルが [Wikipedia] のページとなっている
これは目的の記事のページではない
図26
URL をエンコードした場合
タイトルが [日本語] のページになっている
これが目的の記事のページ
リスト10
日本語を含む URL の扱い.エンコードが必要
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdHTTP;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
ComboBox1: TComboBox;
IdHTTP1: TIdHTTP;
Memo1: TMemo;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
IdURI;
{$R *.dfm}
// =============================================================================
// フォーム表示開始時の処理
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
// テスト用のURLをセット
with ComboBox1.Items do begin
Clear;
Add('http://ja.wikipedia.org/wiki/日本語' );
end ;
ComboBox1.ItemIndex := 0 ;
end ;
// =============================================================================
// TIdHTTPでウエブページのHTMLコードをダウンロード
// URLの文字列をエンコードしない場合
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AURL : String ;
GetStream : TMemoryStream;
begin
Screen.Cursor := crHourGlass;
// HTMLソースを格納するメモリストリーム
GetStream := TMemoryStream.Create;
GetStream.Clear;
Memo1.Clear;
Memo1.Update;
// コンボボックスで選択中のURL文字列
AURL := Trim(ComboBox1.Text);
try
try
IdHTTP1.HandleRedirects := True;
IdHTTP1.Get(AURL, GetStream);
GetStream.Position := 0 ;
Memo1.Lines.LoadFromStream(GetStream, TEncoding.UTF8);
// 確認保存
GetStream.SaveToFile('test.html' );
except
Memo1.Lines.Add('失敗しました.' );
end ;
finally
GetStream.Free;
end ;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
// =============================================================================
// TIdHTTPでウエブページのHTMLコードをダウンロード
// URLの文字列をエンコードする場合
// usesにIdURIが必要
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
AURL : String ;
GetStream : TMemoryStream;
begin
Screen.Cursor := crHourGlass;
// HTMLソースを格納するメモリストリーム
GetStream := TMemoryStream.Create;
GetStream.Clear;
Memo1.Clear;
Memo1.Update;
// コンボボックスで選択中のURL文字列
AURL := Trim(ComboBox1.Text);
try
try
// URLの文字列をエンコード
AURL := TIdURI.URLEncode(AURL);
IdHTTP1.HandleRedirects := True;
IdHTTP1.Get(AURL, GetStream);
GetStream.Position := 0 ;
Memo1.Lines.LoadFromStream(GetStream, TEncoding.UTF8);
// 確認保存
GetStream.SaveToFile('test_URLEncode.html' );
except
Memo1.Lines.Add('失敗しました.' );
end ;
finally
GetStream.Free;
end ;
Memo1.SetFocus;
Screen.Cursor := crDefault;
end ;
end .
11_ TFileStream, TextFile ( Readln ) を使用する方法
UTF-8N のテキストファイルを読込んで表示するテストです.
TFileStream と TextFile を使用した例です.比較用に TStringList を使用したコードと,エンコードしない場合も追加しています.
テキストを行単位で読み込むには,TStreamReader を使用すると便利です.TStreamReader を使用する例は,参考リンクの記事にあります.当サイトにも TStreamWriter, TStreamReader を使用したサンプルがあります.TStreamWriter, TStreamReader 共に.Delphi 2009 以降で使用可能です.
図27
設計時画面
図28
Button1 クリック
図29
Button2 クリック
図30
Button3 クリック
Delphi XE でコンパイルして実行した結果
Delphi XE2 以降でコンパイルした EXE では文字化けする
AssignFile の Delphi XE2 以降用のコードは Button4 クリックのコード
図31
Button5 クリック
リスト11
TFileStream, TextFile を使用して,UTF-8N のテキストを読み込んで表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
OpenDialog1: TOpenDialog;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// UTF-8のテキストファイルを読込んで表示
// TStringListを使用した方法
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStringList : TStringList;
LText : string ;
begin
Memo1.Clear;
LStringList := TStringList.Create;
try
LStringList.LoadFromFile('000.txt' , TEncoding.UTF8);
LText := LStringList.Text;
finally
LStringList.Free;
end ;
Memo1.Text := LText;
end ;
// =============================================================================
// UTF-8のテキストファイルを読込んで表示
// TFileStreamを使用した方法
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LFileStream : TFileStream;
begin
Memo1.Clear;
LFileStream := TFileStream.Create('000.txt' , fmOpenRead);
try
LFileStream.Position := 0 ;
Memo1.Lines.LoadFromStream(LFileStream, TEncoding.UTF8);
finally
LFileStream.Free;
end ;
end ;
// =============================================================================
// UTF-8のテキストファイルを読込んで表示
// どうしてもTextFile,Readlnが使いたい場合
// Delphi 2009~Delphi XE以前専用
// Delphi XE2以降では文字化けする
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LTextFile : TextFile;
LUTF8Text : UTF8String;
LResText : String ;
begin
Memo1.Clear;
Assignfile(LTextFile, '000.txt' );
Reset(LTextFile);
while not Eof(LTextFile) do begin
Readln(LTextFile, LUTF8Text);
// UTF-8→UTF-16なのでロスレス変換なのだけれども、
// 単純に代入するとコンパイラがワーニングを吐くのでキャスト
LResText := UnicodeString(LUTF8Text);
Memo1.Lines.Add(LResText);
end ;
CloseFile(LTextFile);
end ;
// =============================================================================
// UTF-8のテキストファイルを読込んで表示
// どうしてもTextFile,Readlnが使いたい場合
// Delphi XE2以降のAssignFileではエンコードの指定ができるので,その動作確認用
// Delphi XE2以降専用
// =============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
LTextFile : TextFile;
LText : string ;
LEnc : TEncoding;
begin
Memo1.Clear;
{$IF CompilerVersion >= 23.0}
// Assignfile(LTextFile, '000.txt', TEncoding.UTF8.CodePage); //でも可
// GetEncodingを使用した場合は解放が必要
LEnc := TEncoding.GetEncoding(CP_UTF8);
try
Assignfile(LTextFile, '000.txt' , LEnc.CodePage);
Reset(LTextFile);
while not Eof(LTextFile) do begin
Readln(LTextFile, LText);
Memo1.Lines.Add(LText);
end ;
finally
FreeAndNil(LEnc);
end ;
CloseFile(LTextFile);
{$IFEND}
end ;
// =============================================================================
// UTF-8のテキストファイルを読込んで表示
// エンコードしない場合の動作確認用
// =============================================================================
procedure TForm1.Button5Click(Sender: TObject);
var
LStringList : TStringList;
LResText : String ;
begin
Memo1.Clear;
LStringList := TStringList.Create;
try
LStringList.LoadFromFile('000.txt' );
LResText := LStringList.Text;
finally
LStringList.Free;
end ;
Memo1.Text := LResText;
end ;
end .
Delphi 2010 以降では,IOUtils.TFile が使用できます.
以下のコードはテキストファイルの読み込みだけですが,書き込み,保存の際もエンコードしてコードページの指定ができます.
[備考]
IOUtils.TFile でテキストを UTF-8 で保存する時は,BOM なしになります.
図32
実行結果
添付の UTF-8N のテキストファイルを読み込んで表示
リスト12
IOUtils.TFile を使用したテキストファイルの読み込み
copy code
implementation
uses
IOUtils;
{$R *.dfm}
// =============================================================================
// UTF-8のテキストファイルを読込んで表示
// IOUtils.TFileを使用する例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LText : string ;
begin
Memo1.Clear;
LText := TFile.ReadAllText('000.txt' , TEncoding.UTF8);
Memo1.Text := LText;
end ;
13_ TextFile オブジェクトを使用したテキストの保存と読み出し
上のサンプルで TextFile オブジェクトを使用しました.
参考として TextFile オブジェクトだけを使用したテキストの保存と読み出しの簡単なサンプルを掲載しておきます.エンコードについては,上のサンプルを参考にしてください.AssignFile 関数でコードページの指定が可能なのは Delphi XE2 以降です.
図33
[Button1] クリックで保存されたテキストファイルの内容
図34
[Button1] クリックの後 [Button2] クリックで保存されたテキストファイルの内容
図35
保存したテキストを 1 行だけ読み出して表示
図36
保存したテキストを全て読み出して表示
リスト13
TextFile を使用したテキストの保存と読み出しのサンプル
copy code
// =============================================================================
// TextFileオブジェクトの使用例
//
// テキストファイルに新規にテキスト(文字列)を保存する
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LFilePath : string ;
LTextFile : TextFile;
begin
Memo1.Clear;
// テキストファイルのパス名
LFilePath := ExtractFilePath(Application.ExeName) + '000.txt' ;
// テキストファイルをTextFileオブジェクトにアサイン
Assignfile(LTextFile, LFilePath);
// TextFileを書き込み用として開く
// テキストファイルが存在しない場合は新規に作成される
// 存在する場合はテキストファイルの内容はクリアされる
Rewrite(LTextFile);
Writeln(LTextFile, 'テスト文字列です' );
Writeln(LTextFile, '2 行目の文字列です' );
Writeln(LTextFile, '3 行目以降の文字列です' + sLineBreak + '改行コード付きです' );
// TextFileオブジェクトを閉じる
CloseFile(LTextFile);
end ;
// =============================================================================
// TextFileオブジェクトの使用例
//
// テキストファイルにテキスト(文字列)を追加保存する
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LFilePath : string ;
LTextFile : TextFile;
begin
Memo1.Clear;
// テキストファイルのパス名
LFilePath := ExtractFilePath(Application.ExeName) + '000.txt' ;
// ファイル存在を確認
if FileExists(LFilePath) then begin
// テキストファイルをTextFileオブジェクトにアサイン
Assignfile(LTextFile, LFilePath);
// 追加モードでTextFileを開く
Append(LTextFile);
// 1行だけ追加する
Writeln(LTextFile, '追加の文字列です' );
// TextFileオブジェクトを閉じる
CloseFile(LTextFile);
end ;
end ;
// =============================================================================
// TextFileオブジェクトの使用例
//
// テキストファイルからテキスト(文字列)を1行だけ読み出す
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LFilePath : string ;
LTextFile : TextFile;
LResText : String ;
begin
Memo1.Clear;
// テキストファイルのパス名
LFilePath := ExtractFilePath(Application.ExeName) + '000.txt' ;
// ファイル存在を確認
if FileExists(LFilePath) then begin
// テキストファイルをTextFileオブジェクトにアサイン
Assignfile(LTextFile, LFilePath);
// TextFileを読み出し用として開いて先頭に移動
Reset(LTextFile);
// 1行だけ読み出す
Readln(LTextFile, LResText);
Memo1.Lines.Add(LResText);
// TextFileオブジェクトを閉じる
CloseFile(LTextFile);
end ;
end ;
// =============================================================================
// TextFileオブジェクトの使用例
//
// テキストファイルから全てのテキスト(文字列)を読み出す
// =============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
LFilePath : string ;
LTextFile : TextFile;
LResText : String ;
begin
Memo1.Clear;
// テキストファイルのパス名
LFilePath := ExtractFilePath(Application.ExeName) + '000.txt' ;
// ファイル存在を確認
if FileExists(LFilePath) then begin
// テキストファイルをTextFileオブジェクトにアサイン
Assignfile(LTextFile, LFilePath);
// TextFileを読み出し用として開いて先頭に移動
Reset(LTextFile);
// ファイルの最後まで1行づつ読み出す
while not Eof(LTextFile) do begin
Readln(LTextFile, LResText);
Memo1.Lines.Add(LResText);
end ;
// TextFileオブジェクトを閉じる
CloseFile(LTextFile);
end ;
end ;
14_ その他のテキストの書き込みと読み込み用のクラス
以下のようなクラスが実装されていますが,コードページ指定の機能はありません.
Delphi 10 では JSON 関係のクラスも実装されています.