用 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 编码做了自动的解码处理。