-
Notifications
You must be signed in to change notification settings - Fork 0
/
analyser.cpp
280 lines (250 loc) · 8.19 KB
/
analyser.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#include "analyser.h"
#include "parser.h"
#include <iostream>
analyser::analyser(){
context=CONTEXT_NORMAL;
ifstream ifs("dic\\after_rules.dic");
if(ifs.fail()) return;
BCSTR str;
while(getline(ifs,str)){
if(str.empty()) continue;
size_t pos=str.find("\t");
BCSTR left=str.substr(0,pos);
BCSTR right=str.substr(pos+1);
dic_after_rules[left]=right;
}
}
int analyser::load(BCSTR filename){
ifstream ifs(filename.c_str());
if (ifs.fail()) return ERR_INFILE;
BCSTR tmp;
while(getline(ifs, tmp)){
txt.push_back(tmp);
}
return ERR_NONE;
}
int analyser::generateTokens(){
if(txt.empty()) return ERR_NOT_LOADED;
mecab_t *mecab;
const BCCHAR *result;
int i;
size_t len;
int argc=3;
BCSTR prm1="brlcnv";
BCSTR prm2="-r";
BCSTR prm3="mecabrc";
BCCHAR* argv[3]={const_cast<BCCHAR*>(prm1.c_str()),const_cast<BCCHAR*>(prm2.c_str()),const_cast<BCCHAR*>(prm3.c_str())};
mecab=mecab_new(argc, argv);
if(!mecab) return ERR_MECAB_INSTANTIATE;
bool fail=false;
ofstream o("mecab_debug.txt");
for(auto itr=txt.begin();itr!=txt.end();++itr){
result = mecab_sparse_tostr(mecab,(*itr).c_str());
if(!result){
fail=true;
break;
}
BCSTR result_str=result;
o << endl << "-- new line --" << endl << result_str;
size_t index=0;
size_t pos;
bool brk=false;
//トークン分割
while(true){
if(index>=result_str.size()) break;
BCSTR item;
pos=result_str.find("\n",index);
if(pos==BCSTR_NPOS){
item=result_str.substr(index);
brk=true;
}else{
item=result_str.substr(index,pos-index);
}
if(item.find(",")==BCSTR_NPOS) break;
brlToken token;
token.import(item);
tokens.push_back(token);
if(brk) break;
index=pos+(sizeof(BCCHAR)*1);
}
//最後のトークンは行の最後
auto lt=tokens.end()-1;
(*lt).afterLinefeeds=1;
}
mecab_destroy(mecab);
if(fail) return ERR_MECAB_PARSE;
o.close();
return ERR_NONE;
}
int analyser::analyseTokens(){
if(tokens.size()==0) return ERR_NO_TOKENS;
brlToken *previous, *current, *next;
for(int i=0;i<tokens.size();i++){
if(i==0) previous=NULL;
else previous=&tokens[i-1];
if(i==tokens.size()-1) next=NULL;
else next=&tokens[i+1];
current=&tokens[i];
//助詞の後にはスペースを1個入れて、なおかつ、「ハ」「ヘ」は、「ワ」「エ」に置換。ただし、次に「記号」が現れるか、助詞が連続しているか、次のトークンが「よう」に一致する場合にはスペースを入れない。また例外として、「として」は「と して」に置換。
if(current->type=="助詞"){
if(next && next->type!="記号" && next->type!="助詞" && next->read!="ヨウ") current->afterSpaces=1;
if(current->read=="ハ") current->read="ワ";
if(current->read=="ヘ") current->read="エ";
if(current->read=="トシテ") current->read="ト シテ";
if(current->read.size()>2){//先頭から1文字数えて、それが「ニ」、「ヲ」なら、その後にスペースを入れる。(に対して」や「を通して」など)
BCSTR chars=current->read.substr(0,2);
if(chars=="ニ") current->read.replace(0,2,"ニ ");
else if(chars=="ヲ") current->read.replace(0,2,"ヲ ");
}
}
//助動詞の後にはスペース1個だが、いくつかの例外とと完全一致するか、後ろが記号の場合・助動詞にはキャンセル
if(current->type=="助動詞"){
if(current->read!="マショ" && current->read!="ダッ"){
if(next && next->type!="記号" && next->type!="助動詞") current->afterSpaces=1;
}
}
//トークン 「ソノ」と完全一致するときはスペース1個。GCCは0x5c問題を処理してくれないので、ちょっとへんだけどオマジナイ。
if(current->read=="ソ\ノ") current->afterSpaces=1;
//トークン 「ソンナ」と完全一致するときはスペース1個。GCCは0x5c問題を処理してくれないので、ちょっとへんだけどオマジナイ。
if(current->read=="ソ\ンナ") current->afterSpaces=1;
if(current->type=="記号"){
//記号「。」の後にはスペース2個、それ以外の後にはスペース1個。ただし、括弧のときは無視。
if(current->subType.find("括弧")==BCSTR_NPOS){
if(current->read=="。") current->afterSpaces=2;
else current->afterSpaces=1;
}
}
if(current->type=="名詞"){
//現在のトークンが名詞で、次が名詞か動詞ならスペース。ただし、名詞であっても、接尾属性が着いていたらキャンセル。
if(next){
if(next->type=="名詞" || next->type=="動詞"){
if(next->subType!="接尾") current->afterSpaces=1;
}
}
//名詞で、「ノ」と完全一致したら、以前のトークンのスペースを打ち消す(~~されたのが など)
if(previous && current->read=="ノ") previous->afterSpaces=0;
}
if(current->type=="形容詞"){
//今のところ、形容詞の後にはすべてスペースを空ける
current->afterSpaces=1;
//ただし、「ナク」に完全一致し、次が「ライ」であればキャンセル。「エクストリームなくらい」など。
if(next && current->read=="ナク" && next->read=="ライ") current->afterSpaces=0;
}
//現在のトークンが接続詞だったら、前後のスペースを強制的に1にする
if(current->type=="接続詞"){
if(previous && previous->type=="名詞"){
previous->afterSpaces=1;
current->afterSpaces=1;
}
}
//次のトークンが副詞ならスペース1個
if(next && next->type=="副詞") current->afterSpaces=1;
//現在のトークンが副詞ならスペース1個
if(current->type=="副詞") current->afterSpaces=1;
if(current->type=="動詞"){
//現在のトークンが動詞で、次が名詞ならスペース1個。(「買える力」、「作り上げること」など)
if(next && next->type=="名詞") current->afterSpaces=1;
}
//次のトークンが 動詞->サ変活用の場合はスペース1個
if(next && next->conjugationType.find("サ変")!=BCSTR_NPOS) current->afterSpaces=1;
//現在のトークンが 動詞->サ変活用->基本形の場合はスペース1個
if(current->conjugationType.find("サ変")!=BCSTR_NPOS && current->conjugationSubType=="基本形") current->afterSpaces=1;
//次のトークンが「ヨウ」であれば、スペースを打ち消す。置換前なので、検索ワードは「ヨウ」でいい。
if(next && next->read=="ヨウ") current->afterSpaces=0;
//先頭に現れない「ウ」は「ー」に置換
//ただし、文字列が「ウ」に完全一致する時だけはこの処理を飛ばす。
if(current->read!="ウ"){
while(true){
size_t pos=current->read.find("ウ",1);
if(pos==BCSTR_NPOS) break;//ウがもうないから抜ける
if(pos>0){
if(current->type=="動詞" && pos>=current->read.size()-2) break;//動詞のときは、最後の「ウ」であれば置換しないで抜ける
else current->read.replace(pos,2,"ー");
}
}
}else{//「ウ」と完全一致なので「ー」に置換
current->read="ー";
}
//前のトークンが数のとき、「ツキ」は「ガツ」に置換
if(previous && previous->subType=="数"){
size_t pos=current->read.find("ツキ");
if(pos!=BCSTR_NPOS) current->read.replace(pos,4,"ガツ");
}
//現在のトークンが数字のときは、いかなる場合にもスペースを空けない
if(current->subType=="数"){
current->num=true;
current->afterSpaces=0;
//コンテキストが数字じゃなかったら数字府
if(previous && context!=CONTEXT_NUMBER){
context=CONTEXT_NUMBER;
current->require3456=true;
cout << "context changed to number" << endl;
}
//次が数字じゃなかったら、最初の文字を見る
if(next && next->subType!="数") current->require36=checkNumber36(next->read.substr(0,2));
}else{//数字じゃないが、記号だったら数字コンテキストを引き継ぐ
cout << "context check " << context << endl;
if(context==CONTEXT_NUMBER){
cout << "in number context: previous is " << previous->read << ", current is " << current->read << "(" << current->type << ")" << endl;
if(current->type!="記号") context=CONTEXT_NORMAL;
}
}
//今のトークンが英語で、次が英語でない場合はスペース。正し、記号はつなげる
if(next && current->alpha){
if(context!=CONTEXT_ALPHABET){
current->require56=true;
context=CONTEXT_ALPHABET;
}
if(!next->alpha && next->type!="記号") current->afterSpaces=1;
if(next->type=="記号") current->afterSpaces=0;
}else{
if(context==CONTEXT_ALPHABET) context=CONTEXT_NORMAL;
}
//数字コンテキストでは、.は2の点にする
if(context==CONTEXT_NUMBER){
if(current->read==".") current->read="ッ";
current->afterSpaces=0;
}
//ルールを適用したので、dic/after_rules.dicの中身の置き換え規則を適用
for(boost::unordered::unordered_map<BCSTR,BCSTR>::iterator itr=dic_after_rules.begin();itr!=dic_after_rules.end();++itr){
if(current->read==itr->first) current->read=itr->second;
}
//ルール記述ここまで
}
return ERR_NONE;
}
int analyser::extractTokens(){
if(tokens.size()==0) return ERR_NO_TOKENS;
read="";
for(int i=0;i<tokens.size();i++){
read+=tokens[i].read;
for(int j=0;j<tokens[i].afterSpaces;j++){
read+=" ";
}
for(int j=0;j<tokens[i].afterLinefeeds;j++){
read+="\n";
}
}
ofstream o("debug.txt");
o << read;
o.close();
return ERR_NONE;
}
int analyser::outputTo(BCSTR fname){
outputHandler h;
h.output(tokens,fname);
return 1;
}
bool analyser::checkNumber36(BCSTR c){
if(c=="ア") return true;
if(c=="イ") return true;
if(c=="ウ") return true;
if(c=="ル") return true;
if(c=="ラ") return true;
if(c=="エ") return true;
if(c=="レ") return true;
if(c=="リ") return true;
if(c=="オ") return true;
if(c=="ロ") return true;
return false;
}