【问题标题】:How to convert UTF-16 to ASCII/ANSI with XML/SGML entities?如何使用 XML/SGML 实体将 UTF-16 转换为 ASCII/ANSI?
【发布时间】:2023-03-18 06:12:01
【问题描述】:

XML 文档:

<?xml version="1.0" encoding="utf-8"?>
<response>
<center>
<b>Need to decode this -> ????</b>
</center>
</response>

我当前的代码:

procedure TForm1.Button1Click(Sender: TObject);
var
  Doc: IXMLDocument;
  S: AnsiString;
  SW: WideString;
  I: Integer;
begin
  Doc := TXMLDocument.Create(nil);
  Doc.LoadFromFile('example.xml');
  SW := Doc.DocumentElement.ChildNodes['center'].ChildNodes['b'].NodeValue;
  S := '';
  for I := 1 to Length(SW) do
    if Ord(SW[I]) > $04FF then
      S := S + IntToHex(Ord(SW[I]), 4) + ' '
    else
      S := S + SW[I];
  Memo1.Text := s;
end;

SW 以 UTF-16(宽字符串)编码并包含字符序列 #$D83D#$DE09,但我需要它作为 XML/SGML 实体,如 '&amp;#128521;'。我该如何编码?

使用的字符是这样的:http://www.fileformat.info/info/unicode/char/1f609/index.htm

【问题讨论】:

  • 不太清楚。所以基本上,您对什么 XML DOM 实现在基本多语言平面之外解码一个字符并将其编码为两个 UTF-16 单元不满意?并希望将其编码为 SGML 字符实体?
  • 不太明白,我忘了添加XML文档...我现在就添加它
  • 不顾我的判断,我尝试下载您的 XML 文件。我得到的只是 NFL 球衣的广告和下载服务。你能不能只在你的问题中粘贴 XML ?如果它太大而无法发布,那么它太大而无法阅读,因此将其削减到可以证明您的问题的最低限度,然后将其粘贴到此处。
  • 已添加.......
  • 我不知道你在这里问什么。您显示的 UTF-8 XML 有什么问题?我看不到任何需要解码的内容?

标签: xml delphi unicode delphi-7


【解决方案1】:

使用 ANSI Delphi 时,您必须手动处理 UTF-16 代理对(或使用一些第三方库)。

这应该适用于 ANSI 和 Unicode Delphi:

uses
  {$IFDEF UNICODE}
  Xml.XMLDoc, Xml.XMLIntf, System.AnsiStrings, System.Character;
  {$ELSE}
  XMLDoc, XMLIntf;
  {$ENDIF}

{$R *.dfm}

type
{$IFDEF UNICODE}
    ValueString = UnicodeString;
{$ELSE}
    ValueString = WideString;
{$ENDIF}

procedure Check(ATrue: Boolean; const AMessage: string);
begin
  if not ATrue then
    raise Exception.Create(AMessage);
end;

function IsHighSurrogate(AChar: WideChar): Boolean;
begin
{$IFDEF UNICODE}
  Result := TCharacter.IsHighSurrogate(AChar);
{$ELSE}
  Result := (AChar >= #$D800) and (AChar <= #$DBFF);
{$ENDIF}
end;

function ConvertToUtf32(AHigh, ALow: WideChar): Integer;
begin
  {$IFDEF UNICODE}
  Result := Ord(TCharacter.ConvertToUtf32(AHigh, ALow));
  {$ELSE}
  Check(AHigh >= #$D800, 'Invalid high surrogate code point');
  Check(AHigh <= #$DBFF, 'Invalid high surrogate code point');
  Check(ALow  >= #$DC00, 'Invalid low surrogate code point');
  Check(ALow  <= #$DFFF, 'Invalid low surrogate code point');
  // This will return the ordinal value of the Unicode character represented by the two surrogate code points
  Result := $010000 + ((Ord(AHigh) - $D800) shl 10) or (Ord(ALow) - $DC00);
  {$ENDIF}
end;

function MakeEntity(AValue: Integer): AnsiString;
begin
  Result := Format(AnsiString('&#%d;'), [AValue]);
end;

function UnicodeToAsciiWithEntities(const AInput: ValueString): AnsiString;
var
  C: WideChar;
  I: Integer;
begin
  Result := '';
  I := 1;
  while I <= Length(AInput) do
  begin
    C := AInput[I];
    if C < #$0080 then
      Result := Result + AnsiChar(C)
    else
    if IsHighSurrogate(C) then
    begin
      Check((I + 1) <= Length(AInput), 'String truncated after high surrogate');
      Result := Result + MakeEntity(ConvertToUtf32(C, AInput[I + 1]));
      // Skip low surrogate
      Inc(I);
    end
    else
      Result := Result + MakeEntity(Ord(C));
    Inc(I);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Text := string(UnicodeToAsciiWithEntities(LoadXMLDocument(
    'example.xml').DocumentElement.ChildNodes['center'].ChildNodes['b'].NodeValue
  ));
end;

我这里没有 Delphi 7,所以可能需要进行一些小的调整,代码在 XE2 和 2007 中有效。

【讨论】:

  • XML 文档声明其编码为 UTF-8
  • 与其将整个 XML 内容转换为 UCS4String 并浪费 2-4 倍的内存,不如将其保留为 UnicodeString 并循环通过它寻找代理并在需要时将它们转换为实体.查看System.Character 函数,如IsSurrogatePair() 和ConvertToUtf32()。
  • @DavidHeffernan 没错,但这并不重要,因为 XML 解析器无论如何都会将其转换为内部 Delphi 表示(Delphi 7 的 WideString),不是吗?
  • @RemyLebeau 感谢您的建议,我已经相应地更新了答案。
  • 我重构了代码以在 ANSI 和 Unicode Delphi 版本中工作。
猜你喜欢
  • 2011-07-18
  • 1970-01-01
  • 2015-09-19
  • 2010-10-19
  • 2020-09-23
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 2019-05-15
相关资源
最近更新 更多