Snowball分词

using System;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using SF.Snowball.Ext;
using System.Collections.Generic;
using System.Collections;
using OpenNLP.Tools.PosTagger;


namespace Lucene.Net.Analysis.Snowball
{
//词汇的实体类,包括两个属性
public class myEwordEntity
{
public string txtWord;//词的文本
public string stemroot;//被过滤后词的词根
public string posWord;//词的词性
public int token_begin;//在文章中的开始位置
public int token_end;//在文章中的结束位置
public myEwordEntity()
{
txtWord = string.Empty;
posWord = string.Empty;
stemroot = string.Empty;
token_begin = 0;
token_end = 0;
}


}
public class Stemmer
{
private char[] b;
private int i, /* offset into b */
i_end, /* offset to end of stemmed word */
j, k;
private static int INC = 50;
/* unit of size whereby b is increased */

public Stemmer()
{
b = new char[INC];
i = 0;
i_end = 0;
}

/**
* Add a character to the word being stemmed. When you are finished
* adding characters, you can call stem(void) to stem the word.
*/

public void add(char ch)
{
if (i == b.Length)
{
char[] new_b = new char[i + INC];
for (int c = 0; c < i; c++)
new_b[c] = b[c];
b = new_b;
}
b[i++] = ch;
}


/** Adds wLen characters to the word being stemmed contained in a portion
* of a char[] array. This is like repeated calls of add(char ch), but
* faster.
*/

public void add(char[] w, int wLen)
{
if (i + wLen >= b.Length)
{
char[] new_b = new char[i + wLen + INC];
for (int c = 0; c < i; c++)
new_b[c] = b[c];
b = new_b;
}
for (int c = 0; c < wLen; c++)
b[i++] = w[c];
}

/**
* After a word has been stemmed, it can be retrieved by toString(),
* or a reference to the internal buffer can be retrieved by getResultBuffer
* and getResultLength (which is generally more efficient.)
*/
public string stemerToString()
{
return new String(b, 0, i_end);
}

/**
* Returns the length of the word resulting from the stemming process.
*/
public int getResultLength()
{
return i_end;
}

/**
* Returns a reference to a character buffer containing the results of
* the stemming process. You also need to consult getResultLength()
* to determine the length of the result.
*/
public char[] getResultBuffer()
{
return b;
}

/* cons(i) is true <=> b[i] is a consonant. */
private bool cons(int i)
{
switch (b[i])
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u': return false;
case 'y': return (i == 0) ? true : !cons(i - 1);
default: return true;
}
}

/* m() measures the number of consonant sequences between 0 and j. if c is
a consonant sequence and v a vowel sequence, and <..> indicates arbitrary
presence,

<c><v> gives 0
<c>vc<v> gives 1
<c>vcvc<v> gives 2
<c>vcvcvc<v> gives 3
....
*/
private int m()
{
int n = 0;
int i = 0;
while (true)
{
if (i > j) return n;
if (!cons(i)) break; i++;
}
i++;
while (true)
{
while (true)
{
if (i > j) return n;
if (cons(i)) break;
i++;
}
i++;
n++;
while (true)
{
if (i > j) return n;
if (!cons(i)) break;
i++;
}
i++;
}
}

/* vowelinstem() is true <=> 0,...j contains a vowel */
private bool vowelinstem()
{
int i;
for (i = 0; i <= j; i++)
if (!cons(i))
return true;
return false;
}

/* doublec(j) is true <=> j,(j-1) contain a double consonant. */
private bool doublec(int j)
{
if (j < 1)
return false;
if (b[j] != b[j - 1])
return false;
return cons(j);
}

/* cvc(i) is true <=> i-2,i-1,i has the form consonant - vowel - consonant
and also if the second c is not w,x or y. this is used when trying to
restore an e at the end of a short word. e.g.

cav(e), lov(e), hop(e), crim(e), but
snow, box, tray.

*/
private bool cvc(int i)
{
if (i < 2 || !cons(i) || cons(i - 1) || !cons(i - 2))
return false;
int ch = b[i];
if (ch == 'w' || ch == 'x' || ch == 'y')
return false;
return true;
}

private bool ends(String s)
{
int l = s.Length;
int o = k - l + 1;
if (o < 0)
return false;
char[] sc = s.ToCharArray();
for (int i = 0; i < l; i++)
if (b[o + i] != sc[i])
return false;
j = k - l;
return true;
}

/* setto(s) sets (j+1),...k to the characters in the string s, readjusting
k. */
private void setto(String s)
{
int l = s.Length;
int o = j + 1;
char[] sc = s.ToCharArray();
for (int i = 0; i < l; i++)
b[o + i] = sc[i];
k = j + l;
}

/* r(s) is used further down. */
private void r(String s)
{
if (m() > 0)
setto(s);
}

/* step1() gets rid of plurals and -ed or -ing. e.g.
caresses -> caress
ponies -> poni
ties -> ti
caress -> caress
cats -> cat

feed -> feed
agreed -> agree
disabled -> disable

matting -> mat
mating -> mate
meeting -> meet
milling -> mill
messing -> mess

meetings -> meet

*/

private void step1()
{
if (b[k] == 's')
{
if (ends("sses"))
k -= 2;
else if (ends("ies"))
setto("i");
else if (b[k - 1] != 's')
k--;
}
if (ends("eed"))
{
if (m() > 0)
k--;
}
else if ((ends("ed") || ends("ing")) && vowelinstem())
{
k = j;
if (ends("at"))
setto("ate");
else if (ends("bl"))
setto("ble");
else if (ends("iz"))
setto("ize");
else if (doublec(k))
{
k--;
int ch = b[k];
if (ch == 'l' || ch == 's' || ch == 'z')
k++;
}
else if (m() == 1 && cvc(k)) setto("e");
}
}

/* step2() turns terminal y to i when there is another vowel in the stem. */
private void step2()
{
if (ends("y") && vowelinstem())
b[k] = 'i';
}

/* step3() maps double suffices to single ones. so -ization ( = -ize plus
-ation) maps to -ize etc. note that the string before the suffix must give
m() > 0. */
private void step3()
{
if (k == 0)
return;

/* For Bug 1 */
switch (b[k - 1])
{
case 'a':
if (ends("ational")) { r("ate"); break; }
if (ends("tional")) { r("tion"); break; }
break;
case 'c':
if (ends("enci")) { r("ence"); break; }
if (ends("anci")) { r("ance"); break; }
break;
case 'e':
if (ends("izer")) { r("ize"); break; }
break;
case 'l':
if (ends("bli")) { r("ble"); break; }
if (ends("alli")) { r("al"); break; }
if (ends("entli")) { r("ent"); break; }
if (ends("eli")) { r("e"); break; }
if (ends("ousli")) { r("ous"); break; }
break;
case 'o':
if (ends("ization")) { r("ize"); break; }
if (ends("ation")) { r("ate"); break; }
if (ends("ator")) { r("ate"); break; }
break;
case 's':
if (ends("alism")) { r("al"); break; }
if (ends("iveness")) { r("ive"); break; }
if (ends("fulness")) { r("ful"); break; }
if (ends("ousness")) { r("ous"); break; }
break;
case 't':
if (ends("aliti")) { r("al"); break; }
if (ends("iviti")) { r("ive"); break; }
if (ends("biliti")) { r("ble"); break; }
break;
case 'g':
if (ends("logi")) { r("log"); break; }
break;
default:
break;
}
}

/* step4() deals with -ic-, -full, -ness etc. similar strategy to step3. */
private void step4()
{
switch (b[k])
{
case 'e':
if (ends("icate")) { r("ic"); break; }
if (ends("ative")) { r(""); break; }
if (ends("alize")) { r("al"); break; }
break;
case 'i':
if (ends("iciti")) { r("ic"); break; }
break;
case 'l':
if (ends("ical")) { r("ic"); break; }
if (ends("ful")) { r(""); break; }
break;
case 's':
if (ends("ness")) { r(""); break; }
break;
}
}

/* step5() takes off -ant, -ence etc., in context <c>vcvc<v>. */
private void step5()
{
if (k == 0)
return;

/* for Bug 1 */
switch (b[k - 1])
{
case 'a':
if (ends("al")) break; return;
case 'c':
if (ends("ance")) break;
if (ends("ence")) break; return;
case 'e':
if (ends("er")) break; return;
case 'i':
if (ends("ic")) break; return;
case 'l':
if (ends("able")) break;
if (ends("ible")) break; return;
case 'n':
if (ends("ant")) break;
if (ends("ement")) break;
if (ends("ment")) break;
/* element etc. not stripped before the m */
if (ends("ent")) break; return;
case 'o':
if (ends("ion") && j >= 0 && (b[j] == 's' || b[j] == 't')) break;
/* j >= 0 fixes Bug 2 */
if (ends("ou")) break; return;
/* takes care of -ous */
case 's':
if (ends("ism")) break; return;
case 't':
if (ends("ate")) break;
if (ends("iti")) break; return;
case 'u':
if (ends("ous")) break; return;
case 'v':
if (ends("ive")) break; return;
case 'z':
if (ends("ize")) break; return;
default:
return;
}
if (m() > 1)
k = j;
}

/* step6() removes a final -e if m() > 1. */
private void step6()
{
j = k;

if (b[k] == 'e')
{
int a = m();
if (a > 1 || a == 1 && !cvc(k - 1))
k--;
}
if (b[k] == 'l' && doublec(k) && m() > 1)
k--;
}

/** Stem the word placed into the Stemmer buffer through calls to add().
* Returns true if the stemming process resulted in a word different
* from the input. You can retrieve the result with
* getResultLength()/getResultBuffer() or toString().
*/
public void stem()
{
k = i - 1;
if (k > 1)
{
step1();
step2();
step3();
step4();
step5();
step6();
}
i_end = k + 1;
i = 0;
}
}



/// <summary>Filters {@link StandardTokenizer} with {@link StandardFilter}, {@link
/// LowerCaseFilter}, {@link StopFilter} and {@link SnowballFilter}.
///
/// Available stemmers are listed in {@link SF.Snowball.Ext}. The name of a
/// stemmer is the part of the class name before "Stemmer", e.g., the stemmer in
/// {@link EnglishStemmer} is named "English".
/// </summary>
public class SnowballAnalyzer : Analyzer
{
private System.String name;
private System.Collections.Hashtable stopSet;//停用词表
private string mModelPath; //词性标注软件模型所在位置

/// <summary>Builds the named analyzer with no stop words. </summary>
public SnowballAnalyzer(System.String name)
{
//获得词性标注软件模型所在位置。模型文件一般放在本工程下面
mModelPath = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
mModelPath = new System.Uri(mModelPath).LocalPath + @"\Models\";
this.name = name;
}

/// <summary>Builds the named analyzer with the given stop words. </summary>
public SnowballAnalyzer(System.String name, System.String[] stopWords)
: this(name)
{
stopSet = StopFilter.MakeStopSet(stopWords);
}

/// <summary>Constructs a {@link StandardTokenizer} filtered by a {@link
/// StandardFilter}, a {@link LowerCaseFilter} and a {@link StopFilter}.
/// </summary>
public override TokenStream TokenStream(System.String fieldName, System.IO.TextReader reader)
{
TokenStream result = new StandardTokenizer(reader);
result = new StandardFilter(result);
result = new LowerCaseFilter(result);
if (stopSet != null)
result = new StopFilter(result, stopSet);
//从result NokenStream 分离出Token 来,确定词性。
// result = new SnowballFilter(result, name);
return result;
}
/// <summary>
/// 根据Snowball源码自己写的 Analyzer,返回词汇实体列表代替原来调用的TokenStream
///
/// </summary>
/// <param name="fieldName"></param>
/// <param name="reader"></param>
/// <returns></returns>
public List<myEwordEntity> TokenStreamToEntityList(System.String fieldName, System.IO.TextReader reader)
{
TokenStream result = TokenStream(fieldName, reader);
// TokenStream result2 = TokenStream(fieldName, reader);

List<myEwordEntity> wordEnList = new List<myEwordEntity>();
while (true)
{
Token token = result.Next();
myEwordEntity entity = new myEwordEntity();
if (token == null)
break;
else
{
entity.token_begin = token.StartOffset();
entity.token_end = token.EndOffset();
entity.txtWord = token.TermText();//获得词汇文本
entity.stemroot = AfterStemed(entity.txtWord);
wordEnList.Add(entity);

}
}

ArrayList myposlist = new ArrayList();
foreach (myEwordEntity entity in wordEnList)
{
myposlist.Add(entity.txtWord);
}
EnglishMaximumEntropyPosTagger mTager = new EnglishMaximumEntropyPosTagger(mModelPath + "EnglishPOS.nbin", mModelPath + @"\Parser\tagdict");
myposlist = mTager.Tag(myposlist);
for (int i = 0; i < myposlist.Count; i++)
{
wordEnList[i].posWord = myposlist[i].ToString();
}

//对每个词汇进行词根还原
/* result2 = new SnowballFilter(result2, name);
int k=0;//工作下标
while(true)
{
Token token=result2.Next();
if (token == null)
break;
else
{
wordEnList[k].stemroot= token.TermText();
k++;
}

}*/
return wordEnList;


}
public string AfterStemed(string input)
{
Stemmer s = new Stemmer();

input = input.ToLower();
char[] inputchar = input.ToCharArray();
s.add(inputchar, inputchar.Length);
s.stem();
string u = s.stemerToString();
return u;


}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值