前言
提到快遞,必然關聯到地址。對於用戶來說,我們往往只根據自己的理解來填地址,我們覺得填地址時,可以把省份省略掉,因為知道是哪個市,必然就知道它屬於哪個省了;但是對於物流公司,系統里肯定不能存放沒有省份的數據,這時就需要對這樣的地址進行識別並補全省份。
如果你使用過電子運單,那你可能用過“智能識別”的功能,就是你粘貼一段文字,然后系統幫你識別聯系人、電話、地址等。我這里想說的就是如何來識別那地址的省市縣等。
正文
由於受系統框架限制,所以我使用的是盤古分詞,和Lucene.net全文檢索來實現這個功能。
盤古分詞
由於我們的目的是地址識別,那對用戶輸入的句子來分詞時,就不能按我們平常的方法來分詞了,比方說“深圳南山科技園南區”,我肯定不希望它分成“深圳、南山、科技、南區”,而我希望它能分成“深圳、南山、科、技、南、區”,如果是按第一種常規的方法來分詞,那用這組詞來檢索時,肯定就會檢索出更多的結果,而用后一種分詞,我在檢索時,把單個字的過濾點,就只用“深圳、南山”去檢索,其精確率必然更高。
盤古分詞是可以自定義字典的,那我這兒就是以省市縣鄉四級地址及四級地址去掉末尾的“省、市、區、街道”等字眼來作為字典(這兒我是為了犧牲部分准確率,換來其識別率),同時把盤古分詞自帶的字典給移除,我們在使用盤古分詞時,就只使用我們自己的字典就可以了。
private static void LoadMyDict() { _WordDictionary._WordDict.Clear(); _WordDictionary.ChineseName = null; List<string> wordList=new List<string>(); string path = Path.Combine(Environment.CurrentDirectory, "address.txt"); var lines = File.ReadAllLines(path); wordList.AddRange(lines); foreach (string line in lines) { string word = line.TrimEnd(new[] {'省', '市', '區', '鎮', '鄉', '縣'}).Replace("街道",""); if (!wordList.Contains(word)) { wordList.Add(word); } } foreach (var word in wordList) { if(word.Length<2)continue; _WordDictionary.InsertWord(word, 999, POS.POS_D_N); } _WordDictionary.Save(Path.Combine(Environment.CurrentDirectory, "dict.dct")); }
注:盤古分詞源代碼:https://github.com/JimLiu/Lucene.Net.Analysis.PanGu;
上面代碼中的address.txt文檔是四級地址的名稱,以換行作為分隔;另外下載源代碼,在PanGu.Segment中的LoadDictionary方法最后再調用上面代碼即可得到dict.dct,然后把這個dict.dct放到你使用盤古分詞項目的exe目錄下的dict文件夾即可。
Lucene.net
Lucene文檔就只有這5個字段,為什么需要由四級地址拼接成的DetailAddress呢?因為如果我以“廣東”、“深圳”,這兩個關鍵詞查詢,會查找到包含這兩個詞的所有地址,比方說“廣東省深圳市南山區”,而我想要的是“廣東省深圳市”,所以這兒必須加DetailAddress,不然無法把“廣東省深圳市”這一項排在搜索結果的最前面。
另外Lucene的數據源是由只有Province、Province+Prefecture、Province+Prefecture+County、Province+Prefecture+County+Village組成的。
在搜索時,我設置了個搜索字段的權重,四級地址的各自的權重按其級別來定,另外由於Village存在很多同名的,所以把其權重設為最小。
fileterList為需要過濾的搜索關鍵詞,因為這類詞匹配的地址不夠准確,當然,這可以寫成一個配置文件來維護,我這兒只是作為Demo,就直接寫在代碼里了。
words.Take(4):由於我們這兒最多只查詢四級,所以只去4個關鍵詞進行查詢就可以了。