#!/usr/bin/perl
use strict;
#use warnings;
use Encode;
use Win32::GUI();
########################## 第一个窗口“短信发送端” ####################
my $DOS = Win32::GUI::GetPerlWindow(); #这里目的是运行程序时隐藏dos界面
Win32::GUI::Hide($DOS);
my $win1 = Win32::GUI::Window->new( #创建短信发送端窗口
-name => 'mainWindow1',
-title => '短信发送端',
-pos => [150, 300],
-size => [400, 200],
);
$win1->AddLabel(
-name => 'Label1',
-title => '本机号码:',
-pos => [20,20],
);
$win1->AddTextfield(
-name => 'Edit1',
-pos => [80,15],
-size => [100,20],
);
$win1->AddLabel(
-name => 'Label2',
-title => '短信:',
-pos => [20,40],
);
$win1->AddTextfield(
-name => 'Edit2',
-multiline => 1,
-pos => [20,60],
-size => [280,90],
);
$win1->AddButton(
-name => 'ResetSms',
-title => '重置',
-pos => [310,70],
-size => [60,20],
);
$win1->AddButton(
-name => 'SentSms',
-title => '发送',
-pos => [310,100],
-size => [60,40],
-ok => 1,
);
$win1->AddTimer('T1', 1000); #这里是为了在窗口中显示系统时间
sub T1_Timer {
my $time = localtime();
$win1->AddLabel(
-title => $time,
-pos => [210,20],
);
}
######################### 第二个窗口“短信接收端” #########################
my $win2 = Win32::GUI::Window->new( #创建“短信接收端”窗口
-name => 'mainWindow2',
-title => '短信接收端',
-pos => [700, 200],
-size => [400, 500],
);
$win2->AddLabel(
-name => 'Label3',
-title => '原始短信:',
-pos => [20,20],
);
$win2->AddLabel(
-name => 'Label4',
-title => '发信人:',
-pos => [30,45],
);
$win2->AddTextfield(
-name => 'Edit3',
-pos => [90,40],
-size => [100,20],
);
$win2->AddLabel(
-name => 'Label5',
-title => '发送时间:',
-pos => [30,75],
);
$win2->AddTextfield(
-name => 'Edit4',
-pos => [90,70],
-size => [180,20],
);
$win2->AddLabel(
-name => 'Label6',
-title => '内容:',
-pos => [30,105],
);
$win2->AddTextfield(
-name => 'Edit5',
-multiline => 1,
-pos => [90,100],
-size => [280,90],
);
$win2->AddLabel(
-name => 'Label7',
-title => '通讯录:',
-pos => [20,220],
);
$win2->AddLabel(
-name => 'Label8',
-title => '手机:',
-pos => [30,250],
);
$win2->AddTextfield(
-name => 'Edit6',
-pos => [60,245],
-size => [100,20],
);
$win2->AddLabel(
-name => 'Label9',
-title => '座机:',
-pos => [30,275],
);
$win2->AddTextfield(
-name => 'Edit7',
-pos => [60,270],
-size => [100,20],
);
$win2->AddLabel(
-name => 'Label10',
-title => '备注:',
-pos => [30,300],
);
$win2->AddTextfield(
-name => 'Edit8',
-pos => [60,300],
-size => [100,110],
-multiline => 1,
);
$win2->AddLabel(
-name => 'Label11',
-title => '记事本:',
-pos => [190,220],
);
$win2->AddLabel(
-name => 'Label12',
-title => '时间:',
-pos => [200,250],
);
$win2->AddTextfield(
-name => 'Edit9',
-pos => [230,245],
-size => [140,50],
-multiline => 1,
);
$win2->AddLabel(
-name => 'Label13',
-title => '地点:',
-pos => [200,305],
);
$win2->AddTextfield(
-name => 'Edit10',
-pos => [230,300],
-size => [140,50],
-multiline => 1,
);
$win2->AddLabel(
-name => 'Label14',
-title => '动作:',
-pos => [200,365],
);
$win2->AddTextfield(
-name => 'Edit11',
-pos => [230,360],
-size => [140,50],
-multiline => 1,
);
$win1->AddTimer('T2', 1000); #为了在窗口中动态显示系统时间
sub T2_Timer {
my $time = localtime();
$win2->AddLabel(
-title => $time,
-pos => [210,20],
);
}
$win1->Edit1->SetFocus(); #为后面对文本框的处理作准备
$win1->Edit2->SetFocus();
$win2->Edit3->SetFocus();
$win2->Edit4->SetFocus();
$win2->Edit5->SetFocus();
$win2->Edit6->SetFocus();
$win2->Edit7->SetFocus();
$win2->Edit8->SetFocus();
$win2->Edit9->SetFocus();
$win2->Edit10->SetFocus();
$win2->Edit11->SetFocus();
$win1->Show(); #显示窗口
$win2->Show();
Win32::GUI::Dialog();
Win32::GUI::Show($DOS); #程序运行结束后让dos界面重新显示
exit(0);
########################## 某些子函数 #############################
sub mainWindow1_Terminate {
return -1;
}
sub mainWindow2_Terminate {
return -1;
}
sub ResetSms_Click { #对“重置”按钮设置功能,也就是将发送端短信输入框中内容清零
$win1->Edit2->Text("");
}
sub SentSms_Click { #对“发送”按钮设置功能
my $text1 = $win1->Edit1->Text; #获取文本框中的内容
my $text2 = $win1->Edit2->Text;
my $filename = localtime(); #获取系统时间
$filename =~ s/:/-/g; #由于要用系统时间来作为文件名存储短信,所以需要改成合适的表示方式
$filename = $filename.'.txt';
open FILE, ">", "sms\\$filename" || die "Cannot open file: $!";
print FILE $text1,"\n",$text2; #存储原始短信
close(FILE);
$win1->Edit2->Text("发送中……");
$win2->Edit3->Text(""); #每次发短信,都要将见面上原来的痕迹先擦除
$win2->Edit4->Text("");
$win2->Edit5->Text("");
$win2->Edit6->Text("");
$win2->Edit7->Text("");
$win2->Edit8->Text("");
$win2->Edit9->Text("");
$win2->Edit10->Text("");
$win2->Edit11->Text("");
main($text2); #调用主体函数main(),实现对短信的操作
display_osms($text1, $text2, $filename); #显示原始短信
$win1->Edit2->Text("发送成功!");
}
sub display_osms { #显示原始短信
my $num = $_[0];
my $sms = $_[1];
my $filename = $_[2];
$filename =~ s/-/:/g;
$filename =~ s/.txt//;
$win2->Edit3->Text($num);
$win2->Edit4->Text($filename);
$win2->Edit5->Text($sms);
}
############################### 主函数 ########################
sub main {
my $sms = $_[0]; #接收短信内容
$sms = tel($sms); #先查看短信中是否有电话号码并操作
$sms = event_time($sms); #查看短信中事件的时间信息并操作
$sms = participle($sms); #对消去电话号码和事件时间后的短信分词
event_main($sms); #查找事件的地点和动作
sub tel { #短信中电话号码操作
my $tel;
my $i;
my $line = $_[0];
chomp($line);
while ($line =~ /([0-9|+][0-9]*-?[0-9]+)/g) { #正则匹配
$tel = $1;
if (length($tel) >= 5 && length($tel) <= 20) { #设定长度的范围在5和20之间
$line =~ s/$tel//; #将原始短信消去电话号码的部分
if ($tel =~ /^(13|18|15|\+8613|\+8618|\+8615)/) { #如果符合这些规则,则判为手机号码
$win2->Edit6->Text($tel);
}
else { #否则,都判为座机号码
$win2->Edit7->Text($tel);
}
}
}
return $line;
}
sub event_time { #对短信中事件发生时间操作
my $_2 = "3-9-10"; #创建状态转移表
my $_8 = "9-10";
my $_9 = "11-12-9-10";
my @tran = (["4", "2", "7", "7", "7", "7", "8", "8", "8", "5", "6", "6", $_8, "3", "7"],["wait", "wait", "wait", "wait", "wait", "wait", "wait", "wait", "wait"],[$_2, $_2, $_2, $_2, $_2, $_2, $_2, $_2, $_2, $_2, $_2, $_9, "wait"],["2"],["wait"],["wait"],["wait", "wait", "wait", "wait"],["wait", "wait"],["10", "wait", "wait", "wait"],["10", "10", "wait", "10", "wait", "10", "wait", "wait"],[$_9],["13","13"],["14"],["end"],["end","end","15", "wait"]);
my %hash;
my @line;
my $line;
my $j = 0;
my $k;
my $m;
my $sms = $_[0];
my $time = "";
open FILE, "<", "time.txt" || die "Cannot open file: $!";
while (<FILE>) {
chomp;
$line = decode("gbk", $_);
@line = split (/ /, $line);
for ($k = 0; $k < @line; ++ $k) {
$hash{$j + 1}{$line[$k]} = $tran[$j][$k];
}
++ $j;
}
close(FILE);
chomp($sms);
$line = decode("gbk", $sms);
@line = split(//, $line); #将短信按字分开存入数组,然后使用状态机,逐字查看
$m = 0;
hello(-1, 0); #调用函数
sub hello { #有限状态机核心算法
my $index = $_[0]; #字符串上的下标
my $state = $_[1]; #状态号
my @next;
my $i;
while ($index + 1 < @line) { #遍历直到找到被接受的字符串或者到达原字符串的最后
if ($state == 0) { #如果$state
@next = (1, 3); #时间的第一个字必然在状态1或者状态3
for ($i = 0; $i < @next; ++ $i) {
if ($line[$index + 1] =~ /[0-9]/ || exists $hash{$next[$i]}{$line[$index + 1]}) {
$time = $time.encode("gbk", $line[$index + 1]);
$state = $next[$i];
hello($index + 1, $state);
last;
}
}
}
else {
if ($line[$index] =~ /[0-9]/) {
@next = split (/-/, $hash{$state}{"[0-9]"});
}
else {
@next = split (/-/, $hash{$state}{$line[$index]});
}
for ($i = 0; $i < @next; ++ $i) {
if ((exists $hash{$next[$i]}{"[0-9]"} && $line[$index + 1] =~ /[0-9]/ ) || exists $hash{$next[$i]}{$line[$index + 1]}) {
$time = $time.encode("gbk", $line[$index + 1]);
$state = $next[$i];
hello($index + 1, $state);
}
else{
if ($next[$i] eq "end") {
$m = 1;
last;
}
elsif ($next[$i] eq "wait") {
if ($line[$index + 1] =~ /[0-9]/ || exists $hash{3}{$line[$index + 1]}) {
$time = $time.encode("gbk", $line[$index + 1]);
$state = 3;
hello($index + 1, $state);
}
elsif (exists $hash{1}{$line[$index +1]}) {
$time = $time.encode("gbk", $line[$index + 1]);
$state = 1;
hello($index + 1, $state);
}
elsif (exists $hash{15}{$line[$index + 1]}) {
$time = $time.encode("gbk", $line[$index + 1]);
$state = 15;
hello($index + 1, $state);
}
else {
$m = 1;
last;
}
}
}
if ($m == 1) {
last;
}
}
}
if ($m == 1) {
last;
}
++ $index;
$state = 0;
$time = "";
}
}
if ($time ne "") { #如果找出时间了,则在界面中显示出来
$win2->Edit9->Text($time);
}
$sms =~ s/$time//;
return $sms;
}
sub participle { #用前向最大匹配算法分词
my %hash; #用于存放词表的哈希表
my $line;
my $f1;
my $fs;
my $fw;
my $sms = $_[0];
open FILE,"vocabulary.txt" || die "Cannot open file: $!";
while (<FILE>) {
chomp;
$line=decode("gbk",$_); #用gbk编码
$hash{$line}='1';
}
close(FILE);
$f1=decode("gbk",$sms); #用gbk编码
$fs=""; #用来存放每段切分好了的文本
while ($f1) {
$fw=substr $f1,0,4; #将前四个字(包括标点)作为候选字符串$fw
while (!(exists $hash{$fw})&&(length $fw)!=1) { #当$fw在词表中没匹配成功且长度大于单字时
$fw=substr $fw,0,((length $fw)-1); #将$fw最右边一个字去掉
}
$fs=$fs.$fw." ";
$f1=substr $f1,(length $fw),(length $f1); #将待切分字符串去掉已经匹配成功的候选字符串
}
return encode("gbk", $fs);
}
sub event_main {
my @line;
my %hash;
my $i;
my $j;
my $k;
my @v;
my @a;
my $line;
my $ad;
my $veb;
my $maxlen = 2;
my @sign = qw(学校 大学 中学 小学 街 路 门 楼 国 省 市 区 镇 村); #以这些字词作尾的就判为地点
my $sms = $_[0];
my $filename = $_[1];
open FILE, "<", "parse.txt" || die "Cannot open file: $!"; #读入训练好了的规则库
while (<FILE>) {
chomp;
@line = split (/\//, $_);
$hash{$line[0]} = $line[1];
}
close(FILE);
@line = split (/\s+/, $sms);
for ($i = 0; $i < @line; ++ $i) { #找出词性为动词的词
if ((exists $hash{$line[$i]}) && ($hash{$line[$i]} eq "v")) {
push(@v, $i);
}
for ($j = 0; $j < @sign; ++ $j) {
if ($line[$i] =~ /$sign[$j]$/) {
push(@a, $i);
}
}
}
if (@a > 0) { #如果找出了地点
my $ad = "";
$j = 0;
for ($i = 0; $i < @a; ++ $i) {
if ($a[$i] - $j > $maxlen) {
$ad = $ad.' ';
for ($k = $a[$i] - $maxlen; $k <= $a[$i]; ++ $k) {
$ad = $ad.$line[$k];
}
}
else {
for ($k = $j; $k <= $a[$i]; ++ $k) {
$ad = $ad.$line[$k];
}
}
$j = $a[$i] + 1;
}
$win2->Edit10->Text($ad); #在界面中显示出来
}
if (@v > 0) { #如果找到了动词
$veb = "";
for ($i = 0; $i < @v; ++ $i) {
$veb = $veb.$line[$v[$i]].' ';
}
$win2->Edit11->Text($veb); #在界面中显示出来
}
}
}
|