如何正确处理 UTF-8 XML 中的?

How do I properly handle  in UTF-8 XML?

我在表示  时遇到了一些问题,这是一个有效的 UTF-16 结构,而且显然在 Windows 文件名中有效,在 XML 中用于在 [=36= 中使用] 服务器 XML (2012).

以此为例:

DECLARE @Xml xml;
SET @Xml = N'<?xml version="1.0"?><FileName>풜〣&#xFFFF;&#xFFFF;</FileName>'

-- Result: XML parsing: line 1, character 41, illegal xml character

然而,这是合法的 Unicode("Unicode Noncharacter" ''):http://www.fileformat.info/info/unicode/char/ffff/index.htm

所以,我尝试了这个:

DECLARE @Xml xml;
SET @Xml = N'<?xml version="1.0" encoding="utf-16"?><FileName>풜〣&#xFFFF;&#xFFFF;</FileName>'

-- Result: XML parsing: line 1, character 59, illegal xml character

那么 - 我应该如何在 XML 中准确地表示此文件名?我不能只删除字符,它们确实是 Get-ChildItem 报告的 &#xFFFF; 个字符,我需要保留此文件的句柄。

我可以通过将 &#xFFFF; 替换为 &#xEF;&#xBF;&#xBF; 来使 XML 解析工作,&#xEF;&#xBF;&#xBF; 根据 this link\uFFFF 的 UTF-8 表示然后我尝试将此 XML 插入到 nvarchar 列,我需要它作为文件名的正确表示。

DECLARE @Xml xml;
SET @Xml = N'<?xml version="1.0"?><FileName>풜〣&#xEF;&#xBF;&#xBF;&#xEF;&#xBF;&#xBF;</FileName>'
SELECT F.Item.value('.', 'nvarchar(2000)') FROM @Xml.nodes('//FileName') as F(Item)

-- Returns 풜〣ï¿¿ï¿¿ (not correct)

XML 文档中允许的字符集由 XML 规范的 production 2 定义。它不包括 U+FFFF(它被 Unicode 定义为非字符,并且在 XML 开发时不允许使用 Unicode 进行信息交换)。

这意味着您不能在 XML 文档中按字面意思表示 U+FFFF,也不能使用 XML 数字字符引用。您当然可以发明自己的转义机制,或使用 URI 转义之类的方法来对数据中的字符进行编码;在将数据插入允许 U+FFFF 的应用程序之前,您当然必须对您的表示进行转义。

我想知道为什么 Windows 文件名中允许非字符。

&#xFFFF;(即十进制 65535)不是合法字符,即使根据 [=59= 问题中提供的 link ]. link 表明它是非法的、非字符的,并且不能以任何方式表示(根据他们的测试页)。

另外,根据Unicode.org

Noncharacters
These codes are intended for process-internal uses.

FFFE ￾ <not a character>
• may be used to detect byte order by contrast with FEFF 
→ FEFF = zero width no-break space

FFFF ￿ <not a character>

而且,根据 W3C,valid characters 的列表是:

#x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
/* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */


为了将其转换为 XML(至少在 SQL 服务器 XML 数据类型方面),您需要首先替换任何出现的 &#xFFFE;&#xFFFF; 使用自定义转义序列,例如 \uFFFE;\uFFFF; 分别。然后,当转换回 NVARCHAR 时,您只需分别将 \uFFFE; 替换为 NCHAR(65534) 并将 \uFFFF; 替换为 NCHAR(65535)

,您可以对值进行 Base64 编码(在应用程序代码端相当容易)并在输出时解码。如果您需要在数据库端访问它,您可以创建自己的 SQLCLR 函数来进行 Base64 编码和解码,或者只获取 SQL# 库的免费版本(我是) 其中包括 Convert_ToBase64Convert_FromBase64 并且可以按如下方式使用:

DECLARE @Encoded NVARCHAR(200),
        @Decoded NVARCHAR(200);

SET @Encoded =
    SQL#.Convert_ToBase64(CONVERT(VARBINARY(200), N'f' + NCHAR(65535) + N'g'), 'None');

SELECT CONVERT(XML, N'<test>' + @Encoded + N'</test>');

SET @Decoded = SQL#.Convert_FromBase64(@Encoded);
SELECT @Encoded AS [Encoded],
       @Decoded AS [Decoded],
       DATALENGTH(@Decoded) AS [NumBytes], -- 6 bytes = 3 characters (most of the time)
       UNICODE(SUBSTRING(@Decoded, 2, 1)) AS [TaDa!] -- get value of middle character

Returns:

<test>ZgD//2cA</test>

然后:

Encoded     Decoded     NumBytes    TaDa!
ZgD//2cA    fg          6           65535

它似乎不应该用作文件名的一部分(我知道这不是你做的),或者它可能确实是一个有效字符,但被 Get-ChildItem 错误地读取了。

I will need to retain a handle to this file.

是否可以在将 names/info 导入 SQL 服务器之前重命名文件以删除无效字符?只是一个想法。


仅供参考,您不能通过 xml 声明更改编码,至少不是那么容易: