现在的位置: 首页 > 综合 > 正文

delphi 操作 utf-8 的xml 有乱码的解决

2012年07月03日 ⁄ 综合 ⁄ 共 5232字 ⁄ 字号 评论关闭

   说明下,这个方法有好大的缺陷,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
    1. <?xml version="1.0" encoding="utf-8" standalone="no"?>
    2. <root maxnodeId="15">
    3.   <node objid="1" name="発注-H" >
    4.     <subnode objid="1" name="oid" />
    5.     <subnode objid="6" name="発注番号 :" />
    6.     <subnode objid="7" name="発注状況 :"  />
    7.     <subnode objid="72" name="入荷状況 :"/>
    8.     <subnode objid="76" name="買掛状況 :" />
    9.     <subnode objid="8" name="発注日 :" type="date" />
    10.     <subnode objid="73" name="入荷日 :" type="date" />
    11.     <subnode objid="77" name="离开时间:" type="date" />
    12.     <subnode objid="8" name="発注日 :" type="date" />
    13.     <subnode objid="73" name="入荷日 :" type="date" />
    14.     <subnode objid="77" name="支払日 :" type="date" />
    15.     <subnode objid="79" name="請求額 :" type="int" />
    16.   </node>
    17. </root>
encoding.pas 这个主要是参照下面那个 wideString or AnsiString的
  1. unit encoding;
  2. interface
  3.  uses
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics,
  5.  Controls, Forms, Dialogs, ComCtrls, Menus, StdCtrls;
  6. Type
  7. TEncodeFlags = (efUnknown, efAnsi, efUnicode, efUncodeBigEn, efUTF8);
  8. TTextFormat=(tfAnsi,tfUnicode,tfUnicodeBigEndian,tfUtf8);
  9. TUTF8Falg = packed record
  10.    EF, BB, BF: Byte;
  11.  end;
  12. const
  13. TextFormatFlag:array[tfAnsi..tfUtf8] of word=($0000,$FFFE,$FEFF,$EFBB);
  14. Encode: TUTF8Falg = (EF: $EF; BB: $BB; BF: $BF);
  15.    function ChWideToAnsi(const StrW: WideString): AnsiString;
  16.    function ChAnsiToWide(const StrA: AnsiString): WideString;
  17.    function UTF8ToWideString(const Stream: TStream): WideString;
  18.    procedure TextToUTF8Stream(const Text: stringvar Stream: TStream);
  19.    function GetEncodeFromStream(const Stream: TStream): TEncodeFlags;
  20.     
  21.    function loadFromFile(const fName :string) :String;
  22.    procedure savetoFile(const txt, fName :String);
  23. implementation
  24. function loadFromFile(const fName :String) :String;
  25. var
  26.   FStream :TStream;
  27. begin
  28.     FStream := TMemoryStream.Create;  
  29.  TMemoryStream(FStream).LoadFromFile(fName);
  30.  if GetEncodeFromStream(FStream) = efUTF8 then
  31.  begin
  32.    result := ChWideToAnsi(UTF8ToWideString(FStream));
  33.  end;
  34. end;
  35. procedure savetoFile(const txt, fName :String);
  36. var
  37.   FStream :TStream;
  38. begin
  39.     FStream := TMemoryStream.Create;  
  40.   try
  41.     TextToUTF8Stream(txt, FStream);
  42.     TMemoryStream(FStream).SaveToFile(fName);
  43.   finally
  44.     FStream.Size := 0;
  45.   end;
  46. end;
  47. procedure WordLoHiExchange(var w:Word);
  48. var
  49. b:Byte;
  50. begin
  51.   b:=WordRec(w).Lo;
  52.   WordRec(w).Lo:=WordRec(w).Hi;
  53.   WordRec(w).Hi:=b;
  54. end;
  55.     
  56. function ChWideToAnsi(const StrW: WideString): AnsiString;
  57. var
  58.  nLen: integer;
  59. begin
  60.  Result := StrW;
  61.  if Result <> '' then
  62.  begin
  63.    nLen := WideCharToMultiByte(936624, @StrW[1], -1nil0nilnil);
  64.    SetLength(Result, nLen - 1);
  65.    if nLen > 1 then
  66.      WideCharToMultiByte(936624, @StrW[1], -1, @Result[1], nLen - 1nilnil);
  67.  end;
  68. end;
  69. function ChAnsiToWide(const StrA: AnsiString): WideString;
  70. var
  71.  nLen: integer;
  72. begin
  73.  Result := StrA;
  74.  if Result <> '' then
  75.  begin
  76.    nLen := MultiByteToWideChar(9361, PChar(@StrA[1]), -1nil0);
  77.    SetLength(Result, nLen - 1);
  78.    if nLen > 1 then
  79.      MultiByteToWideChar(9361, PChar(@StrA[1]), -1, PWideChar(@Result[1]), nLen - 1);
  80.  end;
  81. end;
  82. function UTF8ToWideString(const Stream: TStream): WideString;
  83. var
  84.  nLen: Cardinal;
  85. begin
  86.  try
  87.    SetLength(Result, Stream.Size div SizeOf(WideChar) * 3);
  88.    nLen := Utf8ToUnicode(@Result[1], Length(Result),
  89.      Pointer(DWord(TMemoryStream(Stream).Memory) + Stream.Position),
  90.      Stream.Size - Stream.Position);
  91.    SetLength(Result, nLen);
  92.  except
  93.    SetLength(Result, 0);
  94.  end;
  95. end;
  96. procedure TextToUTF8Stream(const Text: stringvar Stream: TStream);
  97. var
  98.  StringW, StrW: WideString;
  99.  nLen: Cardinal;
  100. begin
  101.  try
  102.    if Text <> '' then
  103.    begin
  104.      StrW := ChAnsiToWide(Text);
  105.      nLen := Length(StrW) * 3;
  106.      SetLength(StringW, nLen);
  107.      nLen := UnicodeToUtf8(@StringW[1], nLen, @StrW[1], Length(StrW));
  108.      SetLength(StringW, nLen);
  109.      Stream.Write(Encode, SizeOf(Encode));
  110.      Stream.Write(StringW[1], Length(StringW));
  111.    end
  112.    else
  113.      Stream.Write(Encode, SizeOf(Encode));
  114.  except
  115.    SetLength(StrW, 0);
  116.    SetLength(StringW, 0);
  117.  end;
  118. end;
  119. function GetEncodeFromStream(const Stream: TStream): TEncodeFlags;
  120. var
  121.  FEncode: TUTF8Falg;
  122. begin
  123.  Result := efUnknown;
  124.  Stream.Read(FEncode, SizeOf(FEncode));
  125.  if (FEncode.EF = Encode.EF) and (FEncode.BB = Encode.BB)
  126.    and (FEncode.BF = Encode.BF) then Result := efUTF8;
  127. end;
  128. end.

下面是使用代码,你可以直接拷到一个 button的事件中
  1. var
  2.   xmlDom :IXMLDomDocument;
  3. begin
  4.   xmlDom := msxmldom.CreateDOMDocument;
  5.   xmlDom.loadXML(encoding.loadFromFile('temp.xml'));
  6.   showMessage(xmlDom.xml);
  7.  end;

试的环境有, 英文,中文(简体), 中文(台湾), 日语, 朝鲜语, 其中前三者都完成正确,但是后两个有部分不能正常显示,有待考虑.
特别感谢: 王哥的帮助 http://hi.csdn.net/zswang/
另外还参考了 WideString 还是 AnsiString ?谈谈字符编码
http://blog.csdn.net/xwchen/archive/2007/03/21/1536829.aspx
encoding部分是从这来的

抱歉!评论已关闭.