2015-03-13 9 views
1

Я использую SQL Server 2012, и у меня есть nvarchar, у которого есть XML-экранированные представления символов UTF, потому что они присутствуют в имени файла (наиболее заметно).Как заменить все escape-последовательности XML на unicode в SQL Server nvarchar?

Как я могу запустить nvarchar и заменить все эти вхождения на свои действительные символы Unicode?

Это работает, но я должен был бы перечислить каждый из них, и это на самом деле не представляется возможным (Invalid: [#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)) 

Как я могу заменить их все сразу? (& #x ******; => NCHAR (0x ******) и & # ******; => NCHAR (******)), в идеале без CLR функция.

+0

Причиной этого является выполнение массового ввода имен файлов в таблицу SQL Server. Для массивных вставок XML может быть передан непосредственно в хранимую процедуру, и SS может изначально выполнить синтаксический анализ, но XML не может содержать все символы, которые действительны в именах файлов Windows и строках C# (например, '\ uFFFF'). – Ehryk

ответ

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> 

Но это будет ошибкой, если вы не первым заменить недопустимые последовательности с '' (пустой строкой).


UPDATE:

Относительно другой вопрос по этой теме (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 в них, чтобы начать с, а затем важем t в SQL Server. Я не могу себе представить, что это будет единственная проблема, с которой вам придется столкнуться с этими файлами, учитывая недопустимые символы в имени файла. И как я упомянул в другом вопросе, вы уверены, что имя не так или иначе не сообщается PowerShell? Можете ли вы написать небольшое консольное приложение на C# или VB.Net, которое может использовать DirectoryInfo для перечисления файлов в каталоге?


UPDATE 2:

На основе обсуждения (в комментариях) в другой вопрос (связанный выше), сейчас стало понятно, что нужно для этого, чтобы ответить относится к использованию XML в качестве транспортный механизм для массовой вставки информации о файле. Хотя XML может использоваться для отправки в массив данных для этой цели, более эффективным средством являются параметры таблицы (введенные в SQL Server 2008), который представляет собой строго типизированную коллекцию, которая отображается как переменная таблицы на стороне базы данных ,Я обеспечиваю пример делать это правильно (с использованием DataTable, что большинство людей склонны использовать, является не надлежащим образом) в следующем ответе:

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

И есть ссылка в этом ответе на другой ответ, где я представил другой пример использования TVP для потоковой передачи данных в SQL Server.

+0

Вся моя задача - сохранить «недопустимые» XML-объекты - в частности '& # xFFFF;'. Это невозможно сохранить в nvarchar как символ юникода (\ uFFFF)? – Ehryk

+0

У меня есть файл в файловой системе Windows и база данных файлов. Фактическое имя файла, о котором сообщает PowerShell's Get-ChildItem, - '풜 〣 & # xFFFF; & # xFFFF;' (отображается в каталоге cmd как '????', в нем четыре символа). См. Http://stackoverflow.com/questions/29022169/how-do-i-properly-handle-xffff-in-utf-8-xml – Ehryk

+0

@ Ehryk да, его можно сохранить как 'NVARCHAR', но код- точка 65535/xFFFF по-прежнему не является допустимым символом. Я обновлю другое предложение, отражающее то, что обсуждается в вашем другом [вопрос] (http://stackoverflow.com/questions/29022169/how-do-i-properly-handle-xffff-in-utf-8-xml/ 29036559 # 29036559) по этой теме. –

0

Я искал универсальное решение, которое обрабатывало все последовательности исключений XML, недействительные и допустимые, в NVARCHAR и заменяло их символьными значениями. Я написал это, так как не мог найти пример, который не включал перечисление каждого из них с последующими тысячами 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;'))