1. 程式人生 > >jieba分詞的應用(java)

jieba分詞的應用(java)

在上一篇說的猜你喜歡功能中,又加了新的需求,需要對關鍵詞進行分詞,擴大推薦文章的範圍,這樣能夠拓展使用者的喜歡範圍,這時候我就想到可以用jieba分詞對中文進行分詞,同樣的需要去官網下載原始碼,這樣方便自己對原始碼的修改以達到自己的目的。這裡,我需要判斷切分出來的詞是否是無意義的詞,就需要對切出來的詞進行篩選,這時候,jieba分詞的一個屬性就體現出它的強大之處了,jieba分詞會將切分出來的詞進行詞性的定義,我可以通過對於jieba分此後詞的詞性進行判斷,篩選出名詞,去掉無用的連線詞,形容詞等其他詞性的詞來達到我的分詞目的。下面是對原始碼進行修改的部分。(大家也可以根據自己的需要,暴露原來隱藏的屬性來實現自己的功能。)

/**
*在jieba分詞的SegToken.java中對SegToken類增加一個成員變數properties來儲存單詞的詞性
**/
package com.huaban.analysis.jieba;

public class SegToken {
    public String word;

    public int startOffset;

    public int endOffset;

    public String properties;


    public SegToken(String word, int startOffset, int endOffset, String properties) {
        this
.word = word; this.startOffset = startOffset; this.endOffset = endOffset; this.properties = properties; } @Override public String toString() { return "[" + word + ", " + startOffset + ", " + endOffset + ", " + properties + "]"; } }

將從字典檔案dict.txt中讀取出來的單詞的詞性儲存到properties 的欄位中

/**
*在WordDictionary.java中增加property的Map儲存word與詞性的關係。建立索引關係,增加獲取詞性的公共方法
**/
package com.huaban.analysis.jieba;

import java.io.BufferedReader;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


public class WordDictionary {
    private static WordDictionary singleton;
    private static final String MAIN_DICT = "/dict.txt";
    private static String USER_DICT_SUFFIX = ".dict";

    public final Map<String, Double> freqs = new HashMap<String, Double>();
    public final Map<String, String> property = new HashMap<String, String>();
    public final Set<String> loadedPath = new HashSet<String>();
    private Double minFreq = Double.MAX_VALUE;
    private Double total = 0.0;
    private DictSegment _dict;


    private WordDictionary() {
        this.loadDict();
    }


    public static WordDictionary getInstance() {
        if (singleton == null) {
            synchronized (WordDictionary.class) {
                if (singleton == null) {
                    singleton = new WordDictionary();
                    return singleton;
                }
            }
        }
        return singleton;
    }


    /**
     * for ES to initialize the user dictionary.
     * 
     * @param configFile
     */
    public void init(Path configFile) {
        String abspath = configFile.toAbsolutePath().toString();
        System.out.println("initialize user dictionary:" + abspath);
        synchronized (WordDictionary.class) {
            if (loadedPath.contains(abspath))
                return;

            DirectoryStream<Path> stream;
            try {
                stream = Files.newDirectoryStream(configFile, String.format(Locale.getDefault(), "*%s", USER_DICT_SUFFIX));
                for (Path path: stream){
                    System.err.println(String.format(Locale.getDefault(), "loading dict %s", path.toString()));
                    singleton.loadUserDict(path);
                }
                loadedPath.add(abspath);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                // e.printStackTrace();
                System.err.println(String.format(Locale.getDefault(), "%s: load user dict failure!", configFile.toString()));
            }
        }
    }


    /**
     * let user just use their own dict instead of the default dict
     */
    public void resetDict(){
        _dict = new DictSegment((char) 0);
        freqs.clear();
    }


    public void loadDict() {
        _dict = new DictSegment((char) 0);
        InputStream is = this.getClass().getResourceAsStream(MAIN_DICT);
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));

            long s = System.currentTimeMillis();
            while (br.ready()) {
                String line = br.readLine();
                String[] tokens = line.split("[\t ]+");

                if (tokens.length < 2)
                    continue;

                String word = tokens[0];
                String properties = "";
                double freq = Double.valueOf(tokens[1]);
                if(tokens.length == 3)
                    properties = tokens[2];
                total += freq;
                word = addWord(word);
                freqs.put(word, freq);
                property.put(word, properties);//儲存單詞與詞性的索引關係
            }
            // normalize
            for (Entry<String, Double> entry : freqs.entrySet()) {
                entry.setValue((Math.log(entry.getValue() / total)));
                minFreq = Math.min(entry.getValue(), minFreq);
            }
            System.out.println(String.format(Locale.getDefault(), "main dict load finished, time elapsed %d ms",
                System.currentTimeMillis() - s));
        }
        catch (IOException e) {
            System.err.println(String.format(Locale.getDefault(), "%s load failure!", MAIN_DICT));
        }
        finally {
            try {
                if (null != is)
                    is.close();
            }
            catch (IOException e) {
                System.err.println(String.format(Locale.getDefault(), "%s close failure!", MAIN_DICT));
            }
        }
    }


    private String addWord(String word) {
        if (null != word && !"".equals(word.trim())) {
            String key = word.trim().toLowerCase(Locale.getDefault());
            _dict.fillSegment(key.toCharArray());
            return key;
        }
        else
            return null;
    }


    public void loadUserDict(Path userDict) {
        loadUserDict(userDict, StandardCharsets.UTF_8);
    }


    public void loadUserDict(Path userDict, Charset charset) {                
        try {
            BufferedReader br = Files.newBufferedReader(userDict, charset);
            long s = System.currentTimeMillis();
            int count = 0;
            while (br.ready()) {
                String line = br.readLine();
                String[] tokens = line.split("[\t ]+");

                if (tokens.length < 1) {
                    // Ignore empty line
                    continue;
                }

                String word = tokens[0];

                double freq = 3.0d;
                String properties = "";
                if (tokens.length == 2)
                    freq = Double.valueOf(tokens[1]);
                if(tokens.length == 3)
                    properties = tokens[2];//獲取單詞的詞性,存入map中
                word = addWord(word); 
                freqs.put(word, Math.log(freq / total));
                property.put(word, properties);
                count++;
            }
            System.out.println(String.format(Locale.getDefault(), "user dict %s load finished, tot words:%d, time elapsed:%dms", userDict.toString(), count, System.currentTimeMillis() - s));
            br.close();
        }
        catch (IOException e) {
            System.err.println(String.format(Locale.getDefault(), "%s: load user dict failure!", userDict.toString()));
        }
    }


    public DictSegment getTrie() {
        return this._dict;
    }


    public boolean containsWord(String word) {
        return freqs.containsKey(word);
    }

    public String getProperties(String word){//通過單詞獲取單詞的詞性
        if(containsWord(word))
            return property.get(word);
        else
            return "";
    }

    public Double getFreq(String key) {
        if (containsWord(key))
            return freqs.get(key);
        else
            return minFreq;
    }
}

將詞性儲存到SegToken的成員變數中,方便生成和調取。

/**
*在JiebaSegmenter.java中生成每個切分詞的SegToken物件進行儲存,方便使用
**/
 public List<SegToken> process(String paragraph, SegMode mode) {//對paragraphs進行切分,儲存到SegToken中
        List<SegToken> tokens = new ArrayList<SegToken>();
        StringBuilder sb = new StringBuilder();
        int offset = 0;
        for (int i = 0; i < paragraph.length(); ++i) {
            char ch = CharacterUtil.regularize(paragraph.charAt(i));
            if (CharacterUtil.ccFind(ch))
                sb.append(ch);
            else {
                if (sb.length() > 0) {
                    // process
                    if (mode == SegMode.SEARCH) {
                        for (String word : sentenceProcess(sb.toString())) {
                            tokens.add(new SegToken(word, offset, offset += word.length(), wordDict.getProperties(word)));//將詞性儲存進去
                        }
                    }
                    else {
                        for (String token : sentenceProcess(sb.toString())) {
                            if (token.length() > 2) {
                                String gram2;
                                int j = 0;
                                for (; j < token.length() - 1; ++j) {
                                    gram2 = token.substring(j, j + 2);
                                    if (wordDict.containsWord(gram2))
                                        tokens.add(new SegToken(gram2, offset + j, offset + j + 2, wordDict.getProperties(gram2)));
                                }
                            }
                            if (token.length() > 3) {
                                String gram3;
                                int j = 0;
                                for (; j < token.length() - 2; ++j) {
                                    gram3 = token.substring(j, j + 3);
                                    if (wordDict.containsWord(gram3))
                                        tokens.add(new SegToken(gram3, offset + j, offset + j + 3, wordDict.getProperties(gram3)));
                                }
                            }
                            tokens.add(new SegToken(token, offset, offset += token.length(), wordDict.getProperties(token)));
                        }
                    }
                    sb = new StringBuilder();
                    offset = i;
                }
                if (wordDict.containsWord(paragraph.substring(i, i + 1)))
                    tokens.add(new SegToken(paragraph.substring(i, i + 1), offset, ++offset, wordDict.getProperties(paragraph.substring(i, i + 1))));
                else
                    tokens.add(new SegToken(paragraph.substring(i, i + 1), offset, ++offset, wordDict.getProperties(paragraph.substring(i, i + 1))));
            }
        }
        if (sb.length() > 0)
            if (mode == SegMode.SEARCH) {
                for (String token : sentenceProcess(sb.toString())) {
                    tokens.add(new SegToken(token, offset, offset += token.length(), wordDict.getProperties(token)));
                }
            }
            else {
                for (String token : sentenceProcess(sb.toString())) {
                    if (token.length() > 2) {
                        String gram2;
                        int j = 0;
                        for (; j < token.length() - 1; ++j) {
                            gram2 = token.substring(j, j + 2);
                            if (wordDict.containsWord(gram2))
                                tokens.add(new SegToken(gram2, offset + j, offset + j + 2, wordDict.getProperties(gram2)));
                        }
                    }
                    if (token.length() > 3) {
                        String gram3;
                        int j = 0;
                        for (; j < token.length() - 2; ++j) {
                            gram3 = token.substring(j, j + 3);
                            if (wordDict.containsWord(gram3))
                                tokens.add(new SegToken(gram3, offset + j, offset + j + 3, wordDict.getProperties(gram3)));
                        }
                    }
                    tokens.add(new SegToken(token, offset, offset += token.length(), wordDict.getProperties(token)));
                }
            }

        return tokens;
    }

然後在關鍵詞切分的方法中進行判斷,選擇所需要詞性的word即可

//對關鍵詞進行結巴keyword分詞
        for (String sentence : keyword_list) {
            List<SegToken> tokens = segmenter.process(sentence, SegMode.SEARCH);
            for(SegToken s : tokens)
                if(s.word.length() > 1)
                    keyword += " "+s.word;
        }
        keyword_list = keyword.split("[,;\\s'\\*\\+|\\^]+");
        Set<String> keywordList = new LinkedHashSet<String>(Arrays.asList(keyword_list));//用set是為了去除文章的重複

到此完成新需求的實現。與大家共勉~
最後附上jieba分詞的此行類別及表示方法:

Ag

形語素

形容詞性語素。形容詞程式碼為 a,語素程式碼g前面置以A。

a

形容詞

取英語形容詞 adjective的第1個字母。

ad

副形詞

直接作狀語的形容詞。形容詞程式碼 a和副詞程式碼d並在一起。

an

名形詞
具有名詞功能的形容詞。形容詞程式碼 a和名詞程式碼n並在一起。

b

區別詞
取漢字“別”的聲母。

c

連詞
取英語連詞 conjunction的第1個字母。

dg

副語素
副詞性語素。副詞程式碼為 d,語素程式碼g前面置以D。

d

副詞
取 adverb的第2個字母,因其第1個字母已用於形容詞。

e

嘆詞
取英語嘆詞 exclamation的第1個字母。

f

方位詞
取漢字“方”

g

語素
絕大多數語素都能作為合成詞的“詞根”,取漢字“根”的聲母。

h

前接成分
取英語 head的第1個字母。

i

成語
取英語成語 idiom的第1個字母。

j

簡稱略語
取漢字“簡”的聲母。

k

後接成分

l

習用語
習用語尚未成為成語,有點“臨時性”,取“臨”的聲母。

m

數詞
取英語 numeral的第3個字母,n,u已有他用。

Ng

名語素
名詞性語素。名詞程式碼為 n,語素程式碼g前面置以N。

n

名詞
取英語名詞 noun的第1個字母。

nr

人名
名詞程式碼 n和“人(ren)”的聲母並在一起。

ns

地名
名詞程式碼 n和處所詞程式碼s並在一起。

nt

機構團體
“團”的聲母為 t,名詞程式碼n和t並在一起。

nz

其他專名
“專”的聲母的第 1個字母為z,名詞程式碼n和z並在一起。

o

擬聲詞
取英語擬聲詞 onomatopoeia的第1個字母。

p

介詞
取英語介詞 prepositional的第1個字母。

q

量詞
取英語 quantity的第1個字母。

r

代詞
取英語代詞 pronoun的第2個字母,因p已用於介詞。

s

處所詞
取英語 space的第1個字母。

tg

時語素
時間詞性語素。時間詞程式碼為 t,在語素的程式碼g前面置以T。

t

時間詞
取英語 time的第1個字母。

u

助詞
取英語助詞 auxiliary

vg

動語素
動詞性語素。動詞程式碼為 v。在語素的程式碼g前面置以V。

v

動詞
取英語動詞 verb的第一個字母。

vd

副動詞
直接作狀語的動詞。動詞和副詞的程式碼並在一起。

vn

名動詞
指具有名詞功能的動詞。動詞和名詞的程式碼並在一起。

w

標點符號

x

非語素字
非語素字只是一個符號,字母 x通常用於代表未知數、符號。

y

語氣詞
取漢字“語”的聲母。

z

狀態詞
取漢字“狀”的聲母的前一個字母。

un

未知詞
不可識別詞及使用者自定義片語。取英文Unkonwn首兩個字母。(非北大標準,CSW分詞中定義)

相關推薦

jieba應用java

在上一篇說的猜你喜歡功能中,又加了新的需求,需要對關鍵詞進行分詞,擴大推薦文章的範圍,這樣能夠拓展使用者的喜歡範圍,這時候我就想到可以用jieba分詞對中文進行分詞,同樣的需要去官網下載原始碼,這樣方便自己對原始碼的修改以達到自己的目的。這裡,我需要判斷切分出來

治法在排序演算法中的應用JAVA--快速排序Lomuto劃分、Hoare劃分、隨機化快排

分治法在排序演算法中的應用 快速排序:時間複雜度O(nlogn) 如果說歸併排序是按照元素在陣列中的位置劃分的話,那麼快速排序就是按照元素的值進行劃分。劃分方法由兩種,本節將主要介紹Huare劃分,在減治法在查詢演算法中的應用(JAVA)--快速查詢這篇文章中講述了Lomu

中科院系統NLPIR常見錯誤處理JAVA

沒有熟悉這個分詞器用起來真的讓人很崩潰,遇到bug都不知道怎麼辦,但是如果熟悉了用起來還是蠻得心應手的,是一個很不錯的分詞工具哦!強烈推薦! 下載地址:http://ictclas.nlpir.org/downloads,下載最新的NLPIR/ICTCLAS2

治法在求解“最近對”問題中的應用JAVA

分治法在求解“最近對”問題中的應用 最近對問題在蠻力法中有過講解,時間複雜度為O(n^2),下面將會採用分治法講解這類問題,時間複雜度會降到O(nlogn) 我們將笛卡爾平面上n>1個點構成的集合稱為P。若2<= n <= 3時,我們1可以通過蠻力法求解。

java中科院配置ICTCLAS

之前零零散散用過幾次,配置好了就沒管過。後來再用的時候就忘了怎麼配置,又找了很多資料(太麻煩了)。現總結一下當作筆記: 首先,下載中科院分詞專案。 github網址:https://github.com/NLPIR-team/NLPIR/tree/master/NLPIR%20SDK/NLPIR-ICTCL

NLP之CRF訓練

分鐘 -c data ++ del 控制 rdquo 進制 文本 分三步1、先分詞2、做BEMS標註,同時做詞性標註3、訓練模型 1、對語料進行分詞 拿到測試部的語料或者其他渠道的語料,先對語料進行分詞,我剛剛開始是用NS分詞的,等CRF模型訓練好後

lucene英文StandarAnalyzer中會被忽略的stopWords

使用Lucene進行索引查詢時發現有一部分詞會被分詞器直接忽略掉了,被忽略的分詞稱為stopWords,在英文中通常是一些語氣助詞或者無法表達明確含義的詞。 在定義含有stopWords分詞器的時候都會指定stopWords,如果沒有指定可以引用預設的stop

leetcode:49. 字母異位分組java

package LeetCode; import java.util.*; /* 給定一個字串陣列,將字母異位詞組合在一起。字母異位詞指字母相同,但排列不同的字串。 示例: 輸入: ["eat", "tea", "tan", "ate", "nat", "bat"], 輸出: [ ["a

淺談演算法4基於字的方法CRF

目錄 前言 目錄 條件隨機場(conditional random field CRF) 核心點 線性鏈條件隨機場 簡化形式 CRF分詞 CRF VS HMM 程式碼實現 訓練程式碼 實驗結果 參考文獻

中文系列 雙陣列Tire樹(DART)詳解

雙陣列Tire樹是Tire樹的升級版,Tire取自英文Retrieval中的一部分,即檢索樹,又稱作字典樹或者鍵樹。下面簡單介紹一下Tire樹。 1.1 Tire樹 Trie是一種高效的索引方法,它實際上是一種確定有限自動機(DFA),在樹的結構中,每一個結點對應一個DFA狀態,每一個從父結點指向子結點

中文IK的配置檔案

中文(IK)分詞器是在IKAnalyzer分詞包中使用的,使用前請自行下載相應的jar包 將這個檔案(IKAnalyzer.cfg.xml)放入到src的的目錄先, <?xml version="1.0" encoding="UTF-8"?> &

HMM最大匹配演算法Python

正向最大匹配演算法是我國最早提出的解決中文分詞問題的演算法,因其簡單易操作,至今仍作為機器分詞的粗分演算法,在今天看來,這種演算法的準確率遠不夠高,無法達到令人滿意的要求。這只是一次練習。 待切分

貪婪演算法在求解最小生成樹中的應用JAVA--Kruskal演算法

Kruskal演算法又被稱為“加邊法”,這種演算法會將加權連通圖的最小生成樹看成具有V-1條邊的無環子圖,且邊的權重和最小。演算法開始時,會按照權重的非遞減順序對圖中的邊排序,之後迭代的以貪婪的方式新增邊。下面以下圖為例來講解Kruskal演算法的過程:Input:6 101

蠻力法在求解凸包問題中的應用JAVA

凸包問題向來是計算幾何中最重要的問題之一,許多各式各樣的應用大多要麼本身就是圖凸包問題要麼其中一部分需要按照凸包問題解決。 凸集合定義:對於平面上一個點集合,如果集合中的任意兩點p和q為端點的線段都屬於該集合,那麼稱這個集合為凸集合。 凸包定義:一個點集合S的凸包是包含S的

統計使用訊飛語言云進行統計

最近想取一個網名,想起中國文化博大精深,如果用古代的唐詩宋詞組合一定有一個意向不到的名字。組合首先要分詞,想起錘子手機有一個很火的功能叫BigBang,它用的是訊飛的語言云,是免費提供的,所以這次使用訊飛的語言云進行分詞,然後隨機組合。另外,還可以進行有趣的資料統計,看看古

糖果問題java

10個小孩圍成一圈分糖果,老師分給第一個小孩10塊,第二個小孩2塊,第三個小孩8塊,第四個小孩22塊,第五個小孩16塊,第六個小孩4塊,第七個小孩10塊,第八個小孩6塊,第九個小孩14塊,第十個小孩20塊。然後所有的小孩同時將手中的糖分一半給右邊的小孩;糖塊數為奇數的人可向

減治法在查詢演算法中的應用JAVA--快速查詢

減治法在查詢演算法中的應用 快速查詢:選擇問題是求一個n個數列表的第k個最小元素的問題,這個數k被稱為順序統計量。對於k=1或k=n來說,這並沒有什麼意義,我們通常會要找出這樣的元素:該元素比列表中一

Spark 大資料中文統計 開發環境搭建

          幾年前搞BI專案時就聽說過大資料技術,當時也買了書,可惜沒有認真去學。幾年5月份 開始,報名參加王家林老師的大資料蘑菇雲行動,才算真正開始學習Spark,學習大資料技術。           網上很多Spark的例子都是經典的WordCount exam

蠻力法在求解“最近對”問題中的應用JAVA

最近對問題是在計算幾何問題中最簡單的,是指在一個包含n個點的集合中,找到距離最近的兩個點,我們這裡只研究二維空間中的版本,高維計算基本類似,區別只在於計算兩點之間距離的公式略有不同,下面是標準的歐幾里

資料庫查詢的優缺點以及英文和中文各自的方法

1.為什麼需要資料庫分詞查詢  假設有一個數據庫表,表中有一個title欄位 table1 假如有300萬的資料 id為主鍵,title也設定了索引 id title 1 這是計算機,