说明下,这个方法有好大的缺陷,XML必须要严格正确,如果有什么特殊字符,会导致出错,其次,并不能很好的解决日文或韩文问题,我已经换成了c#了
这两天要用delphi 做个东西要与 xml相关, 开始只是想用 delphi本身的控制,网上找了下, 有两种方式,一个是 TXMLDocument 在 Internet 面板下, 另一个是用 IXMLDomDocument 需要用到 单元文件 msxml, 开始只注意到英文,用 TXMLDocument 解析起来没什么问题, 但是后来要涉及到多语言问题, xml文件是 utf-8格式,用 TXMLDocumnet解析的时候,就成了乱码,这两天在网上找了好久,都没成功(我的开发环境是英文的, 王哥说delphi支持 utf-8的xml, 而我照着他的例子一直都没成功, 很闷.后来晚上回家在中文环境下实验时,一切正常...) , 网上看到, delphi处理 utf-8时,需要转成ansi编码 (WideString 还是 AnsiString ?谈谈字符编码)
我按这种方式转了之后, 然后继承 TXMLDocument 使 其的 loadFromXml 可用, 但是结果报错, 因为字符串的编码和encoding的编码不一致, 一直没有进展, 也考虑过用 msxml, 但初步使用之后还是乱码. 但却没想到先转码, 然后通过 msxml的方式...
今天上午无意中用了 IXMLDomDocument 来解析, 不过在解析之前先把文件当做文本读出来并转码, 然后再解析, 终于, 成功了.
有两个部分要注意, 一是转码, 我直接抄网上的, 后面会出来, 另一部分则是用 ixmlDomDocument 解析.
temp.xml 注意保存为 utf-8格式, 记事本-->保存-->下面选择 utf-8
- xml version="1.0" encoding="utf-8" standalone="no"?>
- <root maxnodeId="15">
- <node objid="1" name="発注-H" >
- <subnode objid="1" name="oid" />
- <subnode objid="6" name="発注番号 :" />
- <subnode objid="7" name="発注状況 :" />
- <subnode objid="72" name="入荷状況 :"/>
- <subnode objid="76" name="買掛状況 :" />
- <subnode objid="8" name="発注日 :" type="date" />
- <subnode objid="73" name="入荷日 :" type="date" />
- <subnode objid="77" name="离开时间:" type="date" />
- <subnode objid="8" name="発注日 :" type="date" />
- <subnode objid="73" name="入荷日 :" type="date" />
- <subnode objid="77" name="支払日 :" type="date" />
- <subnode objid="79" name="請求額 :" type="int" />
- node>
- root>
encoding.pas 这个主要是参照下面那个 wideString or AnsiString的
- unit encoding;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics,
- Controls, Forms, Dialogs, ComCtrls, Menus, StdCtrls;
- Type
- TEncodeFlags = (efUnknown, efAnsi, efUnicode, efUncodeBigEn, efUTF8);
- TTextFormat=(tfAnsi,tfUnicode,tfUnicodeBigEndian,tfUtf8);
- TUTF8Falg = packed record
- EF, BB, BF: Byte;
- end;
- const
- TextFormatFlag:array[tfAnsi..tfUtf8] of word=($0000,$FFFE,$FEFF,$EFBB);
- Encode: TUTF8Falg = (EF: $EF; BB: $BB; BF: $BF);
- function ChWideToAnsi(const StrW: WideString): AnsiString;
- function ChAnsiToWide(const StrA: AnsiString): WideString;
- function UTF8ToWideString(const Stream: TStream): WideString;
- procedure TextToUTF8Stream(const Text: string; var Stream: TStream);
- function GetEncodeFromStream(const Stream: TStream): TEncodeFlags;
-
- function loadFromFile(const fName :string) :String;
- procedure savetoFile(const txt, fName :String);
- implementation
- function loadFromFile(const fName :String) :String;
- var
- FStream :TStream;
- begin
- FStream := TMemoryStream.Create;
- TMemoryStream(FStream).LoadFromFile(fName);
- if GetEncodeFromStream(FStream) = efUTF8 then
- begin
- result := ChWideToAnsi(UTF8ToWideString(FStream));
- end;
- end;
- procedure savetoFile(const txt, fName :String);
- var
- FStream :TStream;
- begin
- FStream := TMemoryStream.Create;
- try
- TextToUTF8Stream(txt, FStream);
- TMemoryStream(FStream).SaveToFile(fName);
- finally
- FStream.Size := 0;
- end;
- end;
- procedure WordLoHiExchange(var w:Word);
- var
- b:Byte;
- begin
- b:=WordRec(w).Lo;
- WordRec(w).Lo:=WordRec(w).Hi;
- WordRec(w).Hi:=b;
- end;
-
- function ChWideToAnsi(const StrW: WideString): AnsiString;
- var
- nLen: integer;
- begin
- Result := StrW;
- if Result <> '' then
- begin
- nLen := WideCharToMultiByte(936, 624, @StrW[1], -1, nil, 0, nil, nil);
- SetLength(Result, nLen - 1);
- if nLen > 1 then
- WideCharToMultiByte(936, 624, @StrW[1], -1, @Result[1], nLen - 1, nil, nil);
- end;
- end;
- function ChAnsiToWide(const StrA: AnsiString): WideString;
- var
- nLen: integer;
- begin
- Result := StrA;
- if Result <> '' then
- begin
- nLen := MultiByteToWideChar(936, 1, PChar(@StrA[1]), -1, nil, 0);
- SetLength(Result, nLen - 1);
- if nLen > 1 then
- MultiByteToWideChar(936, 1, PChar(@StrA[1]), -1, PWideChar(@Result[1]), nLen - 1);
- end;
- end;
- function UTF8ToWideString(const Stream: TStream): WideString;
- var
- nLen: Cardinal;
- begin
- try
- SetLength(Result, Stream.Size div SizeOf(WideChar) * 3);
- nLen := Utf8ToUnicode(@Result[1], Length(Result),
- Pointer(DWord(TMemoryStream(Stream).Memory) + Stream.Position),
- Stream.Size - Stream.Position);
- SetLength(Result, nLen);
- except
- SetLength(Result, 0);
- end;
- end;
- procedure TextToUTF8Stream(const Text: string; var Stream: TStream);
- var
- StringW, StrW: WideString;
- nLen: Cardinal;
- begin
- try
- if Text <> '' then
- begin
- StrW := ChAnsiToWide(Text);
- nLen := Length(StrW) * 3;
- SetLength(StringW, nLen);
- nLen := UnicodeToUtf8(@StringW[1], nLen, @StrW[1], Length(StrW));
- SetLength(StringW, nLen);
- Stream.Write(Encode, SizeOf(Encode));
- Stream.Write(StringW[1], Length(StringW));
- end
- else
- Stream.Write(Encode, SizeOf(Encode));
- except
- SetLength(StrW, 0);
- SetLength(StringW, 0);
- end;
- end;
- function GetEncodeFromStream(const Stream: TStream): TEncodeFlags;
- var
- FEncode: TUTF8Falg;
- begin
- Result := efUnknown;
- Stream.Read(FEncode, SizeOf(FEncode));
- if (FEncode.EF = Encode.EF) and (FEncode.BB = Encode.BB)
- and (FEncode.BF = Encode.BF) then Result := efUTF8;
- end;
- end.
下面是使用代码,你可以直接拷到一个 button的事件中
- var
- xmlDom :IXMLDomDocument;
- begin
- xmlDom := msxmldom.CreateDOMDocument;
- xmlDom.loadXML(encoding.loadFromFile('temp.xml'));
- showMessage(xmlDom.xml);
- end;
试的环境有, 英文,中文(简体), 中文(台湾), 日语, 朝鲜语, 其中前三者都完成正确,但是后两个有部分不能正常显示,有待考虑.
特别感谢: 王哥的帮助 http://hi.csdn.net/zswang/
另外还参考了 WideString 还是 AnsiString ?谈谈字符编码
http://blog.csdn.net/xwchen/archive/2007/03/21/1536829.aspx
encoding部分是从这来的