【问题标题】:How can I replace all XML escape sequences with the unicode in SQL Server nvarchar?如何用 SQL Server nvarchar 中的 unicode 替换所有 XML 转义序列?
【发布时间】:2015-05-15 09:06:20
【问题描述】:

我使用的是 SQL Server 2012,并且我有一个 nvarchar,它具有 UTF 字符的 XML 转义表示,因为它们存在于文件名中(最值得注意的是 \uFFFF)。

如何运行 nvarchar 并将所有这些匹配项替换为其实际的 Unicode 字符?

这可行,但我必须列举每一个,这并不可行(无效:[#x0-#x8]|#xB|#xC|[#xE-#x1F]|[#x7F-#x84]|[#x86-#x9F]|[#xD800-#xDFFF]|[#xFDD0-#xFDEF]|#xFFFE|#xFFFF|[#x10FFFF-Up]

DECLARE @string nvarchar(2000)
SET @string = N''
SELECT @string,
REPLACE(REPLACE(REPLACE(
REPLACE(REPLACE(REPLACE(
    @string, 
'', NCHAR(0xFFFF)), '', NCHAR(0xFFFE)), '�', NCHAR(0x0000)),
'', NCHAR(65535)), '', NCHAR(65534)), '�', NCHAR(0))

如何一次更换所有这些? (******; => NCHAR(0x******) 和 ******; => NCHAR(******)),理想情况下没有 CLR功能。

【问题讨论】:

  • 这样做的原因是将文件名批量插入到 SQL Server 表中。对于批量插入,XML 可以直接传递给存储过程,SS 可以对其进行本地解析,但 XML 不能包含 Windows 文件名和 C# 字符串中有效的所有字符(例如\uFFFF)。跨度>

标签: sql-server xml unicode sql-server-2012 nvarchar


【解决方案1】:

翻译&#xFFFF之类的无效序列似乎没有多大意义,但是如果您通过REPLACE去除无效序列,则可以通过转换为XML,然后一次性翻译有效序列再次返回 NVARCHAR:

DECLARE @string NVARCHAR(2000);
SET @string = N'<test>&#xF0F0;&#5535;</test>';
SELECT @string AS [Original],
       CONVERT(XML, @string) AS [ConvertedToXml],
       CONVERT(NVARCHAR(2000), CONVERT(XML, @string)) AS [ConvertedToXmlThenToNVarChar]

返回:

Original                        ConvertedToXML     ConvertedToXmlThenToNVarChar
<test>&#xF0F0;&#5535;</test>    <test>ᖟ</test>    <test>ᖟ</test>

但是如果你不先用''(空字符串)替换无效序列,这会出错。


更新:

关于此主题的另一个问题 (How do I properly handle &#xFFFF; in UTF-8 XML?),您可以执行类似于以下操作的操作,将无效字符转换为可以在输出时转换的自定义转义序列:

DECLARE @Original NVARCHAR(2000),
        @TempXml XML,
        @StoredAsNVarChar NVARCHAR(2000),
        @Extracted NVARCHAR(2000);

SET @Original = N'<FileName>&#xF0F0;풜〣&#xFFFF;&#xFFFF;</FileName>';
SET @Original = REPLACE(@Original, N'&#xFFFF;', N'\uFFFF;');

SET @TempXml = CONVERT(XML, @Original);

SET @StoredAsNVarChar = CONVERT(NVARCHAR(2000), @TempXml);

SET @Extracted = REPLACE(@StoredAsNVarChar, N'\uFFFF;', NCHAR(65535));

SELECT @Original AS [OriginalAfterTranslatingInvalidCharacters],
       @TempXml AS [ConvertedOriginalToXml],
       @StoredAsNVarChar AS [ConvertedXmlBackToNVarChar],
       @Extracted AS [ExtractedAndTranslatedBackToInvalidCharacters];

不过,我还是建议先尝试重命名文件,使它们一开始就没有无效的 Unicode 字符,然后再导入 SQL Server。考虑到文件名中的无效字符,我无法想象这将是您在使用这些文件时将面临的唯一问题。正如我在另一个问题中提到的那样,您确定该名称不会被 PowerShell 误报吗?您是否可以在 C# 或 VB.Net 中编写一个小型控制台应用程序,以利用 DirectoryInfo 列出目录中的文件?


更新 2:

基于另一个问题(上面链接)中的讨论(在 cmets 中),现在可以理解,需要回答这个问题与使用 XML 作为传输机制来批量插入文件信息有关。虽然 XML 可用于为此目的发送数据数组,但更有效的方法是表值参数(在 SQL Server 2008 中引入),它是在数据库端显示为表变量的强类型集合.我在以下答案中提供了一个正确执行此操作的示例(使用大多数人倾向于使用的DataTable不是正确的方式):

Pass Dictionary<string,int> to Stored Procedure T-SQL

该答案中有一个链接指向另一个答案,我在其中提供了另一个使用 TVP 将数据流式传输到 SQL Server 的示例。

【讨论】:

  • 我这样做的重点是保留“无效”的 XML 实体 - 特别是 &amp;#xFFFF;。这不能作为 unicode 字符 (\uFFFF) 存储在 nvarchar 中吗?
  • 我在 Windows 文件系统上有一个文件,以及一个文件数据库。 PowerShell 的 Get-ChildItem 报告的文件的实际名称是 풜〣&amp;#xFFFF;&amp;#xFFFF;(在 cmd 目录中显示为 ????,其中有四个字符)。见:stackoverflow.com/questions/29022169/…
  • @Ehryk 是的,它可以存储为NVARCHAR,但代码点 65535 / xFFFF 仍然不是有效字符。我将更新另一个建议,反映您在其他 question 中讨论的关于此主题的内容。
  • 你能帮我用一些术语吗? “字符+非字符”是什么词,两者都可以清楚地用于 Windows 文件名并具有 Unicode 定义?我一直在为这些实例使用“字符”,并且不知道在文件名/U​​nicode 中还有什么可以调用“类似字符的东西”。
  • @Ehryk 在我的答案中查看“更新 2”,以获取传入数组的 XML 方法的替代方法。
【解决方案2】:

我一直在寻找一种通用解决方案,它可以在 NVARCHAR 中处理所有无效和有效的 XML 转义序列,并将它们替换为字符值。我写了这篇文章,因为我找不到一个不涉及用数千个 REPLACE 语句(至少 65535 个)枚举每个语句并让它工作的例子。如果有更好的方法(非 CLR),我想知道。

/* Converts '0F0F' to 0x0F0F */
CREATE FUNCTION dbo.GetHex(@input nvarchar(max))
RETURNS varbinary(max)
AS
BEGIN
    RETURN CONVERT(varbinary(max), @input, 2)
END

GO

/* Converts '0F0F' to the nchar \u0F0F */
CREATE FUNCTION dbo.GetNcharHex(@input nvarchar(max))
RETURNS nchar(1)
AS
BEGIN
    RETURN NCHAR(dbo.GetHex(@input))
END

GO

/* Converts '123' to the nchar \u7B (decimal 123 in hex) */
CREATE FUNCTION dbo.GetNcharDec(@input nvarchar(max))
RETURNS nchar(1)
AS
BEGIN
    RETURN NCHAR(CONVERT(int, @input))
END

GO

/* Replaces a group of @n nchars inside @prefix and @suffix with the of the @n nchars interpreted as hexadecimal; if @prefix = '&#x', @suffix = ';', and @n = 2 then it will replace all 2 digit hex XML entities: e.g. '&#x52;' with 'R' */
CREATE FUNCTION dbo.ReplaceNGroupsHex(@input nvarchar(max), @prefix nvarchar(max), @group nvarchar(max), @suffix nvarchar(max), @n int = 2)
RETURNS nvarchar(max)
AS
BEGIN
    DECLARE @pattern nvarchar(max), @location int
    SET @pattern = '%' + @prefix + REPLICATE(@group, @n) + @suffix + '%'

    WHILE(1=1)
    BEGIN
        SET @location = PATINDEX(@pattern, @input)
        IF (@location = 0) BREAK
        SET @input = REPLACE(@input, SUBSTRING(@input, @location, LEN(@prefix) + @n + LEN(@suffix)), dbo.GetNcharHex(SUBSTRING(@input, @location + LEN(@prefix), @n)))
    END
    RETURN @input
END

GO

/* Replaces a group of @n nchars inside @prefix and @suffix with the of the @n nchars interpreted as decimal; if @prefix = '&#', @suffix = ';', and @n = 2 then it will replace all 2 digit decimal XML entities: e.g. '&#33;' with '!' */
CREATE FUNCTION dbo.ReplaceNGroupsDec(@input nvarchar(max), @prefix nvarchar(max), @group nvarchar(max), @suffix nvarchar(max), @n int = 1)
RETURNS nvarchar(max)
AS
BEGIN
    DECLARE @pattern nvarchar(max), @location int
    SET @pattern = '%' + @prefix + REPLICATE(@group, @n) + @suffix + '%'

    WHILE(1=1)
    BEGIN
        SET @location = PATINDEX(@pattern, @input)
        IF (@location = 0) BREAK
        SET @input = REPLACE(@input, SUBSTRING(@input, @location, LEN(@prefix) + @n + LEN(@suffix)), dbo.GetNcharDec(SUBSTRING(@input, @location + LEN(@prefix), @n)))
    END
    RETURN @input
END

GO

/* Replaces all Hexadecimal XML entities of @n length or lower (grouped in pairs); @n = 4 will result in '&#x0041;&#x41;' => 'AA', and @n = 2 will result in '&#x0041;&#x41;' => '&#x0041;A' */
CREATE FUNCTION dbo.ReplaceHexEntities(@input nvarchar(max), @n int = 4)
RETURNS nvarchar(max)
AS
BEGIN
    WHILE(@n > 0)
    BEGIN
        SET @input = dbo.ReplaceNGroupsHex(@input, N'&#x', '[0-9,A-F,a-f]', ';', @n)
        SET @n = @n - 2
    END
    RETURN @input
END

GO

/* Replaces all Decimal XML entities of @n length or lower; @n = 5 will result in '&#65514;&#74;' => '↑??J', and @n = 2 will result in '&#118;&#70;' => '&#118;F' */
CREATE FUNCTION dbo.ReplaceDecEntities(@input nvarchar(max), @n int = 5)
RETURNS nvarchar(max)
AS
BEGIN
    WHILE(@n > 0)
    BEGIN
        SET @input = dbo.ReplaceNGroupsDec(@input, N'&#', '[0-9]', ';', @n)
        SET @n = @n - 1
    END
    RETURN @input
END

GO

/* Replaces all XML Entities up to: \uFFFF (Hex) and \u1869F (Decimal 99999) */
CREATE FUNCTION dbo.ReplaceEntities(@input nvarchar(max))
RETURNS nvarchar(max)
AS
BEGIN
    SET @input = dbo.ReplaceHexEntities(@input, DEFAULT)
    SET @input = dbo.ReplaceDecEntities(@input, DEFAULT)
    RETURN @input
END

GO

SELECT dbo.GetHex('00FFAA')
SELECT dbo.ReplaceNGroupsDec(N'z&#xFFFF;&#x0042;&#65535;', '&#', '[0-9]', ';', 5)
SELECT dbo.ReplaceHexEntities(N'z&#xFFFF;&#x0042;&#65535;', DEFAULT)
SELECT dbo.ReplaceDecEntities(N'z&#xFFFF;&#x0042;&#65535;&#69;', DEFAULT)
SELECT dbo.ReplaceEntities(N'z&#xFFFF;&#x0042;&#65535;&#69;'), LEN(dbo.ReplaceEntities(N'z&#xFFFF;&#x0042;&#65535;&#69;'))

【讨论】:

    猜你喜欢
    • 2022-01-19
    • 1970-01-01
    • 2013-05-04
    • 2018-04-23
    • 2013-05-24
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 2011-05-29
    相关资源
    最近更新 更多