MsSql

关注公众号 jb51net

关闭
首页 > 数据库 > MsSql > SQL image转汉字乱码

SQL image转汉字乱码处理的问题解决

作者:焚 城

在SQLServer中遇到image字段存储汉字出现乱码和Unicode错误,本文就来介绍一下SQL image转汉字乱码处理,感兴趣的可以了解一下

前言

有一个小case ,从其他项目数据中取一个字段。
然后发现,那个本来是汉字的字段的存储格式是image
先后经历了:

1、解析成汉字
2、前端显示、excel导出,出错。
Unable to translate Unicode character \uDE8D at index 9 to specified code page.
3、前端显示、excel导出 有乱码

当然最后解决了,这里记录一下

一、解析Image成汉字

拿到字段的时候我是懵逼的,怎么把汉字存成二进制。
解析试试:

 SELECT  TOP 10
 CAST(CAST(Remarks AS VARBINARY(MAX)) AS NVARCHAR(MAX))  AS Remarks  
 FROM  TabelA

突破点

机智的我发现了规律 前面和后面都一样,截取看看

SELECT
TOP 10
  CAST(
			SUBSTRING(
				CAST(Remakes AS VARBINARY(MAX)), 
					4,DATALENGTH(CAST(Remakes AS VARBINARY(MAX)))-6
			) AS NVARCHAR(MAX)
		) as  Remakes
 FROM TabelA

完美

二、 前端Unicode 报错

查询分析器,查出来正常了,
但前端显示、excel导出,报错:
Unable to translate Unicode character \uDE8D at index 9 to specified code page.
找个过滤无效Unicode的方法

Create FUNCTION [dbo].[CleanSurrogateCharacters](@Input NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
     -- 处理NULL值
    IF @Input IS NULL OR @Input = N'' RETURN N'';

	
    --代理字符(U+D800 - U+DFFF)
    DECLARE @SurrogatePattern NVARCHAR(100) = N'%[' + NCHAR(0xD800) + '-' + NCHAR(0xDFFF) + N']%'
    WHILE PATINDEX(@SurrogatePattern, @Input COLLATE Latin1_General_BIN2) > 0
    BEGIN
        SET @Input = STUFF(@Input, PATINDEX(@SurrogatePattern, @Input COLLATE Latin1_General_BIN2), 1, N'')
    END

    -- 通用特殊字符清理(基础乱码)
    SET @Input = REPLACE(@Input, NCHAR(0x00), N''); -- 空字符
    SET @Input = REPLACE(@Input, NCHAR(0xFF), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x01), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x02), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x03), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x04), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x05), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x06), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x07), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x08), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x09), N''); -- 制表符(按需保留,若不需要则保留此行)
    SET @Input = REPLACE(@Input, NCHAR(0x0A), N''); -- 换行符(按需保留)
    SET @Input = REPLACE(@Input, NCHAR(0x0B), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x0C), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x0D), N''); -- 回车符(按需保留)
    SET @Input = REPLACE(@Input, NCHAR(0x0E), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x0F), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x10), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x11), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x12), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x13), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x14), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x15), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x16), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x17), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x18), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x19), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x1A), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x1B), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x1C), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x1D), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x1E), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x1F), N'');
    SET @Input = REPLACE(@Input, NCHAR(0x7F), N'');
	 SET @Input = REPLACE(@Input, NCHAR(127), N''); -- ASCII控制字符
    SET @Input = REPLACE(@Input, NCHAR(0xFEFF), N''); -- BOM标记
    SET @Input = REPLACE(@Input, NCHAR(0x200B), N''); -- 零宽空格
    SET @Input = REPLACE(@Input, NCHAR(0x3000), N''); -- 全角空格
    SET @Input = REPLACE(@Input, NCHAR(0xD800), N'');
    SET @Input = REPLACE(@Input, NCHAR(0xDC00), N'');


    -- 最终去首尾空白,避免残留无效字符
    SET @Input = LTRIM(RTRIM(@Input));

    RETURN @Input;

END;
SELECT
TOP 10
 dbo.CleanSurrogateCharacters(--过滤无效Unicode
  CAST(
			SUBSTRING(
				CAST(Remakes AS VARBINARY(MAX)), 
					4,DATALENGTH(CAST(Remakes AS VARBINARY(MAX)))-6
			) AS NVARCHAR(MAX)
		)) as  Remakes
 FROM TabelA

过滤一下,前端能显示和导出了。

三、部分内容 后面带乱码

无法显示和导出的问题解决了,但是又出现了乱码

这错误吧,
你说它错了,也不完全错。
它只是在正确内容后面加了一段乱码

找几个错误典型出来

0x073F6239652875CF910000004D004B0020006F00720064006500720020006400610074006500200031002F00320039003A0020004600320036002D0057006F006D0065006E002D004100430043002D004F00550054004C00450054002D00530055005000450052004C0020004D004F004E005400450052004500590020005000480049004C0049005000500049004E00450053002D004D0041004B0045005200200031003300370033002D00440031002D004D00490046003A0032003000320036002F0033002F00310035000000FF
应该'改用量'
实际'改用量䴀䬀 漀爀搀攀爀 搀愀琀攀 ㄀⼀㈀㤀㨀 䘀㈀㘀ⴀ圀漀洀攀渀ⴀ䄀䌀䌀ⴀ伀唀吀䰀䔀吀ⴀ匀唀倀䔀刀䰀 䴀伀一吀䔀刀䔀夀 倀䠀䤀䰀䤀倀倀䤀一䔀匀ⴀ䴀䄀䬀䔀刀 ㄀㌀㜀㌀ⴀ䐀㄀ⴀ䴀䤀䘀㨀㈀㈀㘀⼀㌀⼀㄀㔀'
0x07FD00B0653E6B65512875CF9100000052004500320036000000FF
应该'新款入用量'
实际'新款入用量刀䔀㈀㘀'
0x07FD00A25BBA4EF466396500000032002F00320031002F0032003000320036003A0020004600690072006D00200050006C0061006E000000FF
应该'客人更改'
实际'客人更改㈀⼀㈀㄀⼀㈀㈀㘀㨀 䘀椀爀洀 倀氀愀渀'

突破点

我发现 他们都包含‘00000’ 大胆猜测,小心求证
以上面第二条 0x07FD00B0653E6B65512875CF91 00000 052004500320036000000FF 为例

DECLARE @a VARBINARY(MAX);
SET @a = CONVERT(VARBINARY(MAX), '0x07FD00B0653E6B65512875CF91', 1);
SELECT CONVERT( NVARCHAR(MAX),
           SUBSTRING(@a,4,len(@a))
        ) 

验证正确! 取'00000'前面那段有效的就行

五、实现截取掉乱码部分

DECLARE @a VARBINARY(MAX);
SET @a = CONVERT(VARBINARY(MAX), '0x07FD00B0653E6B65512875CF9100000052004500320036000000FF', 1);
SELECT CHARINDEX('000000',CONVERT(VARCHAR(MAX), @a, 2))-1
DECLARE @b int =CHARINDEX('000000',CONVERT(VARCHAR(MAX), @a, 2))-1
SELECT  SUBSTRING(@a,4,@b)

期望是
0xB0653E6B65512875CF91
结果是
0xB0653E6B65512875CF9100000052004500320036000000FF

这结果不对呀,比想截取的部分长了一倍,
等等,一倍?
莫非是因为二进制,一个字节=2个字符。

DECLARE @a VARBINARY(MAX);
SET @a = CONVERT(VARBINARY(MAX), '0x07FD00B0653E6B65512875CF9100000052004500320036000000FF', 1);
SELECT len(@a)

结果27
果然是54的一半

修正

DECLARE @a VARBINARY(MAX);
SET @a = CONVERT(VARBINARY(MAX), '0x07FD00B0653E6B65512875CF9100000052004500320036000000FF', 1);
DECLARE @len1 int=CHARINDEX('000000',CONVERT(VARCHAR(MAX), @a, 2))-1;
--到00000的字符长度是26
SELECT  @len1
DECLARE @len12 int=(@len1+2)/2-4;
--需要截取的长度是10 
-- +0x 的2字符(VARBINARY转存VARCHAR丢失)
-- /2转换成字节长度
-- -无效的4个字节 (截取从字节第4位开始)
SELECT  @len12
SELECT SUBSTRING(@a, 4,@len12)
--从第4位字节 截取10个字节
SELECT CAST(SUBSTRING(@a, 4,@len12) AS NVARCHAR(MAX))

这次对了,带入我们的逻辑:

SELECT
TOP 10
  CAST(
	 SUBSTRING(CAST(Remakes AS VARBINARY(MAX)),4, 
	 ((CHARINDEX('000000',CONVERT(VARCHAR(MAX), CAST(Remakes AS VARBINARY(MAX)), 2))-1+2)/2-4)
	 --截取长度
	 ) AS NVARCHAR(MAX)
  ) as  Remakes
 FROM TabelA  

六、整合

我们再来捋一捋,实际上有三种情况
1、空值 (0x07FD000000FF 转换出来是 ‘’)
2、有乱码
3、无乱码

1、空值好判断1
((CHARINDEX('000000',CONVERT(VARCHAR(MAX), CAST(Remakes AS VARBINARY(MAX)), 2))-1+2)/2-4)=-1
就行
就是说 00000 起始于0x07FD000000FF 排除掉 前面的4个字节’0x 07 FD 00的第3个

1、空值好判断2
或者直接一点
用二进制空值 0x07FD000000FF 去对比
CONVERT(VARCHAR(MAX), CAST(Remakes AS VARBINARY(MAX)), 2)='07FD000000FF'

2、无乱码也好判断
CHARINDEX('000000',CONVERT(VARCHAR(MAX),CAST(Remakes AS VARBINARY(MAX)), 2))=0
就行
相当于C# 中的 IndexOf("000000")=-1
就是不包含的意思

SELECT
 --1、 空
 CASE WHEN  ((CHARINDEX('000000',CONVERT(VARCHAR(MAX), CAST(Remakes AS VARBINARY(MAX)), 2))-1+2)/2-4)=-1
      THEN ''
 --2、无乱码  详情见第(二)节
 WHEN  CHARINDEX('000000',CONVERT(VARCHAR(MAX),CAST(Remakes AS VARBINARY(MAX)), 2))=0
 
	  THEN
		 dbo.CleanSurrogateCharacters(
		 CAST(
				SUBSTRING(
					CAST(Remakes AS VARBINARY(MAX)), 
						4,DATALENGTH(CAST(Remakes AS VARBINARY(MAX)))-6
				) AS NVARCHAR(MAX)
			))
 --3、有乱码   截取掉非内容(00000之后)部分  详情见第(五)节
 ELSE
	  dbo.CleanSurrogateCharacters(
	  CAST(
		  SUBSTRING(CAST(Remakes AS VARBINARY(MAX)),4, ((CHARINDEX('000000',CONVERT(VARCHAR(MAX), CAST(Remakes AS VARBINARY(MAX)), 2))-1+2)/2-4)
			) AS NVARCHAR(MAX)
		))
  END  AS Remakes
 FROM TabelA  

问题完美解决。前端显示、导出,都不再会有乱码

((CHARINDEX('000000',CONVERT(VARCHAR(MAX), CAST(Remakes AS VARBINARY(MAX)), 2))-1+2)/2-4)
-------示例说明-------------------
image ---------数据:0x07FD00B0653E6B65512875CF9100000052004500320036000000FF
转VARBINARY 后:0x07FD00B0653E6B65512875CF9100000052004500320036000000FF
转varchar -------后: 07FD00B0653E6B65512875CF9100000052004500320036000000FF
找00000 位置-----------得: 27-1=26
加上0x 的2 ------------- 得 :28
二进制转字节长度 /2 -得: 14
去掉前面无效的 0x 07 FD 00 4个字节 得: B0653E6B65512875CF91 10个字节 为实际截取内容

到此这篇关于SQL image转汉字乱码处理记录的文章就介绍到这了,更多相关SQL image转汉字乱码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文