如何使用 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 实体,如 '&#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 字符串
如何将 utf-16 字符放入 Android 字符串资源中?