由於不爽正則表達式複雜的語法,較低的中文支持度,不是很好的效率,因此本身寫了一個類正則表達式的自動匹配/提取機。目前測試來看,MatcherDroid相較於正則表達式,有更易使用的語法規範、大數據下更高的執行效率以及正好的中文支持。 正則表達式
鑑於本人能力很是有限,但願各位大神發現什麼問題或者有什麼新的功能需求能夠隨時聯繫我,很樂意效勞。郵箱:384924552@qq.com 算法
下載地址:http://www.oschina.net/code/snippet_1409138_26921 數據結構
MatcherDroid是在字符串搜索/比較的基礎上,最大化的使用剪枝與複用的思路,創建一個高效的匹配搜索引擎,其提升效率的三大方式: 函數
(1)剪枝:儘量地減小運算/迭代次數。 測試
(2)複用:最大程度地避免重複運算,儘量地用有限的空間換有限的時間。 大數據
(3)優化代碼:儘量地避免冗餘運算,使用高效運算符替代低效運算符。 優化
MatcherDroid的關鍵技術點爲: 搜索引擎
(1)匹配機首先使用錨來搜索模串在匹配文本中的可能開始下標,本質上就是多模式匹配問題。這裏使用AC算法對模串集合進行優化,自動調度遍歷(Java String類型中自帶的indexOF方法)/KMP/Sunday算法來搜索模串在匹配文本中可能的下標列表。其中遍歷適合於較短文本,較簡單模串的狀況,KMP適用於較短文本,較複雜模串的狀況,Sunday適用於較長文本,較複雜模串的狀況。 spa
(2)匹配機構造了規則隊列與源輸入串兩個動態匹配隊列,構形成了一個類自動機的迭代機制,儘量地使用剪枝的方法減小迭代次數同時每一次匹配動做盡量地匹配較長的文本。 .net
錨 |
將規則中規定的必然出現的字符串/字符串集合稱爲錨,MatcherDroid首先搜索錨在匹配文本中的下標,而後從下標位置開始前向或者後向匹配。 【注】:對於規則字符串中可能存在多個單配/通配字符串的狀況時,由於在實際測試中MatchDroid用於錨下標搜索的時間遠大於匹配耗費的時間,目前默認只選擇最長的單配字符串(第一優先級)或者字符串數目最少的多配字符串集合做爲錨。可能在長文本中設置多錨能夠提升效率,若是有任何問題請及時交流。 |
字符項 |
l 單配字符:如 ab、張二狗、- 這樣匹配特定單一字符串的輸入字符串。規則中全部的單配字符須要放置在()中。 l 多配字符串集合:輸入的一個HashSet集合中包含有一個或多個的單配字符。 規則中HashSet集合名須要放置在[]中。 l 通配字符:如%這種能夠匹配任意中文字符的匹配字符。注意,通配字符不須要放置在()中。 |
規則項 |
任何一個「[字符項][重複次數]」的組合會被解析爲一個規則項,譬如: (張二狗){1,1}就是一個規則項。 |
% |
匹配全部中文字符。 |
() |
定義輸入的單配字符串。 |
[] |
定義須要輸入的多配字符串的集合名稱。 |
{m,n} |
定義輸入的字符項的重複次數,省略則默認爲1; |
關鍵字的數目仍是不多的,懶得用那麼多的轉義字符。
IsGreedy(默認true) |
是否進行貪婪匹配。比較以下: 關鍵字:張%{1,2}二 匹配句式:張二二二二 貪婪匹配結果:張二二二 非貪婪匹配結果:張二二 |
AcEnabled(默認true) |
多模串匹配時是否使用AhoCorasick算法進行優化。 |
algorithm (默認爲CollectionUtil) |
選定單模串匹配算法,可選: l CollectionUtil:String自帶的indexOf方法,適用於短文本、簡單模串。 l KMP:適用於短文本、複雜模串。 l Sunday:適用於長文本。 |
setAnchor(int Pos) |
自行設定哪個規則項爲錨,注意,規則項中若存在單配字符串則不能選擇多配字符串做爲錨。 |
將輸入的規則按照從左到右的順序提取成一個個規則項而後放入內置的規則項匹配隊列中,基本數據結構以下所示:
首先在輸入的匹配文本中搜索錨的位置,而後從錨位置開始前向或者後向匹配。
KMP |
|
Sunday |
|
CollectUtil |
|
AC |
|
N次單模串匹配 |
|
設置了兩個動態指針,初始化分別指向錨所在的輸入字符串的當前位置與錨所對應的匹配規則項的當前位置。若是錨所在的規則項不是規則匹配隊列首,則自動前向匹配。若是錨在規則匹配隊列首或者前向匹配完成以後,會自動開始從錨位置後向匹配,當發現一個匹配項後會自動將結果加入到一個靜態的結果集變量中。
Step 1 : 建立MatchDroid實例。
MatcherDroid cre = new MatcherDroid();
Step 2 : 編譯規則
String reg = "(尊敬的)%{2,3}";
這是MatcherDroid規則,對應正則表達式的規則是:"尊敬的.{2,3}",由此能夠看出MatchDroid能夠更好地支持中文。下面使用compiled函數進行規則編譯。
cre.compiled(reg,null);
Step 3 : 匹配機可選參數設定
l 設定單模串匹配算法:KMP、Sunday或者String自帶的IndexOf,這裏使用KMP做爲示範。
cre.setAlgorithm(Algorithm.KMP);
Step 4 : 進行模式匹配
String str_input = "尊敬的張二狗11你好,尊敬的成吉思汗,尊敬的張1234";
Result_Set rs = cre.matcher(str_input);
System.out.println(rs.getGroup());
匹配的結果是:[尊敬的張二狗, 尊敬的成吉思]
數據集:9335862條40~120個字符的文本數據
|
MatchDroid |
正則 |
String reg = "(尊敬的)%{2,3}"; |
33s |
40s |
String reg = "(尊敬的會員)%{2,3}(個人名字是二狗)"; |
67s |
183s |
由上可見,越是複雜的規則MatchDroid的效率越高。
Step 1 : 建立MatchDroid實例。
MatcherDroid cre = new MatcherDroid();
Step 2 : 編譯規則
車牌號對應的正則表達式規則是:
"[蘇滬浙京魯皖豫閩鄂湘粵贛津冀晉黑吉遼渝川雲貴桂瓊陝甘青寧新蒙藏]{1}[A-Z]{1}[\\-]{1}[A-Z_0-9]{5}"
則將[蘇~藏]、[A~Z]、[A~Z_0~9]這三個多配字符串轉化爲HashSet數據集
String reg = "[hs_p]{1}[En_Set]{1}(-){0,1}[En_Num_Set]{5}";
這裏的hs_p是存放[蘇~藏]這些字符的HashSet數據集,En_Set是內置的存放全部英文字符的數據集,En_Num_Set是內置的存放全部數字與英文字符的數據集。
cre.compiled("[hs_p]{1}[En_Set]{1}(-){0,1}[En_Num_Set]{5}",hm);
這裏的hm是一個HashMap<String, HashSet<String>>結構體,String表示多配字符串數據集名,HashSet<String>存放實際數據。
Step 3 : 匹配機可選參數設定
l 設定錨規則,這裏選擇[hs_p]這個多配字符串集合所在的規則項做爲錨規則。
cre.setAnchor(0);
Step 4 : 進行模式匹配
str_input = "張二狗蘇F--5FAF6蘇F-FFFFF甘A97671王下邀月甘G---8786F熊";
rs = cre.matcher(str_input);
System.out.println(rs.getGroup());
結果是:
[甘A97671, 蘇F-FFFFF]
|
MatchDroid |
正則 |
MatchDroid (AC優化) |
String reg = "[hs_p]{1}[En_Set]{1}(-){1}[En_Num_Set]{5}"; |
3783ms |
95362ms |
|
String reg = "[hs_p]{1}[En_Set]{1}(-){0,1}[En_Num_Set]{5}"; |
32s |
75s |
22788ms |
因而可知,
l 設定優質的錨字符能夠幾何級的提升效率。上表中第一條與第二條規則的區別在於’-’這個字符是否必定出現。
l 在多模式匹配中,使用AC算法能夠大幅度提升效率。
IndexOf/KMP/Sunday |
較長文本 |
較短文本 |
較簡單模串 |
|
72/80/107 |
較複雜模串 |
2381ms/4500ms/1032ms |
109/99/102 |
l 任何一個MatchDroid規則只須要調用一次compiled函數,matcher函數能夠無限調用。千萬不要將compiled函數放在循環體內部,這很是耗費時間。
l 選定優質的錨字符:儘量是文本中出現次數少的單配/多配字符串。