代码之家  ›  专栏  ›  技术社区  ›  Ehryk

如何正确处理UTF-8 XML格式?

  •  2
  • Ehryk  · 技术社区  · 10 年前

    我在代表  ,是一个有效的UTF-16构造,在Windows文件名中显然也是有效的,在SQL Server XML中使用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非字符”“”): 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准确地表示这个文件名呢?我不能只是删除这些字符,它们确实是 &#xFFFF; 字符,我需要保留此文件的句柄。

    我可以通过替换 &#xFFFF; 具有 &#xEF;&#xBF;&#xBF; 它是的UTF-8表示 \uFFFF 根据 this link 然后我尝试获取此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)
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   C. M. Sperberg-McQueen    10 年前

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

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

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

        2
  •  1
  •   Community CDub    7 年前

    &#xFFFF; (即十进制65535)为 法律性质,即使根据问题中提供的链接“然而,这是合法的UTF-16”。该链接显示它是非法的、非字符的,并且不能以任何方式表示(根据他们的测试页面)。

    此外,根据 Unicode.org :

    非字符
    这些代码用于工艺内部使用。

    燃料电池 <不是字符>
    可用于通过与FEFF的对比来检测字节顺序
    FEFF=零宽度无中断空间

    FFFF公司 <不是字符>

    根据W3C valid characters 是:

    #x9 |#xA |#xD |[x20-#xD7FF]|[#xE000-#xFFFD]|[#x10000-#xFFFF]
    /*任何Unicode字符,不包括代理块FFFE和FFFF*/


    为了将其转换为XML(至少在SQL Server XML数据类型方面),需要首先替换 &#xFFFE; &#xFFFF; 使用自定义转义序列,例如 \uFFFE; \uFFFF; 切除。然后,当转换回NVARCHAR时,您可以简单地替换 \uFFFE; 具有 NCHAR(65534) \uFFFF; 具有 NCHAR(65535) 分别地

    ,您可以对值进行Base64编码(在应用程序代码端相当容易),并在退出时进行解码。如果您需要在数据库端访问它,您可以创建自己的SQLCLR函数来Base64编码和解码,或者只获取 SQL# 图书馆(我是其作者),包括 Convert_ToBase64 Convert_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
    

    退货:

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

    然后:

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

    它似乎不应该作为文件名的一部分使用(我知道这不是你做的),或者它确实是一个有效的字符,被 Get-ChildItem .

    我需要保留此文件的句柄。

    在将名称/信息导入SQL Server之前,是否可以重命名文件以删除无效字符?只是一个想法。


    仅供参考,您不允许通过xml声明更改编码,至少不太容易: SQL Server 2008R2 and creating XML document