如何在 Delphi 中使用 TXMLDocument 将 Ansi 转换为 UTF 8
Posted
技术标签:
【中文标题】如何在 Delphi 中使用 TXMLDocument 将 Ansi 转换为 UTF 8【英文标题】:How to Convert Ansi to UTF 8 with TXMLDocument in Delphi 【发布时间】:2013-07-18 12:03:17 【问题描述】:可以在 Delphi 6 中将 XML 转换为 UTF-8 编码吗? 目前这就是我正在做的事情:
用 AnsiString 填充 TXMLDocument 最后使用WideStringVariable = AnsiToUtf8(Doc.XML.Text);
将数据转换为UTF-8
使用TFileStream
将WideStringVariable
的值保存到文件中,并在文件开头添加BOM for UTF8
。
代码:
Procedure SaveAsUTF8( const Name:String; Data: TStrings );
const
cUTF8 = $BFBBEF;
var
W_TXT: WideString;
fs: TFileStream;
wBOM: Integer;
begin
if TRIM(Data.Text) <> '' then begin
W_TXT:= AnsiToUTF8(Data.Text);
fs:= Tfilestream.create( Name, fmCreate );
try
wBOM := cUTF8;
fs.WriteBUffer( wBOM, sizeof(wBOM)-1);
fs.WriteBuffer( W_TXT[1], Length(W_TXT)*Sizeof( W_TXT[1] ));
finally
fs.free
end;
end;
end;
如果我在 Notepad++ 或其他检测编码的编辑器中打开文件,它会显示带有 BOM 的 UTF-8。但是,它似乎没有正确编码文本。
出了什么问题,我该如何解决?
更新:XML 属性:
XMLDoc.Version := '1.0';
XMLDoc.Encoding := 'UTF-8';
XMLDoc.StandAlone := 'yes';
【问题讨论】:
由于我对 unicode 不太了解,因为我的问题才刚刚开始,如果您向我推荐一些有关它的初学者书籍或论文,我将不胜感激。 docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/… 保存文件好吗? 保存了文件但是webservices说它不是UTF-8编码的,还有,编辑器里出现奇怪的字符正常吗?WideString
是 Microsoft OLE BSTR
类型,根据定义是 UTF-16。显示AnsiToUtf8
声明,它的返回类型是什么?您应该对变量使用相同的类型,否则会发生文本转换。而且由于您在声明 ANSI 时的输出将包含 UTF-8 文本,这意味着您误导了 Delphi 并促使它使 ANSI->Tf-16 转换为非 ANSI 文本
“它会保存文件”,因此请使用 Hex Editor、WinCmd Lister、UniRed、notepad++ 或任何其他类似工具检查它——它是否真的是 utf-8。不管服务告诉你什么——你不应该盲目相信它。 PS D6 是相当有缺陷的版本。你确定你安装了所有的更新吗?也许您现在会考虑升级到 CodeTyphon...
【参考方案1】:
您可以使用标准SaveToFile
方法在TXMLDocument
变量上保存文件:http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/XMLDoc_TXMLDocument_SaveToFile.html
文件是否为 UTF8,您必须使用本地工具(如前面提到的 Notepad++ 或 Hex Editor 或其他任何工具)进行检查。
如果你坚持使用中间字符串和文件流,你应该使用适当的变量。 AnsiToUTF8
返回 UTF8String
类型,这就是要使用的内容。
编译 `WideStringVar := AnsiStringSource' 会发出编译器警告和
这是一个正确的警告。谷歌搜索“Delphi WideString” - 或阅读有关主题的 Delphi 手册 - 表明 WideString
aka Microsoft OLE BSTR
以 UTF-16 格式保存数据。 http://delphi.about.com/od/beginners/l/aa071800a.htm
因此赋值UTF16 string <= 8-bit source
必然会转换数据,因此转储WideString
数据不能转储UTF-8
定义WideString
的文本
Procedure SaveAsUTF8( const Name:String; Data: TStrings );
const
cUTF8: array [1..3] of byte = ($EF,$BB,$BF)
var
W_TXT: UTF8String;
fs: TFileStream;
Trimmed: AnsiString;
begin
Trimmed := TRIM(Data.Text);
if Trimmed <> '' then begin
W_TXT:= AnsiToUTF8(Trimmed);
fs:= TFileStream.Create( Name, fmCreate );
try
fs.WriteBuffer( cUTF8[1], sizeof(cUTF8) );
fs.WriteBuffer( W_TXT[1], Length(W_TXT)*Sizeof( W_TXT[1] ));
finally
fs.free
end;
end;
end;
顺便说一句,如果源数据为空,您的这段代码甚至不会创建空文件。它看起来相当可疑,尽管由您来决定这是否是您程序的其余部分的错误。
将接收到的文件或流正确“上传”到网络是另一个问题(将作为一个单独的问题放在像 SO 这样的问答网站上),与测试与 HTTP 的一致性有关。作为前言,你可以在WWW server reports error after POST Request by Internet Direct components in Delphi阅读一些提示
【讨论】:
然而那部分网络服务是不必要的 我认为不是。 Web 服务清单显示了许多“检查点”,从而为您将来调查该问题提供了指导。每个选项都不是说“你忽略了它,傻瓜”,而是“你可能在这里有问题,也检查一下”。好吧,由你决定。【参考方案2】:为了在文档中获得正确的编码,您应该使用 XML 文档中的 Encoding 属性进行设置,如下所示:
myXMLDocument.Encoding := 'UTF-8';
我希望这会有所帮助。
【讨论】:
我不明白。如果它已经这样设置,那么为什么将其标记为解决方案?为了解决您的问题,您还需要对代码进行哪些其他更改?【参考方案3】:你只需要调用文档的SaveToFile
方法:
XMLDoc.SaveToFile(FileName);
由于您已经指定了编码,组件将使用该编码。
这不包括 BOM,但这通常是您想要的 XML 文件。文件内容将指定编码。
关于你的SaveAsUTF8
方法,不需要,但很容易修复。这可能对你有启发。
问题是当您分配给WideString
变量时,您正在转换为UTF-16。您应该将 UTF-8 文本放入 AnsiString
变量中。将您命名为W_TXT
的变量类型更改为AnsiString
就足够了。
函数可能如下所示:
Procedure SaveAsUTF8(const Name: string; Data: TStrings);
const
UTF8BOM: array [0..2] of AnsiChar = #$EF#$BB#$BF;
var
utf8: AnsiString;
fs: TFileStream;
begin
utf8 := AnsiToUTF8(Data.Text);
fs:= Tfilestream.create(Name, fmCreate);
try
fs.WriteBuffer(UTF8BOM, SizeOf(UTF8BOM));
fs.WriteBuffer(Pointer(utf8)^, Length(utf8));
finally
fs.free;
end;
end;
【讨论】:
不仅仅是“你已经完成了”,而是“没有(单独)解决问题”【参考方案4】:另一种解决方案:
procedure SaveAsUTF8(const Name: string; Data: TStrings);
var
fs: TFileStream;
vStreamWriter: TStreamWriter;
begin
fs := TFileStream.Create(Name, fmCreate);
try
vStreamWriter := TStreamWriter.Create(fs, TEncoding.UTF8);
try
vStreamWriter.Write(Data.Text);
finally
vStreamWriter.Free;
end;
finally
fs.free;
end;
end;
【讨论】:
以上是关于如何在 Delphi 中使用 TXMLDocument 将 Ansi 转换为 UTF 8的主要内容,如果未能解决你的问题,请参考以下文章
如何在不同步的情况下使用 Delphi (Pre Delphi 2010) 版本更新 GUI 控件
Delphi:如何在公共函数中使用 ComponentCount?
如何停止在 Delphi 中使用 IVBSAXXMLReader 解析 XML 文档?