某些背景-我从UTF-8中的json网站接收到响应数据。 json的body属性具有base64binary类型的值,我将其存储为ms sql服务器上的nvarchar类型。
当我将base64binary数据转换为varchar或nvarchar时,我看到有趣的字符(代替双引号),表明存在编码问题,这就是此问题的原因。
下面,我在解释解剖的代码,在底部,您可以看到一个可运行的示例和相关问题。
转换不好,注意有趣的人物。
例如代表IRB Holding Corp( – 公司”)
下面的示例查询解决了上述问题。它将数据转换为xml中的可读文本,我看到应有的引号,但现在在包含'&'的行上失败,因为这是xml中的特殊字符。
select convert(xml, '<?xml version="1.0" encoding="UTF-8"?>' + convert(varchar(max),cast('' as xml).value('xs:base64Binary(sql:column("body"))','varbinary(max)')))
最后,在下面的查询中,我通过替换语句解决了该问题,并且能够按预期完全看到所有行。但是此解决方案只能处理'&'。我担心如果行在xml中还有其他特殊字符(如<,>等)
,代码将会中断。问题-解决此问题的唯一方法就是添加更多替换语句。
要运行的示例代码:
declare @t table ( [body] nvarchar(max) )
insert into @t(body)
select 'REFMTEFTLCBUWCDigJMgTWF5IDcsIDIwMTkg4oCTIENvdmV5ICYgUGFyayBFbmVyZ3kgSG9sZGluZ3MgTExDICjigJxDb3ZleSBQYXJr4oCdIA=='
select convert(varchar(max),cast('' as xml).value('xs:base64Binary(sql:column("body"))','varbinary(max)'))
, convert(xml, '<?xml version="1.0" encoding="UTF-8"?>'+ replace(convert(varchar(max),convert(varchar(max),cast('' as xml).value('xs:base64Binary(sql:column("body"))','varbinary(max)'))),'&','&'))
from @t
答案 0 :(得分:3)
XML技巧很好用,只是让XML引擎处理字符实体:
declare @t table ([body] nvarchar(max));
insert into @t(body)
values ('REFMTEFTLCBUWCDigJMgTWF5IDcsIDIwMTkg4oCTIENvdmV5ICYgUGFyayBFbmVyZ3kgSG9sZGluZ3MgTExDICjigJxDb3ZleSBQYXJr4oCdIA==');
select
cast(
cast('<?xml version="1.0" encoding="UTF-8"?><root><![CDATA[' as varbinary(max))
+
CAST('' as xml).value('xs:base64Binary(sql:column("body"))', 'VARBINARY(MAX)')
+
cast(']]></root>' as varbinary(max))
as xml).value('.', 'nvarchar(max)')
from
@t;
这里的重要部分是:
N
的 encoding="UTF-8"
varbinary
即可得到有效的UTF-8 <![CDATA]]>
块。请注意,它仍然不过是黑客。涉及XML时,您将受到XML的限制,并且如果您的字符串包含characters not representable in XML,则该类型的XML转换将失败
XML解析:第1行,字符54,非法的xml字符
答案 1 :(得分:1)
尝试此功能
CREATE FUNCTION dbo.Convert_utf8(@utf8 VARBINARY(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE @rslt NVARCHAR(MAX);
SELECT @rslt=
CAST(
--'<?xml version="1.0" encoding="UTF-8"?><![CDATA['
0x3C3F786D6C2076657273696F6E3D22312E302220656E636F64696E673D225554462D38223F3E3C215B43444154415B
--the content goes within CDATA
+ @utf8
--']]>'
+ 0x5D5D3E
AS XML).value('.', 'nvarchar(max)');
RETURN @rslt;
END
GO
并这样称呼
SELECT *
,dbo.Convert_utf8(CAST(t.body AS XML).value('.','varbinary(max)'))
FROM @t t;
结果是
DALLAS, TX – May 7, 2019 – Covey & Park Energy Holdings LLC (“Covey Park”
GSerg,非常感谢!在下面的回答。我尝试并简化了它,使其可以在UDF中使用。
看起来varbinary(max)
到XML的转换完全在CLR环境中完成,其中考虑了XML的编码声明。这似乎也可以与其他编码一起使用,但是我现在没有时间进行通用测试。
它包含一些有关字符串编码的背景知识,可能值得一读。
我简化了您的代码:
declare @t table ( [body] nvarchar(max) )
insert into @t(body)
select 'REFMTEFTLCBUWCDigJMgTWF5IDcsIDIwMTkg4oCTIENvdmV5ICYgUGFyayBFbmVyZ3kgSG9sZGluZ3MgTExDICjigJxDb3ZleSBQYXJr4oCdIA==';
SELECT CAST(t.body AS XML).value('.','varbinary(max)')
,CAST(CAST(t.body AS XML).value('.','varbinary(max)') AS VARCHAR(MAX))
FROM @t t;
您将看到此结果
0x44414C4C41532C20545820E28093204D617920372C203230313920E2809320436F7665792026205061726B20456E6572677920486F6C64696E6773204C4C432028E2809C436F766579205061726BE2809D20
DALLAS, TX – May 7, 2019 – Covey & Park Energy Holdings LLC (“Covey Parkâ€
我将把第一个字符放在读者更友好的位置
0x44414C4C41532C20545820E28093
D A L L A S , T X â € “
0x44
是D
,两倍的0x4C
是两倍的LL
,在空格0x20
之后,我们到达E28093
。这是3-byte encoded code point for the en dash。 SQL-Server将无法为您提供帮助...它将解释为每个1个字节的3个字符...
恐怕你运气不好...
SQL-Server不支持utf-8
字符串。 BCP / BULK
仅支持从文件系统输入,但 之内的字符串T-SQL
必须是两个受支持的选项之一:
(var)char
,是扩展的ASCII 。它严格是每个字符一个字节,并且需要排序规则来处理有限的一组外国字符。n(var)char
,它是 UCS-2 (非常类似于UTF-16
)。严格来说,它是每个字符两个字节,并且将以几乎两倍的内存大小来编码(几乎)任何已知字符。 UTF-8
与(var)char
兼容,只要我们坚持使用 plain latin 和一字节代码即可。但是,任何高于127的ASCII码都会导致麻烦(使用正确的排序规则可能会起作用)。但是-这是您的情况-您的字符串使用多字节代码点。 UTF-8
将为一个字符编码两个或更多字节(最多4个!)的许多字符。
您将不得不使用一些能够处理UTF-8的引擎
然后-thx到@GSerg-另外两个选项:
utf-8
本地支持您可以按一种或另一种方式使用数据库,可以按原样保存存储数据或工作数据。将图片存储为VARBINARY(MAX)
只是一小部分。您不会尝试使用SQL Server执行图像识别。
这与文本数据相同。如果您只存储一小段文本,则无关紧要。但是,如果要使用此文本进行筛选,搜索,或者要使用SQL Server显示此文本,则必须考虑格式和性能需求。
带有可变字节长度的包围将不允许简单的SUBSTRING('blahblah',2,3)
。在固定长度的情况下,引擎可以只将字符串作为数组,跳到第二个索引并选择接下来的三个字符。但是对于可变字节,如果可能存在任何多字节代码点,引擎将必须通过检查所有字符来计算索引。这将极大地减慢许多字符串方法的速度。
最好是,不要以某种格式存储数据,SQL-Server无法处理(很好)...
答案 2 :(得分:0)
如果你有 SQL server 2019,你可以创建另一个以 UTF8 作为默认排序规则的数据库,并在那里创建简单的函数:
USE UTF8_DATABASE
GO
CREATE OR ALTER FUNCTION dbo.VarBinaryToUTF8
(@UTF8 VARBINARY(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
RETURN CAST(@UTF8 AS VARCHAR(MAX));
END;
你会打电话
SELECT
UTF8_DATABASE.dbo.VarBinaryToUTF8
(
CAST('' as xml).value('xs:base64Binary(sql:column("body"))','varbinary(max)')
)
FROM
@t
这是可行的,因为 SQL Server 对其变量和函数返回值使用特定数据库的默认排序规则。您必须将结果存储到 NVARCHAR
或 UTF8
整理的 'VARCHAR in your non-
UTF8` 数据库中。