QLOOK ANALYTICS

TEncodingとテキストの入出力

TEncodingを使うと簡単に様々な文字コードのテキストファイルを読み書きできます。
TStringListと組み合わせて使うのが一般的。

テキストの読み書き

2007まで:TStringList.LoadFromFile / SaveToFile

普通Delphiでテキストを入出力するって言ったらTStringListを使うわけですが、
従来の処理ではAnsiStringのデフォルトのコードページで保存されていました

Code#01:TStringList.SaveToFile(Legacy)

上記コードであれば、"Delphi 2009欲しいなぁ"という文字列がCP932(Shift_JIS)で保存されます。
同様にLoadFromFileではCP932(Shift_JIS)で文字列が読み出されることになります。

当然ですがロケールが変わってコードページが変われば、
CP932(Shift_JIS)以外のコードページで保存されることになります。

2009:TStringList.LoadFromFile / SaveToFile + TEncoding

Delphi 2009では新たにTEncodingというクラスが追加されました。
.NETをやったことがある人なら分かると思いますが、.NETのEncodingクラス(System.Text)をネイティブに移植したものです。
TStringListのLoadFromFile / SaveToFileには新たにTEncodingを引数にとる物がオーバーロードされているので、
それを使えば任意の文字コードで手軽にテキストの読み書きが可能になります。

Code#02:TStringList.SaveToFile(with TEncoding)

さっきのコードとほとんど同じですが、SaveToFileメソッドの第2引数にTEncodingを指定しています。
上記例では、"Delphi 2009欲しいなぁ"という文字列がUTF-16LEで保存されます。
読み込み時にはLoadFromFileの第2引数にTEncodingを指定するだけです。

TEncodingを指定しなくても自動的に認識されるもの

ANSIのデフォルトのコードページ(CP932:Shift_JIS)、
BOM付きのUTF-16LE/BEおよびBOM付きのUTF-8であれば、
指定を省略しても正常に読み込まれるようです。

TOpenTextFileDialogとTSaveTextFileDialogと組み合わせる

VCLにはDialogにTOpenTextFileDialogとTSaveTextFileDialogというコンポーネントがあります。
これらは通常のOpenDialogとSaveDialogにエンコーディングのオプションを指定できるようにしたものです。
これらにはEncodingというStrings型のプロパティがあり、この中に選択させたいエンコーディングを記述します。
デフォルトでは、

・ANSI
・ASCII
・Unicode
・Big Endian Unicode
・UTF-8
・UTF-7

とリストアップされています。
適宜対応するエンコードリストに編集すればよいでしょう。

これでユーザーが指定したエンコーディングに合わせたテキストの読み書きがしやすくなりますね。

TEncoding.GetEncoding

任意のコードページを取得しよう

TEncodingのGetEncodingメソッドを使うと、任意のコードページの情報を取得できます。
例えば、EUC_JPで保存する場合には下記のようにします

Code#03:TStringList.SaveToFile(EUC_JP)

EUC_JPのコードページは20932なので、GetEncodingの引数に指定してやるだけです。
なお、.NETにはGetEncodingの引数が文字列型のものがありますが、
TEncodingには搭載されていません。
(既にQCではリクエスト済みです)

なお、GetEncodingは内部でTMBCSEncoding.Create(CodePage)を呼び出しているだけなので、
明示的に破棄しないとメモリリークが発生します。
ちなみにUTF8プロパティなどであれば明示的に破棄する必要性はありません。

--Column:GetEncodingの仕様--
GetEncodingを使用した際に明示的にFreeしない場合、
メモリリークが発生するのは仕様です。
GetEncodingを用いた時は必ず明示的に破棄するようにしましょう。

動作するコードページと動作しないコードページ

TEncoding.GetEncodingには色々と制限があります。
基本的にGetCPInfoで情報を取れないコードページには対応できていません。

日本語を扱う上で良く出てくるコードページを以下にまとめてみました

Table#01

コードページ 名称 動作 代替手段
コードページ 名称 動作 代替手段
0 Default (Shift_JIS) 不要
932 Shift_JIS 不要
1200 UTF-16LE × TEncoding.Unicode
TUnicodeEncoding
1201 UTF-16BE × TEncoding.BigEndianUnicode
TBigEndianUnicodeEncoding
10001 x-mac-japanese 不要
12000 UTF-32LE × 無し
12001 UTF-32BE × 無し
20290 IBM EBCDIC (日本語カタカナ) 不要
20932 EUC_JP 不要
50220 iso-2022-jp (JIS) 不要
50221 csISO2022JP (JIS 1 バイト カタカナ可) 不要
50222 iso-2022-jp (JIS 1 バイト カタカナ可 - SO/SI) 不要
51932 EUC_JP × CP20932を使う
65000 UTF-7 TEncoding.UTF7
TUTF7Encoding
65001 UTF-8 TEncoding.UTF8
TUTF8Encoding

動作の欄が×となっているものは、指定するとエラーが発生するものです。
このうち、黄色のものは代替手段があるもの、赤いものは代替手段がないものです。

CP51932はCP20932をもって代替しろと言うことですが、これらは厳密には少し異なります。
まぁ別の代替手段がないわけではないですが、TEncodingを用いると20932を使えということになっています。

現在のDelphi 2009には、とても残念なことにUTF32プロパティやUTF32Encodingクラスが搭載されていません。
よってUTF32LE/BEでのテキストの入出力を手軽に行えません。
これらについては将来的なアップデートで是非搭載して欲しいところです。

BOMなしUTF-8

TEncoding.UTF8およびTUTF8EncodingはどちらもBOM付きのUTF-8として機能しているようです。
ですが、UTF-8には本来BOMは不要なものです。
BOM無しのUTF-8で保存/読み込みをするには下記のようにします。

Code#04:UTF-8 without BOM

GetEncodingを使って引数に65001を指定すると、BOM無しのUTF-8で保存されるようです。
この要領でUTF-16やUTF-32も、と行きたいところですが、
上記表にあるとおりCP1200/1201およびCP12000/12001ではGetEncodingは機能しません。

BOMなしUTF-16LE / BE

UTF-16においてBOM無しで保存をしたいと思っても、
GetEncodingではCP1200/1201には対応していないうえ、
TUnicodeEncodingおよびTBigEndianUnicodeEncodingを使うと自動的にBOMが付与されてしまいます。

最も手っ取り早い方法はTUnicodeEncodingまたはTBigEndianUnicodeEncodingを継承したクラスを作ることです。
BOMを返すメソッドがGetPreambleというメソッドなので、それをオーバーライドしてしまえばOKです。

Code#005:BOMなしTUnicodeEncoding

ここでは例としてUTF-16LE用にTUnicodeEncodingを継承したTUnicodeEncodingExというクラスを作成してみました。
GetPreambleは戻り値がTBytes(Byteの動的配列)であるため、
オーバーライドしたメソッドではSetLengthで0に指定してしまえばOKです。

ちなみにTUTF8Encodingについても同様のことが言えますが、
GetEncodingと使い分ければいいだけなので、わざわざこの手法を採る必要性はありません。

Delphi 2009 特集へ戻る