本帖最后由 atgc 于 2014-1-5 19:19 编辑
几年前写过一个识别汉字的函数
http://www.itpub.net/thread-847680-1-1.html
但是只能识别GB2312的6763个汉字,现在做了扩展,可以识别GBK的21003个汉字
包括CJK(中日韩),以及少量部首,如果不要这些部首,也可以修改函数过滤掉
新函数性能比旧函数好,把旧函数里的SELECT语句去掉了,并且做了些简单的优化,性能还有优化余地
缺点,调用比旧函数稍微麻烦一些,主要是那个DUMP函数只能在select语句中执行
参考资料
http://baike.baidu.com/link?url=rPKTqUcCknukF72Yduld1CAKd_lT1iGAsnE4F-W5RMWfaVyzGBiQ9B_gvv-aOvjT
http://zh.wikipedia.org/wiki/GBK
http://ff.163.com/newflyff/gbk-list/
GBK字符集共收录中日韩(CJK)汉字(简繁体)21003个,含少量部首,分布在GBK编码表的2区,3区和4区
其中
------------------------------------------------------------------------------------
GBK 2区(B0A1-F7FE), 收录GB2312汉字共6763个
------------------------------------------------------------------------------------
2区的高位是十进制的176-247, 低位是十进制的161-254
H 176 247
L 161 254
B0 0 1 2 3 4 5 6 7 8 9 A B C D E F
A 啊 阿 埃 挨 哎 唉 哀 皑 癌 蔼 矮 艾 碍 爱 隘
B 鞍 氨 安 俺 按 暗 岸 胺 案 肮 昂 盎 凹 敖 熬 翱
C 袄 傲 奥 懊 澳 芭 捌 扒 叭 吧 笆 八 疤 巴 拔 跋
D 靶 把 耙 坝 霸 罢 爸 白 柏 百 摆 佰 败 拜 稗 斑
E 班 搬 扳 般 颁 板 版 扮 拌 伴 瓣 半 办 绊 邦 帮
F 梆 榜 膀 绑 棒 磅 蚌 镑 傍 谤 苞 胞 包 褒 剥
------------------------------------------------------------------------------------
GBK 3区(8140-A0FE), GB13000.1扩充汉字区, 收录CJK(中日韩)汉字6080个
------------------------------------------------------------------------------------
3区的高位是十进制的129-160, 低位是十进制的64-254,其中7F,也就是127没有汉字
H 129 160
L 64 254 (127不要)
81 0 1 2 3 4 5 6 7 8 9 A B C D E F
4 丂 丄 丅 丆 丏 丒 丗 丟 丠 両 丣 並 丩 丮 丯 丱
5 丳 丵 丷 丼 乀 乁 乂 乄 乆 乊 乑 乕 乗 乚 乛 乢
6 乣 乤 乥 乧 乨 乪 乫 乬 乭 乮 乯 乲 乴 乵 乶 乷
7 乸 乹 乺 乻 乼 乽 乿 亀 亁 亂 亃 亄 亅 亇 亊
8 亐 亖 亗 亙 亜 亝 亞 亣 亪 亯 亰 亱 亴 亶 亷 亸
9 亹 亼 亽 亾 仈 仌 仏 仐 仒 仚 仛 仜 仠 仢 仦 仧
A 仩 仭 仮 仯 仱 仴 仸 仹 仺 仼 仾 伀 伂 伃 伄 伅
B 伆 伇 伈 伋 伌 伒 伓 伔 伕 伖 伜 伝 伡 伣 伨 伩
C 伬 伭 伮 伱 伳 伵 伷 伹 伻 伾 伿 佀 佁 佂 佄 佅
D 佇 佈 佉 佊 佋 佌 佒 佔 佖 佡 佢 佦 佨 佪 佫 佭
E 佮 佱 佲 併 佷 佸 佹 佺 佽 侀 侁 侂 侅 來 侇 侊
F 侌 侎 侐 侒 侓 侕 侖 侘 侙 侚 侜 侞 侟 価 侢
------------------------------------------------------------------------------------
GBK 4区(AA40-FEA0), GB13000.1扩充汉字区, 收录CJK汉字和增补的汉字8160个(含少量部首)
------------------------------------------------------------------------------------
4区的高位是十进制的170-254, 低位是十进制的64-160,其中7F,也就是127和3区一样没有汉字
H 170 254
L 64 160 (127不要)
AA 0 1 2 3 4 5 6 7 8 9 A B C D E F
4 狜 狝 狟 狢 狣 狤 狥 狦 狧 狪 狫 狵 狶 狹 狽 狾
5 狿 猀 猂 猄 猅 猆 猇 猈 猉 猋 猌 猍 猏 猐 猑 猒
6 猔 猘 猙 猚 猟 猠 猣 猤 猦 猧 猨 猭 猯 猰 猲 猳
7 猵 猶 猺 猻 猼 猽 獀 獁 獂 獃 獄 獅 獆 獇 獈
8 獉 獊 獋 獌 獎 獏 獑 獓 獔 獕 獖 獘 獙 獚 獛 獜
9 獝 獞 獟 獡 獢 獣 獤 獥 獦 獧 獨 獩 獪 獫 獮 獰
A 獱
如下函数字符集必须ZHS16GBK
create or replace function func_chinese
(
p_str in varchar2, -- 输入的字符串
p_code in varchar2, -- dump(字符串)
p_chinese in pls_integer -- 1, 提取汉字, 非1, 提取非汉字
) return varchar2
as
v_code varchar2(32767) := substr(p_code,instr(p_code,':')+2);
v_chinese varchar2(32767) := '';
v_non_chinese varchar2(32767) := '';
v_comma pls_integer;
v_code_h pls_integer;
v_code_l pls_integer;
begin
if p_str is not null then
for i in 1..length(p_str) loop
if lengthb(substr(p_str,i,1))=2 then
v_comma := instr(v_code,',');
v_code_h := to_number(substr(v_code,1,v_comma-1));
v_code_l := to_number(substr(v_code,v_comma+1,abs(instr(v_code,',',1,2)-v_comma-1)));
if (v_code_h>=176 and v_code_h<=247 and v_code_l>=161 and v_code_l<=254) or
(v_code_h>=129 and v_code_h<=160 and v_code_l>=64 and v_code_l<=254 and nvl(v_code_l,127)!=127) or
(v_code_h>=170 and v_code_h<=254 and v_code_l>=64 and v_code_l<=160 and nvl(v_code_l,127)!=127) then
v_chinese := v_chinese||substr(p_str,i,1);
else
v_non_chinese := v_non_chinese||substr(p_str,i,1);
end if;
v_code := ltrim(v_code,'1234567890');
v_code := ltrim(v_code,',');
else
v_non_chinese := v_non_chinese||substr(p_str,i,1);
end if;
v_code := ltrim(v_code,'1234567890');
v_code := ltrim(v_code,',');
end loop;
if p_chinese = 1 then
return v_chinese;
else
return v_non_chinese;
end if;
else
return '';
end if;
end;
/
SQL> select * from t;
NAME
----------------------
新年快乐X
狿X
狿
199
春天会来的
-----------------------
实验1: 提取汉字
-----------------------
select name,func_chinese(name,dump(name),1) result from t;
NAME RESULT
------------------------------ -----------
新年快乐X 新年快乐
狿X 狿
狿 狿
199
春天会来的 春天会来的
-----------------------
实验2: 提取非汉字
-----------------------
select name,func_chinese(name,dump(name),0) result from t;
NAME RESULT
------------------------------ -------
新年快乐X X
狿X X
狿
199 199
春天会来的
-----------------------
实验3: 判断是否都是汉字
-----------------------
select name from t where func_chinese(name,dump(name),0) is null and name is not null;
NAME
-----------
狿
春天会来的
如上函数执行100万次,判断的字符串平均5个汉字,耗时22秒,其中dump耗时6,.5秒,实际判断的字节是10M
同时测试了在字符串很长的时候的性能
测试了字符串都是1000字节,判断1000行需要22秒,实际判断的字节也是10M
总体看,如果用来判断姓名,如果记录很多,性能还可以接受,如果用来判断很长的字符串,且记录很多的时候,性能很差
SQL> select count(*) from t;
COUNT(*)
----------
1048576
我加了paralle只花了4秒
select /*+ parallel(t 8) */ count(*) from t where func_chinese(name,dump(name),0) is null and name is not null;
COUNT(*)
----------
786432
Elapsed: 00:00:03.94
刚才尝试将v_code := ltrim(v_code,'1234567890'); 去掉,换了一种substr的方法
在字符串很短的时候,性能提升30%,但是字符串很长的时候,性能很糟糕