超难问题!如何将一个句子中的所有词组提取出来? ( 积分: 46 )

  • 主题发起人 主题发起人 batconv
  • 开始时间 开始时间
B

batconv

Unregistered / Unconfirmed
GUEST, unregistred user!
如下面的句子:
“感谢大家给我的鼓励,我一直有个想法,就是将我的故事写出来,但是我的文采不行,所以一直没有实施,我想在这里,借红豆的一席宝地,慢慢的将我已经尘封的故事写出来。”

如何将词组如:感谢,大家,鼓励,一直,想法,故事,写出来,但是,文采,不行,所以,一直,没有,实施,在这里,红豆,一席,宝地,慢慢的,已经,尘封,故事。

提取出来,要有通用性,关键是如何自动判断是否是词组,有高手研究过这个问题吗?给小弟提供点思路,最好有代码,谢谢。
 
那你不是需要做一个词组的库?要不程序如何给你判断?
 
如果不用词组库有办法吗?
 
觉得应该是用词组库的,找找看有没有词典的词库,下下来,然后
 
我已经转换了个五笔的词库成文本文件,但不知有什么好的算法可以提高效率,不知道有哪个控件支持读取文本文件用SQL查询
 
用"清华计算语言所"的词库吧(一个月的人民日报)

就是分词嘛

我有源码~
 
三. 算法一览
 基于字符串匹配
这种方法又叫做机械分词方法,它是按照一定的策略将待分析的汉字串与一个「充分大的」机器词典中的词条进行配,若在词典中找到某个字符串,则匹配成功(识别出一个词)。常用的集中匹配分词方法:
1. 最大正向匹配: 从左到右匹配
2. 最大逆向匹配: 从右到左匹配
3. 最小切分: 使每一句中切出的词的数量最少。

 基于统计
基本思想是:
1. 一个待切分的汉字串可能包含多种分词结果
2. 将其中概率最大的那个作为该字串的分词结果

有 意 见 分 歧
0 1 2 3 4 5

路径1: 0-1-3-5 (有/意见/分歧)
路径2: 0-2-3-5 (有意/见/分歧)

该走哪条路呢? 我们选概率最大的那一条路径。

 基于理解
这种分词方法是通过让计算机模拟人对句子的理解,达到识别词的效果。其基本思想就是在分词的同时进行句法、语义分析,利用句法信息和语义信息来处理歧义现象。它通常包括三个部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统可以获得有关词、句子等的句法和语义信息来对分词歧义进行判断,即它模拟了人对句子的理解过程。这种分词方法需要使用大量的语言知识和信息。由于汉语语言知识的笼统、复杂性,难以将各种语言信息组织成机器可直接读取的形式,因此目前基于理解的分词系统还处在试验阶段。
 算法综合
目前比较成熟的分词算法一般都综合了匹配与统计,并使用一些简单的语法规则(理解)。
 
果然高手,佩服!
我不需要那么复杂,只想找出每个句子中比较关键的几个词组,可以提供点示例代码吗,谢谢。batconv@163.com
 
这是一个人名识别的单元

////////////////////////////////////////////////////////////////////////////
//使用说明:这是一个抽象类,该类被treeCutWord类继承 // //
// 1,设置TRecgName的DictPath(姓名字典的路径)。 //
// 2,覆盖类的抽象方法---GetsFee,GetgFee,GetsgFee,ComputeName. //
// 3,调用 CheckName方法分析字符串中的姓名。该方法将姓名的前后加上 //
// 分隔标志(StartSplit,EndSplit),然后返回加上了分隔标志的字串。 //
////////////////////////////////////////////////////////////////////////////
unit RecgName;

interface
uses SysUtils, StrUtils, Variants, Classes, Dialogs, untDict;
const
HZ_NUM=6768; //国标码中汉字的个数
CorpusSize=200000; // 语料库规模,用于计算词的出现概率
SurNameSize=174000; // 姓名语料库中姓氏用字总数
GivenNameSize=320000;// 姓名语料库中人名用字总数
Max2Fee=-2.14; //2个字的姓名的费用最大值
Max3Fee=-0.80; //3个字的姓名的费用最大值

hanzixm='hanzixm.txt'; //字典文件名


type
PMaybeName=^MaybeName;
MaybeName=record //可能是名字的词
offset,len:smallint; //偏移,长度
fee:double; //费用
end;

type
PNameFreq=^recNameFreq;
recNameFreq=record
HanZi:Word;
wSFreq,wGFreq:Word;
end;


type TNameDict=class
private
HZFreqs:array[0..65535] of PNameFreq; //保存汉字作为人名和姓氏的费用 。因为GBK码共有汉字20902个,所以数组大小为20902
function GetIndex(character:string):word; //获得数组索引
procedure InitArray;

public
protected
//procedure SetArrayValue(C:string;sf:word=0;gf:word=0);
published
constructor create(Apath:string);
destructor destroy;override;
procedure LoadNameDict(APath:string); //载入汉字作为姓,名,的费用表
function GetsNameFreq(character:string):Word; //查找汉字当作姓的频率
function GetgNameFreq(character:string):Word; //查找汉字当作名的频率

end;

type TRecgName=class
private
FNameDict:TNameDict;
FDict:TInterfacedDict;
MaybeNames:TList;
lstName:TStringList; //保存所有姓名
public
constructor Create(APath:string);
destructor Destroy;override;
function isCrossPair(p1, p2: PMaybeName): boolean; //侯选姓名是否有重叠
function isHomoPair(p1, p2: PMaybeName): boolean; //侯选姓名是否有相同的起点
published
function CheckName(S, StartSplit, EndSplit: string): string; //检查分词碎片
function GetgNameFreq(character: string): Word;
function GetsNameFreq(character: string): Word;

function GetsFee(character:string):Double;virtual;//abstract; //查找姓的费用
function GetgFee(character:string):Double;virtual;//abstract; //查找名的费用
function GetsgFee(character:string):Double;virtual;//abstract; //姓+名的费用

procedure ClearNames;
function GetNames:TStringList;
end;
implementation
{ TNameDict }

constructor TNameDict.create(Apath:string);
begin
InitArray;
LoadNameDict(Apath+hanzixm);
end;

destructor TNameDict.destroy;
var
i:Word;
begin
for i:=0 to 65535 do
Dispose(hzfreqs);

inherited;
end;

function TNameDict.GetgNameFreq(character: string): Word;
begin
Result:=HZfreqs[GetIndex(character)].wGFreq;
end;

function TNameDict.GetIndex(character: string): word;
begin
Result:=Word(byte(character[1]) shl 8) + Word(character[2]);
end;

function TNameDict.GetsNameFreq(character: string): Word;
begin
Result:=HZfreqs[GetIndex(character)].wSFreq;
end;

procedure TNameDict.InitArray;
var
i:Word;
P:PNameFreq;
begin
for i:=0 to 65535 do
begin
New(HZFreqs);
HZFreqs.HanZi:=i;
HZFreqs.wSFreq:=0;
HZFreqs.wGFreq:=0;
end;
end;

procedure TNameDict.LoadNameDict(APath: string);
var
F:TextFile;
Line,HanZi:string;
QuotePos1,QuotePos2:Integer;
index:Word;
begin
try
AssignFile(F,APath);
//ShowMessage(APath);
Reset(F);
while (not Eof(F)) do
begin
Readln(F,line);
QuotePos1:=Pos('"',Line);
QuotePos2:=LastDelimiter('"',Line);
HanZi:=Copy(Line,QuotePos1+1,QuotePos2-QuotePos1-1);

index:=GetIndex(HanZi);

QuotePos1:=Pos(#9,Line);
QuotePos2:=LastDelimiter(#9,Line);
if QuotePos2-QuotePos1<>1 then
HZFreqs[index].wSFreq:=StrToInt(Copy(Line,QuotePos1+1,QuotePos2-QuotePos1-1)); //获取姓氏频度
if QuotePos2<>Length(Line) then
HZFreqs[index].wGFreq:=StrToInt(Copy(Line,QuotePos2+1,Length(Line)-QuotePos2));//获取人名频度

end;
finally
CloseFile(F);
end;
end;



{ TRecgName }

function TRecgName.CheckName(S, StartSplit, EndSplit: string): string;
var
i,j,len,Slen:integer;
fee:double;
PName,PName1,PName2:PMaybeName; //会不会被过程里的局部变量释放掉?
iDelete:boolean;
S2,W:string;
preName,sucName:string;
begin
Slen:=length(S) div 2;
for i:=1 to Slen do //遍历句子里的每个字
begin
len:=2;
while ((len<=3) and (i+len-1<=Slen)) do //向前取2个字或3个字,看是否是姓名
begin
fee:=GetsgFee(copy(S,i*2-1,len*2)); //计算一下当作“姓+名”模式的费用
//ShowMessage(copy(S,i*2-1,len*2)+FloatToStr(fee)); ////////////////////////////////////
if ((len=2) and (fee>=Max2Fee)) or ((len=3) and (fee>=Max3Fee)) then //费用超过最大值,不是姓名
begin
inc(len);
continue;
end;

new(PName); //否则,存在这个姓名,将它加入到列表
PName^.offset :=i * 2-1;
PName^.len:=len *2;
PName^.fee:=fee;
MaybeNames.Add(PName);
inc(len);
end;
end;

iDelete:=false;

i:=0;
while (i< MaybeNames.Count) do
begin
PName1:=PMaybeName(MaybeNames.Items);
//ShowMessage(Copy(s, Pname1.offset,pname1.len) );
j:=i+1;
while ((j<= i+2) and (j<MaybeNames.Count) ) do //只比较临近的2个姓名
begin
PName2:=PMaybeName(MaybeNames.Items[j]);
//ShowMessage(Copy(s, Pname2.offset,pname2.len) );
if (isHomoPair(PName1,PName2)) then //2个姓名是否有相同的起点
if (PName1^.fee >PName2^.fee) then //比较姓名的费用,删除
begin
MaybeNames.Delete(i); //删除
iDelete:=true;
break;
end
else
begin
MaybeNames.Delete(j);
end
else
if (isCrossPair(PName1,PName2)) then //2个姓名是否有重叠部分

if (PName1^.fee >PName2^.fee) then
begin
MaybeNames.Delete(i); //删除

iDelete:=true;
break;
end
else
begin
MaybeNames.Delete(j);
end
else //如果有重复,下一个词的比较没有必要,所以inc(j).
inc(j);
end; //内层while循环结束

if not iDelete then
inc(i) //跳过下次循环
else
iDelete:=false;
end;//最外层while循环结束
//----------------------------------------


s2:='';
if MaybeNames.Count<1 then //不存在姓名,作为单字输出。。
begin
for i:=1 to Slen do
s2:=s2+copy(S,i*2-1,2)+' ';
Result:=s2;
exit;
end;

for i:=0 to MaybeNames.Count-1 do
begin
if i=0 then
j:=1
else
j:=Pname^.offset+PName^.len;

PName:=PMaybeName(MaybeNames.Items);
while(j<PName.offset) do
begin
S2:=S2+copy(S,j,2)+' ';
inc(j,2);
end;

W:=copy(S,PName^.offset ,PName^.len);
if lstName.IndexOf(W)=-1 then //不能重复
lstName.Add(W);

S2:=S2+ StartSplit+W+endsplit; //加上词性标志

//preName:=leftbstr(W,2);
//sucName:=copy(W,3,2);

end;
j:=PName^.offset+PName^.len;
while(j<Slen*2) do
begin
S2:=S2+copy(s,j,2)+' ';
inc(j,2);
end;

for i:=MaybeNames.Count-1 downto 0 do
MaybeNames.Delete(i);

Result:=S2;
end;

procedure TRecgName.ClearNames;
begin
lstName.Clear;
end;

constructor TRecgName.Create(APath:string);
begin
//ShowMessage(APath);
FNameDict:=TNameDict.create(APath);
//FDict:=TDict.CreateInstance(APath); //不创建单例类的实例。只是在要用到的地方实时地引用
MaybeNames:=TList.Create;
lstName:=TStringList.create;
end;

destructor TRecgName.Destroy;
begin
FNameDict.Free;
//TDict(FDict).ReleaseInstance;
MaybeNames.Free;
lstName.Free;
inherited;
end;

function TRecgName.GetgFee(character: string): Double;
var
wFreq:Word;
wFee,Fee:Double;
index,ID:Word;
nd:TCharTreeNode;
begin
FDict:=TDict.GetInstance(0);
nd:=FDict.GetRoot(character);
wFreq:=FDict.GetFreq(nd);
wFee:=-ln((wFreq+1.000000)/CorpusSize);

Fee:=GetgNameFreq(character);

ID:=(Byte(character[1])-176)*94+byte(character[2])-161;
if (ID>=0) and (ID<=HZ_NUM) and (Fee>0.0) then
if Fee>0.0 then
Result:=-ln((fee +1.00000)/GivenNameSize)-wFee
else
Result:=-ln(1.000000/GivenNameSize)-wFee //作为姓的费用减去作为单字的费用
else
Result:=20.000000;
end;

function TRecgName.GetgNameFreq(character: string): Word;
begin
Result:=FNameDict.GetgNameFreq(character);
end;

function TRecgName.GetNames: TStringList;
begin
Result:=lstName;
end;

function TRecgName.GetsFee(character: string): Double;
var
wFreq:Word;
wFee,Fee:Double;
index,ID:Word;
nd:TCharTreeNode;
begin
FDict:=TDict.GetInstance(0);
nd:=FDict.GetRoot(character);
wFreq:=FDict.GetFreq(nd);
wFee:=-ln((wFreq+1.000000)/CorpusSize); //作为单字使用的费用

Fee:=GetsNameFreq(character); //作为姓的费用

ID:=(Byte(character[1])-176)*94+byte(character[2])-161;
if (ID>=0) and (ID<=HZ_NUM) and (Fee>0.0) then
Result:=-ln((fee +1.000000)/SurNameSize)-wFee //作为姓的费用减去作为单字的费用
else
Result:=20.000000;

end;


function TRecgName.GetsgFee(character: string): Double;
var
fee:double;
preName,sucName,lstName:string;
preByte,sucByte:byte;
begin
preName:=leftbstr(character,2);
sucName:=copy(character,3,2 );
lstName:=RightBStr(character,2);
if GetsNameFreq(preName)>0 then //字串最左边的一个字是否是姓
begin
fee:=GetsFee(preName)+GetgFee(sucName);
if (Length(character)=4) then
fee:=fee-ln(0.370000)
else
fee:=fee+GetgFee(lstName)+(-ln(0.630000));
end
else //全部当作名的费用
begin
if (Length(character)>6) then
fee:=20.000000
else if (Length(character)=4) then
fee:=GetgFee(preName)+GetgFee(lstName);
end;
Result:=fee;
end;

function TRecgName.GetsNameFreq(character: string): Word;
begin
Result:=FNameDict.GetsNameFreq(character);
end;

function TRecgName.isCrossPair(p1, p2: PMaybeName): boolean;
begin
Result:=not ((p1^.offset=p2^.offset) or (p1^.offset+p1^.len<=p2^.offset)or(p2^.offset+p2^.len<=p1^.offset));
end;

function TRecgName.isHomoPair(p1, p2: PMaybeName): boolean;
begin
Result:=(p1^.offset=p2^.offset);
end;

end.
 
to linuxping,
非常感谢,我学习一下先。
 
如果你问中文分词技术会显得更专业一点
 
to batconv:
我不是高手!
 
to wr960204,
呵呵,主要是我太不专业了:)谢谢
 
如果你想花最少的钱学习流行的计算机技术,本人低价提供
计算机专业高清晰 视 频 教 程,所有教程由业内名家讲解,物超所值,
所有光盘4元/张,特快专递全国三天内到货.
联系方式:手 机:1 3 5 76145612(手机联系佳) Q Q:108410943
E-mail:ncncldjjg@126.com
1、Oracle9iOCP(Oracle认证专家DBA)视频教程(23CD)
2、SQL语言、数据挖掘、SQLServer2000视频教程(6CD)
3、计算机网络原理视频教程(4CD)
4、计算机实用组网技术视频教程(3CD)
5、MCSE(微软认证系统工程师)认证视频教程(11CD)
6、windows2003即学即会视频教程(6CD)
7、J2EE Web程序开发定向班/Java就业班视频教程
(java基础jsp核心技术UML设计)(13CD)
8、J2EE基础高级案例分析视频教程(9CD)
9、马士兵J2SE入门与精通(含坦克大战、BBS实例)视频教程(6CD)
10、java语言深入详解视频教程(12CD)
11、java技术从入门到精通视频教程(21CD)
12、javascript全接触视频教程(13CD)
13、各类网站源程序(9CD)
14、unix原理视频教程(3CD)
15、Linux软件开发工程师(C语言)视频教程(4CD)
16、Linux配置、管理、优化实战工程师视频教程(8CD)
17、Linux内核编程视频教程(11CD)
18、C语言详解视频教程(2CD)
19、VC++6.0(MFC)开发技术术详解视频教程(6CD)
20、ASP.NET中文视频教程(3CD)
21、洪恩C#入门多媒体教程(1CD)
22、XML技术视频教程(2CD)
23、操作系统视频教程(3CD)
24、网络技术基础视频教程(2CD)
25、数据结构视频教程(5CD)
26、清华大学计算机专业多媒体教程(数据结构、汇编、编译原理等)(13CD)
27、大学数学[离散数学线性代数概率与数理统计微积分]视频教程(22CD)
28、LearnKey Asp.Net (老外用英语讲课)(13CD)
29、LearnKey Asp.Net Webservice(老外用英语讲课)(5CD)
30、AppDev Asp.Net2.0 UsingC# (老外用英语讲课)(1CD)
31、APPDev .Net Framework(老外用英语讲课)(2CD)
32、林清安pro/e2001(野火版)视频教程(18CD)
33、深圳大学3D设计视频教程(4CD)
34、3DMAX建模视频教程(老外用英语讲课)(6CD)
35、3DMAX纹理与贴图材质灯光视频教程(老外用英语讲课)(5CD)
36、3DMAX格式500个常用模型库(4CD)
37、lightscape基础入门视频教程(2CD)
38、聚光制造(lightscape3.2入门与精通视频教程)(4CD)
39、coreldraw12中文版入门与精通swf格式视频教程(2CD)
 
后退
顶部