德语字符URL编码异常:为何ü编码结果为%fc而非%C3%BC?
自定义UrlEncode函数编码异常分析
问题描述
我编写了如下SQL Server自定义UrlEncode函数,通过遍历字符串对字符进行编码:
CREATE FUNCTION [dbo].[UrlEncode](@url nvarchar(MAX)) RETURNS nvarchar(3072) AS BEGIN DECLARE @c nchar(1); DECLARE @count int = LEN(@url); DECLARE @i int = 1; DECLARE @urlReturn nvarchar(max) = ''; WHILE (@i <= @count) BEGIN SET @c = SUBSTRING(@url, @i, 1); IF @c LIKE N'[A-Za-z0-9()''*\-._!~]' COLLATE Latin1_General_BIN ESCAPE N'\' COLLATE Latin1_General_BIN BEGIN SET @urlReturn = CONCAT(@urlReturn, @c); END ELSE BEGIN SET @urlReturn = CONCAT(@urlReturn, '%', SUBSTRING(sys.fn_varbintohexstr(CAST(@c AS varbinary(MAX))), 3, 2), ISNULL(NULLIF(SUBSTRING(sys.fn_varbintohexstr(CAST(@c AS varbinary(MAX))), 5, 2), '00'), '')); END SET @i = @i +1; END RETURN @urlReturn; END
执行语句 SELECT dbo.UrlEncode('ü') 后,得到的结果是 %fc,但正确的URL编码结果应为 %C3%BC,请问问题出在哪里?
原因分析
问题核心在于字符编码的转换逻辑不符合URL编码标准:
- SQL Server中
nvarchar类型的字符以UTF-16编码存储,直接将nchar(1)的'ü'转为varbinary时,得到的是UTF-16编码值0xFC00,函数截取后两位FC,最终输出%fc。 - 标准URL编码要求将字符转换为UTF-8编码后,再对每个字节进行十六进制编码。
'ü'的UTF-8编码是两个字节0xC3和0xBC,因此正确结果应为%C3%BC。
修正方案
需要修改函数逻辑,先将字符转换为UTF-8编码的二进制数据,再遍历每个字节进行编码,而非直接处理UTF-16的单个字符:
CREATE FUNCTION [dbo].[UrlEncode](@url nvarchar(MAX)) RETURNS nvarchar(MAX) AS BEGIN DECLARE @utf8Bytes varbinary(MAX) = CONVERT(varbinary(MAX), @url, 65001); -- 转换为UTF-8编码的二进制 DECLARE @len int = DATALENGTH(@utf8Bytes); DECLARE @i int = 1; DECLARE @urlReturn nvarchar(MAX) = ''; DECLARE @byte tinyint; WHILE (@i <= @len) BEGIN SET @byte = SUBSTRING(@utf8Bytes, @i, 1); -- 判断是否为URL安全字符(对应ASCII范围的安全字符) IF (@byte BETWEEN 0x41 AND 0x5A) -- A-Z OR (@byte BETWEEN 0x61 AND 0x7A) -- a-z OR (@byte BETWEEN 0x30 AND 0x39) -- 0-9 OR (@byte IN (0x28, 0x29, 0x27, 0x2A, 0x2D, 0x2E, 0x5F, 0x21, 0x7E)) -- ()'*-._!~ BEGIN SET @urlReturn = CONCAT(@urlReturn, CHAR(@byte)); END ELSE BEGIN -- 将字节转为十六进制,拼接%前缀 SET @urlReturn = CONCAT(@urlReturn, '%', UPPER(sys.fn_varbintohexstr(@byte))); END SET @i = @i + 1; END RETURN @urlReturn; END
关键改动说明
- 使用
CONVERT(varbinary(MAX), @url, 65001)将nvarchar字符串转换为UTF-8编码的二进制数据(65001是SQL Server中UTF-8的编码页编号)。 - 遍历UTF-8二进制数据的每个字节,而非原字符串的单个UTF-16字符,确保多字节UTF-8字符被正确拆分编码。
- 直接对每个字节进行安全判断和编码,符合URL编码的标准逻辑。
执行修正后的函数 SELECT dbo.UrlEncode('ü'),将得到正确结果 %C3%BC。
内容的提问来源于stack exchange,提问作者Ivan Debono
相关产品推荐
相关产品推荐

