【主要代码】
-
@Override
-
public final boolean incrementToken() throws IOException {
-
-
clearAttributes();
-
int posIncr = 1;
-
-
-
// 返回的结果集
-
StringBuffer resultBuffer = new StringBuffer();
-
// token类型 single单字节字符 double 双字节字符
-
String tokenType = "";
-
Token token = null;
-
if (tokens != null && tokens.size() > 0) {
-
token = tokens.get(0);
-
tokens.remove(0);
-
} else if (cnBuffer != null && cnBuffer.length() > 0) {
-
// 中文段落处理
-
tokenType = "double";
-
if (nextToken != null && nextToken.length() > 0) {
-
if (nextToken.length() < cnBuffer.length()) {
-
cnBuffer = new StringBuffer(cnBuffer.substring(nextToken.length(),
-
cnBuffer.length()));
-
} else {
-
cnBuffer = null;
-
}
-
-
token = new Token(nextToken.toString(), cnStartOffset, cnStartOffset
-
+ nextToken.length(), tokenType);
-
tokens = getAllMatchTokens(nextToken, cnStartOffset);
-
cnOffset += nextToken.length();
-
nextToken = null;
-
} else {
-
StringBuffer str = new StringBuffer(cnBuffer.toString());
-
String tokenResult = getTokenResult(str);
-
//token = new Token(tokenResult, cnStartOffset, cnStartOffset + tokenResult.length(), tokenType);
-
-
tokens = getAllMatchTokens(new StringBuffer(tokenResult), cnStartOffset);
-
-
offsetAtt.setOffset(cnStartOffset, cnStartOffset + tokenResult.length());
-
//termAtt.setLength(arg0);
-
//Appends the specified String to this character sequence.
-
termAtt.append(tokenResult);
-
typeAtt.setType(tokenType);
-
-
}
-
cnStartOffset = startOffset + cnOffset;
-
}{
-
startOffset = currentOffset;
-
// 得到待处理的段落
-
while (true) {
-
-
posIncrAtt.setPositionIncrement(posIncr);
-
-
// 缓存取完则载入新的字符串
-
if (bufferIndex >= bufferLen) {
-
bufferLen = input.read(buffer);
-
bufferIndex = 0;
-
}
-
if (bufferLen == -1) {
-
if (resultBuffer.length() > 0) {
-
break;
-
} else {
-
return false;
-
}
-
}
-
// 从缓存中取出字符
-
currentChar = buffer[bufferIndex];
-
if (Character.isLetterOrDigit(currentChar) && !isUnUseWord(currentChar)) {
-
// 有效字符
-
Character.UnicodeBlock ub = Character.UnicodeBlock.of(currentChar);
-
if ((ub == Character.UnicodeBlock.BASIC_LATIN)
-
|| (ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS)) {
-
// 当前字符为非中文字符或者全角字符
-
// 如果结果集不为空,而且数据是双字节类型则退出循环进行处理
-
if (resultBuffer.length() > 0 && "double".equals(tokenType)) {
-
break;
-
}
-
// 英文字符处理
-
// 全角转为半角
-
if (ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
-
int i1 = (int) currentChar;
-
i1 = i1 - 65248;
-
currentChar = (char) i1;
-
}
-
// 转为小写并加入结果集
-
resultBuffer.append(Character.toLowerCase(currentChar));
-
currentOffset++;
-
bufferIndex++;
-
tokenType = "single";
-
} else {
-
// 当前字符为中文字符
-
// 如果结果集不为空,而且数据是单字节类型则退出循环进行处理
-
if (resultBuffer.length() > 0 && "single".equals(tokenType)) {
-
break;
-
}
-
resultBuffer.append(currentChar);
-
currentOffset++;
-
bufferIndex++;
-
tokenType = "double";
-
}
-
} else {
-
// 非索引有效字符
-
if (resultBuffer.length() > 0) {
-
// 结果集中有内容
-
currentOffset++;
-
bufferIndex++;
-
break;
-
} else {
-
// 结果集为空
-
bufferIndex++;
-
currentOffset++;
-
startOffset++;
-
continue;
-
}
-
}
-
}
-
// 如果结果集是中文段落则进行进一步处理,如果是英文单词则直接处理
-
if (resultBuffer.length() > 0 && "double".equals(tokenType)) {
-
cnStartOffset = startOffset;
-
cnOffset = 0;
-
resultBuffer = new StringBuffer(getTokenResult(resultBuffer));
-
cnStartOffset = startOffset + cnOffset;
-
tokens = getAllMatchTokens(resultBuffer, startOffset);
-
}
-
-
-
offsetAtt.setOffset(startOffset, startOffset
-
+ resultBuffer.length());
-
termAtt.append(resultBuffer);
-
-
typeAtt.setType(tokenType);
-
return true;
-
}
-
}
代码解析,其实代码中有一部分功能其实TokenStreamImpl都已经提供了,这里列出来是为了巩固下,吼吼。。。
首先会通过TokenFilter来调用
input.incrementToken() 通过分词器,获取下一个token,如果在构造createTokenStreamComponent时候没有传入过滤器,默认是调用CachingTokenFilter的incrementToken方法。
首先会进入while(true)循环,这里 posIncrAtt.setPositionIncrement(posIncr); 这里是设置当前token与前一个token在实际的原文本中相隔词的数量,前面也具体提过。
并进一步判断,如果缓存的字符串已经完成读取,则读取新的字符串,或跳出循环。
从缓存中如([我, 一,个, ,, 四, 级, ,, b, i, b, i......])取出第一个字符,判断是否是有效字符,
如果是有效字符,进过一系列操作,如全角转半角等,放入结果集中,当前字符的位置,缓存指针都+1,
------------------------------------------------------------------------------------------
然后从待处理内容缓存中读取第二个字符,判断字符的有效性,(这里的'double','single'是楼主参考2.0lucene时候定义的,不符合现在的定义。)
放入结果集中,当前字符的位置,缓存指针都+1,
------------------------------------------------------------------------------------------
.......
------------------------------------------------------------------------------------------
读入第四个字符,不是有效字符,当前字符的位置,缓存指针都+1,并且跳出循环,
进入下面构建token步奏,
这里主要介绍两个方法
getTokenResult(StringBuffer str),用来对中文字符串进行处理并得到以字符串第一个字符起始的Token
getMatchWords(StringBuffer str),用来返回和字库匹配的token字符串。
'我一个'三个字会首先入getTokenResult方法,
获取字符串第一个字,'我' ,判断是否是连词,如果是连词,则先判断下一个字符开始是否有词,获得匹配直接返回,如果不是
调用如下getMatchWords(StringBuffer str)方法作为最后匹结果返回,如果没有,采用二分法,获取匹配token字符串,
同时将剩余字符串如'个'放入cnBuffer中,然后返回主程序,
填充offsetAtt,termAtt,typeAtt,一个token形成,回到过滤器方法中。
getMatchWords(StringBuffer str) ,字典匹配,采用正向最大匹配法
------
当传入'我一个'三个字后,根据第一个关键字,从字典中获取包含'我'的所有词语,将这些词语按照字符从多到少排列,然后按照
正向最大匹配法匹配,例如 字典中查出'我一',和 '我一个'这三个字比较,截取相同长度,结果匹配,返回该匹配字符到getTokenResult()中
再token流获取下一个token时候,首先会判断是否中文字符段落处理完成,如上一步还遗留一个'个', 交由getTokenResult(str)方法返回token匹配字符串,加入attribute中。
进入while(true)循环中,继续从待处理字符串中获取字符,直到待处理字符串中的字符处理完毕返回‘false’到过滤器中。
贴出两个主要方法
-
/**
-
* 对中文字符串进行处理并得到以字符串第一个字符起始的Token
-
*
-
*
-
* @param str
-
* @return
-
*/
-
private String getTokenResult(StringBuffer str) {
-
if (str.length() <= 2) {
-
cnBuffer = null;
-
return str.toString();
-
}
-
char currChar = str.charAt(0);
-
String token = null;
-
// 临时存放查找内容
-
StringBuffer tmpBuffer = new StringBuffer();
-
if (isConj(currChar)) {
-
// 如果是连词,则先判断下一个字符开始是否有词
-
tmpBuffer.append(str.substring(1, str.length()));
-
token = getMatchWords(tmpBuffer);
-
if (token != null) {
-
// 有词的话则单独返回该连词
-
nextToken = new StringBuffer(token);
-
cnBuffer = tmpBuffer;
-
cnOffset++;
-
return String.valueOf(currChar);
-
}
-
}
-
tmpBuffer = str;
-
token = getMatchWords(tmpBuffer);
-
if (token != null) {
-
if (token.length() < str.length())
-
cnBuffer = new StringBuffer(str.substring(token.length(), str.length()));
-
else
-
cnBuffer = null;
-
cnOffset += token.length();
-
return token;
-
} else {
-
// 没有匹配的词,则采用二分法
-
cnBuffer = new StringBuffer(str.substring(1, str.length()));
-
cnOffset++;
-
token = str.substring(0, 2);
-
return token;
-
}
-
}
-
-
/**
-
* 字典匹配,采用逆向最大匹配法
-
*
-
* @param str
-
* @return
-
*/
-
@SuppressWarnings("unchecked")
-
private String getMatchWords(StringBuffer str) {
-
char keyChar = str.charAt(0);
-
List<String> list = (List) FMMAnalyzer.dictManager.getWordMap()
-
.get(keyChar);
-
// 对字典进行排序,长度长的在前面
-
Collections.sort(list, new java.util.Comparator<String>() {
-
public int compare(String o1, String o2) {
-
if (o1.length() == o2.length())
-
return 0;
-
if (o1.length() >= o2.length())
-
return -1;
-
return 1;
-
}
-
});
-
for (String word : list) {
-
if (word.length() <= str.length()) {
-
String strWord = str.substring(0, word.length());
-
if (word.equals(strWord))
-
return word;
-
}
-
}
-
return null;
-
}
阅读(1757) | 评论(0) | 转发(0) |