在 Sql Server 中實現 UrlDecode
最近在公司網站中要做一個統計,統計一下按關鍵字或者來源網站關鍵字的數量,結果發現數據庫中太多的 URL 地址中出現了漢字,並且,這些漢字還是經過了 UrlEncode 之後的內容,天啊,你玩死文盲吧,難道每統計一次,你都想讓文盲把這些內容用程式做下解碼嗎?
於是,文盲同學發揚了共享精神,趕緊上網搜怎麼用 SqlServer 進行 UrlDecode。。。。。時間過去很久(兩個小時)。。。。沒有相關的結果。。。即便是有相關內容,也都是使用System.Web.HttpUtility.UrlDecode進行實現的,但問題是用這個方法實現的話,CLR還需要 System.Web.dll以及其所依賴的其他元件支援,實在是太麻煩了
再然後,實在沒有相關內容了,只好自己寫一個CLR程式來進行支援了(其實到此為止都是廢話,文盲實在想不明白,為什麼網上搜不到相關內容)
----------------------------------------------------------------------------------------------------
首先,我們需要實現不使用System.Web.HttpUtility.UrlDecode這個方法的解碼方式
於是先寫一個解碼函式,這個函式需要錄入一個字串,如果有編碼後的內容,將編碼後的內容解碼,否則正常返回
所以先做一個支援函式
這個函式的輸入引數是正則匹配結果,返回的則是對應的 byte[],為什麼這麼寫呢,因為不管是什麼編碼型別,編碼內容都符合 %00-%FF這個規則,即:百分號後跟隨兩位十六進位制數字,先不管它是utf(三位)還是gb2312(兩位),至少轉成 byte[] 是沒錯的private static byte[] EnCodeToChar(Match match) { if (Regex.IsMatch(match.Value, @"%[0-9a-f]{2}", RegexOptions.IgnoreCase)) { return new byte[] { (byte)Convert.ToInt32(match.Value.Replace("%", ""), 16) }; } else { return Encoding.UTF8.GetBytes(match.Value); } }
然後將對應字串的byte[]集合按順序合併成一個大byte[],然後就可以直接使用 Encoding.GetString()方法來轉成字串了,編碼在這一步進行實現
那麼分解字串併合並byte[]的支援函式也是需要的
最後,為了能夠符合CLR程式集要求,並在SqlServer中使用,寫一個SqlFunctionprivate static byte[] UTF8Byte(string str) { MatchCollection mc = Regex.Matches(str, @"%[0-9a-f]{2}|[\s\S]", RegexOptions.IgnoreCase); List<byte[]> btlist = new List<byte[]>(); int s = 0; for (int i = 0; i < mc.Count; i++) { byte[] t = EnCodeToChar(mc[i]); btlist.Add(t); s += t.Length; } byte[] bt = new byte[s]; s = 0; for (int i = 0; i < btlist.Count; i++) { for (int j = 0; j < btlist[i].Length; j++) { bt.SetValue(((byte[])btlist[i])[j], s); s++; } } return bt; }
[SqlFunction]
public static SqlString UTF8Decode(SqlChars input)
{
return Encoding.UTF8.GetString(UTF8Byte(new string(input.Value)));
}
然後把程式集匯入到資料庫,並建立自定義函式
CREATE FUNCTION [dbo].[UTF8Decode](@expression [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [UrlDecode].[UrlDecode].[UTF8Decode]
恩恩恩,測試了一下,非常不錯,可以直接在資料庫中使用了
----------------------------------------------------------------------------------------------------
其實這麼看來,UrlDecode和UrlEncode也沒有那麼神祕了,其實演算法還是很簡單的,那麼,就來最後一步吧考慮到不是所有網站的編碼都是一致的,上述內容只實現了UTF8編碼下的解碼,現在做一個支援任意編碼的解碼方法吧
以下是完整程式碼
using Microsoft.SqlServer.Server;
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Text;
using System.Text.RegularExpressions;
public static partial class UrlDecode
{
[SqlFunction]
public static SqlString UTF8Decode(SqlChars input)
{
return Encoding.UTF8.GetString(UTF8Byte(new string(input.Value)));
}
[SqlFunction]
public static SqlString Decode(SqlChars input, SqlString charset)
{
Encoding en = Encoding.UTF8;
try
{
en = Encoding.GetEncoding(charset.Value);
}
catch (Exception ex)
{
}
return en.GetString(GetCharsetBytes(new string(input.Value), en));
}
private static byte[] EnCodeToChar(Match match, Encoding en)
{
if (Regex.IsMatch(match.Value, @"%[0-9a-f]{2}", RegexOptions.IgnoreCase))
{
return new byte[] { (byte)Convert.ToInt32(match.Value.Replace("%", ""), 16) };
}
else
{
return en.GetBytes(match.Value);
}
}
private static byte[] GetCharsetBytes(string str, Encoding en)
{
MatchCollection mc = Regex.Matches(str, @"%[0-9a-f]{2}|[\s\S]", RegexOptions.IgnoreCase);
List<byte[]> btlist = new List<byte[]>();
int s = 0;
for (int i = 0; i < mc.Count; i++)
{
byte[] t = EnCodeToChar(mc[i], en);
btlist.Add(t);
s += t.Length;
}
byte[] bt = new byte[s];
s = 0;
for (int i = 0; i < btlist.Count; i++)
{
for (int j = 0; j < btlist[i].Length; j++)
{
bt.SetValue(((byte[])btlist[i])[j], s);
s++;
}
}
return bt;
}
private static byte[] EnCodeToChar(Match match)
{
if (Regex.IsMatch(match.Value, @"%[0-9a-f]{2}", RegexOptions.IgnoreCase))
{
return new byte[] { (byte)Convert.ToInt32(match.Value.Replace("%", ""), 16) };
}
else
{
return Encoding.UTF8.GetBytes(match.Value);
}
}
private static byte[] UTF8Byte(string str)
{
MatchCollection mc = Regex.Matches(str, @"%[0-9a-f]{2}|[\s\S]", RegexOptions.IgnoreCase);
List<byte[]> btlist = new List<byte[]>();
int s = 0;
for (int i = 0; i < mc.Count; i++)
{
byte[] t = EnCodeToChar(mc[i]);
btlist.Add(t);
s += t.Length;
}
byte[] bt = new byte[s];
s = 0;
for (int i = 0; i < btlist.Count; i++)
{
for (int j = 0; j < btlist[i].Length; j++)
{
bt.SetValue(((byte[])btlist[i])[j], s);
s++;
}
}
return bt;
}
}
CREATE FUNCTION [dbo].[UTF8Decode](@expression [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [UrlDecode].[UrlDecode].[UTF8Decode]
CREATE FUNCTION [dbo].[UrlDecode](@expression [nvarchar](max), @pattern [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [UrlDecode].[UrlDecode].[Decode]
select *,count(0) as cnt,dbo.UTF8Decode(keyword) from tableName order by cnt desc
perfect,就這麼完成了,支援任意編碼的方法則使用
select *,count(0) as cnt,dbo.UrlDecode(keyword,'gb2312') from tableName order by cnt desc
需要注意的是,如果輸入的編碼名稱無法解析,則自動使用utf8進行解碼了
以上,所有內容就都完成了,當然,如果在新增程式集出錯了,請參考文盲的另一篇博文參考下基本就可以解決了哦
好吧。。。寫完了這個文章,才發現,原來有其他方式實現,可以不使用 CLR 方法,真的是玩死文盲了
http://www.cnblogs.com/guanjie20/p/3412446.html
http://blog.csdn.net/ruijc/article/details/6931189