用 Delphi 寫了個程序,在 Windows 底下,加載一個文本文件,查裏面的字符串。
文本文件,隨手用 Windows 的記事本編輯了一個。
程序運行起來,測試這個文本文件,正常。
但是,某天我把 Windows 的系統語言設置爲英語,再次跑這個程序,加載上述文本文件時,出錯了,查不到應該有的字符串了。
用記事本打開那個文本文件一看,裏面的中文字符串是亂碼。這纔想起來,Windows 的記事本,默認保存的格式是 ANSI。在中文的 Windows 環境下保存的中文字符串,在英文環境下打開,就是亂碼。
於是俺對這個文本文件進行了測試,保存爲 Unicode,保存爲 UTF8,然後在中文和英文的 Windows 底下進行測試。
以下是測試結果:
procedure TForm1.Button1Click(Sender: TObject);
var
SL: TStringList;
S: string;
begin
{--------------------------------------------------------------------------
以下代碼用 TStringList 直接加載一個文本文件,直接讀取它的 Text 屬性,
獲得的 Text 是 UNICODE 編碼的字符串。
因此應該是 Delphi 的 TStringList 在輸出字符串的時候自動做了一個解碼操作。
但這個解碼操作不能處理無 BOM 的 UTF8 編碼的文本文件。
以下代碼能夠處理 UTF8 BOM 編碼的文本文件;
不能處理 UTF8 無 BOM 編碼的文本文件。
--------------------------------------------------------------------------}
S := '深圳';
if OpenDialog1.Execute then
begin
SL := TStringList.Create;
try
SL.LoadFromFile(OpenDialog1.FileName);
Memo1.Lines.Add('-------------------');
Memo1.Lines.Add(SL.Text);
if SL.IndexOf(S) > 0 then
begin
ShowMessage('Found target');
end
else
begin
ShowMessage('Can not found 深圳');
end;
finally
SL.Free;
end;
end;
end;
以下是另外一段程序:
procedure TForm1.Button2Click(Sender: TObject);
var
M: TMemoryStream;
S: string;
B: TBytes;
begin
{--------------------------------------------------------------------------
以下代碼能夠處理 UTF8 BOM 和 UTF8 無 BOM 的兩種不同編碼,都能正確解碼爲 UNICODE 給 MEMO 顯示。
--------------------------------------------------------------------------}
if OpenDialog1.Execute then
begin
M := TMemoryStream.Create;
try
M.LoadFromFile(OpenDialog1.FileName);
M.Position := 0;
SetLength(B, M.Size);
M.Read(B[0], M.Size);
S := TEncoding.UTF8.GetString(B);
Memo1.Lines.Add(S);
finally
M.Free;
end;
end;
end;
上述程序,都在中文的 Windows 和英文的 Windows 底下做了測試。
因此,現在編程的時候需要注意,如果使用到外部的文本文件,最好是保存爲 UNICODE 格式。
在 Delphi 的源代碼裏面寫死的中文字符串,則因爲是 Unicode 格式,放到哪個系統下,都還是那個字符串,不會因爲系統變了導致出問題。
-----------------------------------------------------
更進一步:
如果是數據庫而不是文本文件,作爲外部數據源。這種情況下,不同的數據庫可能有不同的情況。以目前 Delphi FireDAC 支持的 SQLite 數據庫來說,最好設置數據庫文件的編碼格式是 UTF8,這樣方便程序在任何語言的系統下正常工作。然後注意:
1. 如果是 NVarChar 類型的字段,FireDAC 的 TField.AsString 會自動將裏面存儲的 UTF8 格式轉換爲 UNICODE 格式;
2. Blob 字段,如果直接讀取它的 TField.AsString 則不會自動轉換,因此如果把 TDBMemo 綁定到一個 Blob 字段,會顯示亂碼(裏面顯示的是 UTF8 而不是 UNICODE,說明 TDBMemo 不會自動處理 UTF8);
3. 如果是 Memo 字段,對應的 FireDAC 的字段類型也是 Memo 類型,則直接綁定到 TDBMemo 上面,是可以正確顯示文本內容的。說明 FireDAC 的 TField.AsString 對 Memo 字段的 UTF8 編碼做了自動的解碼處理。