如何使用 XML/SGML 实体将 UTF-16 转换为 ASCII/ANSI?

Posted

技术标签:

【中文标题】如何使用 XML/SGML 实体将 UTF-16 转换为 ASCII/ANSI?【英文标题】:How to convert UTF-16 to ASCII/ANSI with XML/SGML entities? 【发布时间】:2014-09-30 02:03:36 【问题描述】:

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 有什么问题?我看不到任何需要解码的内容? 【参考方案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 版本中工作。

以上是关于如何使用 XML/SGML 实体将 UTF-16 转换为 ASCII/ANSI?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 将 powershell 脚本编码为 base64 UTF16-LE 字符串

如何将 xml 返回为 UTF-8 而不是 UTF-16

如何将 utf-16 字符放入 Android 字符串资源中?

如何将 UTF-8 std::string 转换为 UTF-16 std::wstring?

将 UTF-16 图像加载到内存中

PHP转换emoji表情为HTML字符实体