1. 程式人生 > >在 Sql Server 中實現 UrlDecode

在 Sql Server 中實現 UrlDecode

最近在公司網站中要做一個統計,統計一下按關鍵字或者來源網站關鍵字的數量,結果發現數據庫中太多的 URL 地址中出現了漢字,並且,這些漢字還是經過了 UrlEncode 之後的內容,天啊,你玩死文盲吧,難道每統計一次,你都想讓文盲把這些內容用程式做下解碼嗎?

於是,文盲同學發揚了共享精神,趕緊上網搜怎麼用 SqlServer 進行 UrlDecode。。。。。時間過去很久(兩個小時)。。。。沒有相關的結果。。。即便是有相關內容,也都是使用System.Web.HttpUtility.UrlDecode進行實現的,但問題是用這個方法實現的話,CLR還需要 System.Web.dll以及其所依賴的其他元件支援,實在是太麻煩了

再然後,實在沒有相關內容了,只好自己寫一個CLR程式來進行支援了(其實到此為止都是廢話,文盲實在想不明白,為什麼網上搜不到相關內容)

----------------------------------------------------------------------------------------------------

首先,我們需要實現不使用System.Web.HttpUtility.UrlDecode這個方法的解碼方式

於是先寫一個解碼函式,這個函式需要錄入一個字串,如果有編碼後的內容,將編碼後的內容解碼,否則正常返回

所以先做一個支援函式

        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[],為什麼這麼寫呢,因為不管是什麼編碼型別,編碼內容都符合 %00-%FF這個規則,即:百分號後跟隨兩位十六進位制數字,先不管它是utf(三位)還是gb2312(兩位),至少轉成 byte[] 是沒錯的

然後將對應字串的byte[]集合按順序合併成一個大byte[],然後就可以直接使用 Encoding.GetString()方法來轉成字串了,編碼在這一步進行實現

那麼分解字串併合並byte[]的支援函式也是需要的

        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;
        }
最後,為了能夠符合CLR程式集要求,並在SqlServer中使用,寫一個SqlFunction
    [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