File Coverage

blib/lib/Lingua/DetectCyrillic.pm
Criterion Covered Total %
statement 165 272 60.6
branch 59 128 46.0
condition 4 45 8.8
subroutine 16 16 100.0
pod 0 6 0.0
total 244 467 52.2


line stmt bran cond sub pod time code
1             package Lingua::DetectCyrillic;
2             # Нужно для перекодировки. Будет заменено в следующих выпусках
3 2     2   77670 use Unicode::Map8;
  2         16180  
  2         214  
4 2     2   2403 use Unicode::String;
  2         16921  
  2         137  
5            
6 2     2   26 use Exporter ();
  2         10  
  2         104  
7             @ISA = qw ( Exporter );
8             @EXPORT_OK = qw ( &toLowerCyr &toUpperCyr &TranslateCyr %RusCharset );
9            
10            
11             # Увеличение словаря с 300 до 3000 слов практически ничего не дало:
12             # при распознавании koi8/windows разница была порядка 30.
13             # Активизируем словари и хэши
14 2     2   2727 use Lingua::DetectCyrillic::DictRus;
  2         9  
  2         81  
15 2     2   5139 use Lingua::DetectCyrillic::DictUkr;
  2         11  
  2         97  
16            
17             # Хеширование по 3 пришлось снять - оно дает в среднем в 5 раз
18             # лучший результат, но хэш нужен в те же 5-7 раз больше.
19 2     2   9326 use Lingua::DetectCyrillic::WordHash2Rus;
  2         18  
  2         167  
20 2     2   8471 use Lingua::DetectCyrillic::WordHash2Ukr;
  2         16  
  2         10806  
21            
22             $VERSION = "0.02";
23            
24             # Глобальные переменные
25             $FullStat=0;
26            
27             ######## Экспортируемые переменные
28             $RusCharset{'Upper'} = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯҐЄІЇЎ";
29             $RusCharset{'Lower'} = "абвгдеёжзийклмнопрстуфхцчшщъыьэюяґєіїў";
30             $RusCharset{'All'}="АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяҐґЄєІіЇїЎў«»“”–—№";
31             $RusCharset{'Ukrainian'}= "ҐґЄєІіЇї";
32             $RusCharset{'Punctuation'}="«»“”–—№";
33             ### Конец экспортируемых переменных
34            
35             ### Экспортируемые функции
36            
37             sub TranslateCyr {
38 25     25 0 415 my ($CodingIn, $CodingOut, $String) = @_;
39 25         27 my ($MapIn, $MapOut);
40            
41 25         48 $CodingIn=lc ($CodingIn);
42 25         31 $CodingOut=lc ($CodingOut);
43             # Если входная и выходная кодировки совпадают, вернуть неизмененную строку
44 25 100       59 if ( $CodingIn eq $CodingOut ) { return $String; }
  6         19  
45            
46 19 100       170 if ($CodingIn =~ /(1251|win)/) { $MapIn = Unicode::Map8 -> new("cp1251"); $CodingIn="cp1251"; } elsif
  7 100       28  
  7 100       839  
    100          
    100          
    100          
    50          
47 2         10 ($CodingIn =~ /(koi8u|koi8-u)/) { $MapIn = Unicode::Map8 -> new("koi8-u"); $CodingIn="koi8-u"; } elsif
  2         191  
48 2         12 ($CodingIn =~ /koi/) { $MapIn = Unicode::Map8 -> new("koi8-r"); $CodingIn="koi8-r"; } elsif
  2         274  
49 2         9 ($CodingIn =~ /(dos|866|alt)/) { $MapIn = Unicode::Map8 -> new("cp866"); $CodingIn="cp866"; } elsif
  2         196  
50 2         9 ($CodingIn =~ /(iso|8859-5)/) { $MapIn = Unicode::Map8 -> new("ISO_8859-5"); $CodingIn="ISO_8859-5"; } elsif
  2         213  
51 2         9 ($CodingIn =~ /(mac|10007)/) { $MapIn = Unicode::Map8 -> new("cp10007"); $CodingIn="cp10007"; } elsif
  2         289  
52 2         3 ($CodingIn =~ /(utf|uni)/) { $CodingIn="utf-8"; } else
53 0         0 { return ""; } # Если не определили входную кодировку - выйти
54            
55 19 100       373 if ($CodingOut =~ /(1251|win)/) { $MapOut = Unicode::Map8 -> new("cp1251"); $CodingOut="cp1251"; } elsif
  13 100       82  
  13 100       2288  
    100          
    100          
    100          
    50          
56 1         5 ($CodingOut =~ /(koi8u|koi8-u)/) { $MapOut = Unicode::Map8 -> new("koi8-u"); $CodingOut="koi8-u"; } elsif
  1         104  
57 1         7 ($CodingOut =~ /koi/) { $MapOut = Unicode::Map8 -> new("koi8-r"); $CodingOut="koi8-r"; } elsif
  1         110  
58 1         6 ($CodingOut =~ /(dos|866|alt)/) { $MapOut = Unicode::Map8 -> new("cp866"); $CodingOut="cp866";} elsif
  1         88  
59 1         4 ($CodingOut =~ /(iso|8859-5)/) { $MapOut = Unicode::Map8 -> new("ISO_8859-5"); $CodingOut="ISO_8859-5";} elsif
  1         87  
60 1         4 ($CodingOut =~ /(mac|10007)/) { $MapOut = Unicode::Map8 -> new("cp10007"); $CodingOut="cp10007"; } elsif
  1         90  
61 1         2 ($CodingOut =~ /(utf|uni)/) { $CodingOut="utf-8"; } else
62 0         0 { return ""; } # Если не определили выходную кодировку - выйти
63            
64             # Из UTF-8 в 8-битовую кодировку
65 19 100       52 if ( $CodingIn eq "utf-8" ) {
66 2         30 $s=Unicode::String::utf8($String)->ucs2;
67 2 50       772 if ( $CodingOut eq "utf-8" ) { return $String; } else { return $MapOut->to8($s); }
  0         0  
  2         21  
68             }
69             # Из 8-битовой кодировки в UTF-8
70 17 100       516 if ( $CodingOut eq "utf-8" ) { return $MapIn->tou($String)->utf8; }
  1         7  
71             # Если это не utf-8, то перекодируем из 8-битовго набора в 8-битовый же
72 16         188 return $MapIn->recode8($MapOut,$String);
73             }
74            
75             sub toLowerCyr {
76 1     1 0 511 my ($s, $SourceCoding ) = @_;
77 1 50       5 if ( $SourceCoding ) { $s = TranslateCyr ( $SourceCoding, "win", $s ) }
  1         6  
78 1         199 eval ("\$s =~ tr/A-Z$RusCharset{'Upper'}/a-z$RusCharset{'Lower'}/");
79 1 50       9 if ( $SourceCoding ) { $s = TranslateCyr ( "win", $SourceCoding, $s ) }
  1         4  
80 1         8 $s;
81             }
82             sub toUpperCyr {
83 1     1 0 3 my ($s, $SourceCoding ) = @_;
84 1 50       5 if ( $SourceCoding ) { $s = TranslateCyr ( $SourceCoding, "win", $s ) }
  1         4  
85 1         77 eval ("\$s =~ tr/a-z$RusCharset{'Lower'}/A-Z$RusCharset{'Upper'}/");
86 1 50       27 if ( $SourceCoding ) { $s = TranslateCyr ( "win", $SourceCoding, $s ) }
  1         4  
87 1         6 $s;
88             }
89            
90             ######## Конец экспортируемых переменных и функций
91            
92             sub new
93             {
94             # Заполнили данные по умолчанию
95 1     1 0 747 %Args = ( MaxTokens => 100, DetectAllLang => 0 );
96 1         4 my $self = {};
97 1         4 shift;
98             # Зачитали аргументы - это глобальный хэш
99 1         6 %Args = @_;
100            
101 1         5 @Codings = ( "win1251", "koi8r", "cp866", "utf", "iso", "mac" );
102 1 50       5 if ( $Args{DetectAllLang} ) {push @Codings, koi8u};
  1         199  
103 1         4 @InputData="";
104            
105 1         9 return bless ($self);
106             }
107            
108            
109             sub LogWrite {
110 1     1 0 2 shift;
111 1         2 my $Outfile=shift;
112            
113             ########### Формат отчета ##########
114             format STAT =
115             @<<<<<<@######@######@######@######@######@##########@##########@######@######
116             $key,%Stat->{$key}{GoodTokensChars},%Stat->{$key}{GoodTokensCount},%Stat->{$key}{AllTokensChars},%Stat->{$key}{AllTokensCount},%Stat->{$key}{CharsUkr},%Stat->{$key}{HashScore2Rus},%Stat->{$key}{HashScore2Ukr},%Stat->{$key}{WordsRus},%Stat->{$key}{WordsUkr}
117             .
118            
119             # Выводим отчет. Если названия файла нет, или это stdout - выводим на экран, иначе в файл
120 1 50 33     11 if (!$Outfile or uc ($Outfile) eq "STDOUT") {
121 0         0 $OUT=\*STDOUT;
122             } else {
123 1         125 open $OUT, ">$Outfile";
124             }
125            
126 1         5 select $OUT; $~="STAT";
  1         6  
127 1         12 print "Coding: $Coding Language: $Language Algorithm: $Algorithm \n\n";
128 1         3 print " GdChr GdCnt AllChr AllCnt ChrUkr HashRus HashUkr WRus WUkr\n";
129 1         3 print "*" x 78 ."\n";
130 1         9 foreach $key (keys %Stat) { write ; }
  7         15  
131 1         2 print "*" x 78 ."\n";
132 1         153 print "Time: " .localtime() ."\n";
133 1         2 print <
134             GoodTokensChars - number of characters in pure Cyrillic tokens with correct
135             capitalization.
136             GoodTokensCount - number of pure Cyrillic tokens with correct capitalization.
137             AllTokensChars - number of characters in tokens containing Cyrillica.
138             AllTokensCount - their number
139             CharsUkr - number of Ukrainian letters
140             HashScore2Rus (Ukr) - hits on 2-letter hash
141             WordsRus (Ukr) - hits on frequency dictionary
142             POD
143            
144 1         4 select STDOUT;
145 1 50       8 if ( $OUT ne \*STDOUT ) { close $OUT; }
  1         138  
146            
147             } # LogWrite()
148            
149            
150             sub Detect {
151 1     1 0 2 shift;
152             # Перегружаем в глобальный массив @InputData, он может понадобиться еще раз
153             # при получении расширенной статистики.
154            
155 1         4 @InputData = @_;
156             ######## Отладочно сливаем ввод в файл
157             #$Outfile = "C:\\test_out3.txt";
158             #open OUT, ">$Outfile";
159             #for (@_) { print OUT; }
160             #close OUT;
161             ########
162            
163            
164 1         4 _GetStat();
165            
166             # Теперь выясняем кодировку
167 1         2 $Language = ""; $Coding="";
  1         3  
168 1         2 $Algorithm=0; # Это примененная схема определения кодировки
169 1         1 $MaxCharsProcessed =0;
170 1         5 _AnalyzeStat();
171 1 50       17 if ( $Coding eq "win1251" ) { $Coding = "windows-1251" }
  0 50       0  
    50          
    50          
    50          
    50          
    50          
172 0         0 elsif ( $Coding eq "koi8r" ) { $Coding = "koi8-r" }
173 0         0 elsif ( $Coding eq "koi8u" ) { $Coding = "koi8-u" }
174 0         0 elsif ( $Coding eq "iso" ) { $Coding = "iso-8859-5" }
175 0         0 elsif ( $Coding eq "cp866" ) { $Coding = "cp866" }
176 0         0 elsif ( $Coding eq "utf" ) { $Coding = "utf-8" }
177 0         0 elsif ( $Coding eq "mac" ) { $Coding = "x-mac-cyrillic" }
178 1         3 else { $Coding = "iso-8859-1" }
179            
180 1         12 return ( $Coding, $Language, $MaxCharsProcessed, $Algorithm );
181            
182             }
183            
184             sub _GetStat {
185             # Инициализируем структуру хэшей (описание см. в конце)
186            
187 2     2   7 for (@Codings) {
188 14 100       30 if ( $FullStat ) {
189 7         14 $Stat{$_}{AllTokensChars} = 0;
190 7         11 $Stat{$_}{AllTokensCount} = 0;
191 7         10 $Stat{$_}{CharsUkr} = 0;
192 7         10 $Stat{$_}{HashScore2Rus} = 0;
193 7         14 $Stat{$_}{HashScore2Ukr} = 0;
194 7         14 $Stat{$_}{WordsRus} = 0;
195 7         14 $Stat{$_}{WordsUkr} = 0;
196            
197             } else { # $FullStat
198            
199 7         22 $Stat{$_}{GoodTokensChars} = 0;
200 7         13 $Stat{$_}{GoodTokensCount} = 0;
201 7         13 $Stat{$_}{CharsUkr} = 0;
202            
203             } # $FullStat
204             } # end for
205            
206            
207             # Получаем статистику для каждой строки
208 2         4 $EnoughTokens=0;
209 2         6 for ( @InputData ) {
210 2         3 my $String=$_;
211            
212 2         4 for (@Codings) {
213 14         38 _ParseString ($_,$String,%Stat->{$_});
214             # Выходим, если хоть по одной из кодировок набрали максимальное число токенов
215 14 50       55 if (%Stat->{$_}{GoodTokensCount} > $Args{MaxTokens} ) { $EnoughTokens=1; }
  0         0  
216             }
217            
218 2 50       11 if ( $EnoughTokens ) { last; }
  0         0  
219             } # Конец получения статистики
220            
221            
222             }
223            
224             sub _AnalyzeStat {
225            
226             # Сначала анализируем соотношение букв в чисто кириллических токенах
227             # с правильной капитализацией
228            
229            
230             ## Анализируем формальную статистику по токенам с кириллицей
231             # Минимальное соотношение токенов с правильной капитализацией, при котором
232             # разницу считаем значимой для вычисления результата. Пока не используется.
233             # my $TokensRatio=0.95;
234             # Минимальный процент токенов с украинскими символами, чтобы текст считался украинским
235 1     1   3 my $UkrTokensShare=0.01;
236            
237             ## Анализируем чисто кириллические токены с правильной капитализацией.
238 1         2 my @CyrCharRating;
239 1         3 for ( @Codings ) { push @CyrCharRating,[$_, %Stat->{$_}{GoodTokensChars}]; }
  7         23  
240 1         8 @CyrCharRating = sort { $b->[1] <=> $a->[1] } @CyrCharRating;
  11         16  
241            
242 1         3 $MaxCharsProcessed = $CyrCharRating[0]->[1];
243            
244             # После сортировки получаем список наиболее вероятных кодировок
245             # Они содержат максимальное число "правильных" кириллических слов в данной кодировке
246             # Выясняем, между сколькими кодировками нужно провести различие
247 1         2 my @BestCodings;
248 1         3 for $arrayref ( @CyrCharRating ) {
249 7 50       15 if ( $arrayref ->[1] == $CyrCharRating[0]->[1] ) {
250 7         14 push @BestCodings, $arrayref ->[0] ;
251             }
252             }
253            
254             # Если первая возможная кодировка содержит больше правильных символов,
255             # чем любая иная, считаем, что дело сделано. Вообще здесь лучше ввести
256             # определение минимально необходимого преимущества, скажем, 10% или что-то вроде.
257            
258 1 50       5 if ( scalar(@BestCodings) == 1 ) {
259 0         0 $Coding = $CyrCharRating[0]->[0];
260            
261             # Определяем язык. Смотрим, нет ли украинских токенов. Если они присутствуют
262             # в количестве не менее $UkrTokensShare, считаем язык украинским, иначе - русским.
263 0 0       0 if ( %Stat->{$Coding}{CharsUkr} / %Stat->{$Coding}{GoodTokensCount} > $UkrTokensShare )
264 0         0 { $Language = "Ukr"; } else { $Language = "Rus"; }
  0         0  
265 0         0 $Algorithm = 11;
266 0         0 return;
267             } # Конец разбора одной кодировки
268            
269             # Следующий вариант: одинаковое число баллов набрали ровно две кодировки.
270             # Не исключено, что это либо koi русский и украинский, либо win1251 и мас.
271             # Тогда мы их можем различить по формальным параметрам.
272 1 50       5 if ( scalar(@BestCodings) == 2 ) {
273            
274 0         0 $BestCoding1 = $CyrCharRating[0]->[0];
275 0         0 $BestCoding2 = $CyrCharRating[1]->[0];
276             # Первый вариант - это кодировки koi8u и koi8r.
277 0 0 0     0 if ( $BestCoding1 =~ /koi/ && $BestCoding2 =~ /koi/ ) {
278             # Определяем язык и на этом основании - кодировку
279 0 0 0     0 if (%Stat->{$Coding}{GoodTokensCount} > 0 && %Stat->{$Coding}{CharsUkr} / %Stat->{$Coding}{GoodTokensCount} > $UkrTokensShare )
280 0         0 { $Coding = "koi8u"; $Language = "Ukr"; } else { $Coding = "koi8r"; $Language = "Rus"; }
  0         0  
  0         0  
  0         0  
281 0         0 $Algorithm = 21;
282 0         0 return;
283             } # Конец 1-го варианта
284            
285             # Второй вариант - это кодировки win1251 и mac. То есть весь текст записан
286             # строчными буквами без Ю и Э. Предпочитаем однозначно win1251
287 0 0 0     0 if ( $BestCoding1 =~ /(win1251|mac)/ && $BestCoding2 =~ /(win1251|mac)/ ) {
288 0         0 $Coding="win1251";
289 0 0 0     0 if (%Stat->{$Coding}{GoodTokensCount} > 0 && %Stat->{$Coding}{CharsUkr} / %Stat->{$Coding}{GoodTokensCount} > $UkrTokensShare )
290 0         0 { $Language = "Ukr"; } else { $Language = "Rus"; }
  0         0  
291 0         0 $Algorithm = 22;
292 0         0 return;
293             } # Конец 2-го варианта
294            
295             } # Конец разбора двух кодировок при котором когда мы еще можем обойтись только анализом
296             # "правильных" символов, без привлечения расширенной статистики
297            
298             # Итак, кодировку с ходу не удалось определить по статистике символов с правильной
299             # капитализацией. Тогда устанавливаем флаг $FullStat и еще раз получаем статистику по
300             # строкам - на этот раз с хэшем и словарем
301 1         2 $FullStat = 1;
302 1         3 _GetStat();
303            
304             # Проверяем, а есть ли кириллица в тексте вообще.
305 1         3 for ( @BestCodings ) { push @CyrCharRating,[$_, %Stat->{$_}{AllTokensChars}]; }
  7         24  
306 1         5 @CyrCharRating = sort { $b->[1] <=> $a->[1] } @CyrCharRating;
  29         35  
307            
308 1         3 $MaxCharsProcessed = $CyrCharRating[0]->[1];
309            
310             # Выйти, если не было ни одного кириллического символа
311 1 50       4 if ( $MaxCharsProcessed == 0 ) { $Coding = "iso-8859-1"; $Language = "NoLang"; $Algorithm = 100; return; }
  1         2  
  1         3  
  1         2  
  1         7  
312            
313            
314             # Делаем следующие два шага. Сначала создаем массив из комбинаций языка и кодировки
315             # для подсчета слов из словаря, затем оставляем только комбинации с максимальным значением,
316             # т.е. сужаем список потенциальных комбинаций. Если этих комбинаций больше одной,
317             # переходим ко второму шагу - создаем аналогичный массив для хэшей и снова отбираем
318             # комбинации с максимальным значением. Если снова не удалось выделить единственного
319             # "победителя", предпочитаем русский язык украинскому, кодировку windows - макинтошу.
320            
321             # Шаг 1. Ищем максимальный рейтинг слов из частотного словаря
322 0         0 my @WordsRating;
323 0         0 for ( @BestCodings ) {
324 0         0 push @WordsRating, [$_,"Rus", %Stat->{$_}{WordsRus}];
325 0         0 push @WordsRating, [$_,"Ukr", %Stat->{$_}{WordsUkr}];
326             }
327 0         0 @WordsRating = sort { $b->[2] <=> $a->[2] } @WordsRating;
  0         0  
328            
329             #print "WordsRating: \n";
330             #for $arrayref (@WordsRating) {
331             # print " " . $arrayref ->[0] . " " .$arrayref ->[1] ." ".$arrayref ->[2] ."\n"; }
332            
333            
334             # Если обнаружили в тексте хотя бы одно слово из словаря, и нет альтернатив,
335             # то считаем, что определение языка/кодировки произошло
336 0 0 0     0 if ( $WordsRating[0]->[2] > 0 && $WordsRating[0]->[2] > $WordsRating[1]->[2] ) {
337 0         0 $Coding = $WordsRating[0]->[0];
338 0         0 $Language = $WordsRating[0]->[1];
339 0         0 $Algorithm = 31;
340 0         0 return;
341             }
342            
343             # Либо слова из частотного словаря вообще не были обнаружены,
344             # либо имеем совпадение числа слов для нескольких комбинаций язык/кодировка
345             # Шаг 2. Обращаемся к хэшу и еще больше сужаем ареал поиска.
346            
347 0         0 my @BestWordsRating;
348 0         0 for $arrayref ( @WordsRating ) {
349 0 0       0 if ( $arrayref ->[2] == $WordsRating[0]->[2] ) {
350 0         0 push @BestWordsRating, [ $arrayref ->[0],$arrayref ->[1],$arrayref ->[2] ] ;
351             }
352             }
353             #print "BestWordsRating: \n";
354             #for $arrayref (@BestWordsRating) {
355             # print " " . $arrayref ->[0] . " " .$arrayref ->[1] ." ".$arrayref ->[2] ."\n"; }
356            
357            
358 0         0 my @HashRating;
359 0         0 for $arrayref ( @BestWordsRating ) {
360            
361 0 0       0 if ( $arrayref->[1] eq "Rus" ) {
362 0         0 push @HashRating, [$arrayref->[0],"Rus", %Stat->{$arrayref->[0]}{HashScore2Rus}]; }
363 0 0       0 if ( $arrayref->[1] eq "Ukr" ) {
364 0         0 push @HashRating, [$arrayref->[0],"Ukr", %Stat->{$arrayref->[0]}{HashScore2Ukr}]; }
365            
366             }
367 0         0 @HashRating = sort { $b->[2] <=> $a->[2] } @HashRating;
  0         0  
368            
369             #for $arrayref (@HashRating) {
370             # print " " .$arrayref ->[0] . " " .$arrayref ->[1] ." ".$arrayref ->[2] ."\n"; }
371            
372             # Если обнаружили в тексте хотя бы один реальный хэш, и нет альтернатив,
373             # то считаем, что определение языка/кодировки произошло
374 0 0 0     0 if ( $HashRating[0]->[2] > 0 && $HashRating[0]->[2] > $HashRating[1]->[2] ) {
375 0         0 $Coding = $HashRating[0]->[0];
376 0         0 $Language = $HashRating[0]->[1];
377 0         0 $Algorithm = 32;
378 0         0 return;
379             }
380            
381            
382             # Либо хэш не обнаружен, либо имеем совпадение числа слов для нескольких комбинаций
383             # язык/кодировка
384             # Шаг 3. Оставляем только те комбинации язык/кодировка, которые содержат наибольшее число
385             # попаданий в хэш.
386            
387 0         0 my @BestHashRating;
388 0         0 for $arrayref ( @HashRating ) {
389 0 0       0 if ( $arrayref ->[2] == $HashRating[0]->[2] ) {
390 0         0 push @BestHashRating, [ $arrayref ->[0],$arrayref ->[1] ] ;
391             }
392             }
393             # for $arrayref (@BestHashRating) {
394             # print " " .$arrayref ->[0] . " " .$arrayref ->[1] ." ".$arrayref ->[2] ."\n"; }
395            
396            
397             # Теперь наступили тяжелые времена. ;-)) Остались только те комбинации кодировка/язык,
398             # для которых полностью совпадают данные и по частотному словарю, и по хэшу.
399             # Это может случиться ровно в двух случаях. Первый - весь текст набран строчными буквами.
400             # Тогда смешиваются Mac/Win. Предпочитаем Win.
401             # Второй - текст в koi набран без украинских букв. Тогда смешиваются koi8-r и koi8-u.
402             # Предпочитаем koi8-r (впрочем, разницы в данном случае никакой).
403            
404            
405 0         0 for $arrayref (@BestHashRating) {
406 0 0       0 if ( $arrayref ->[0] =~ /win/ ) {
407 0         0 $Coding = "win1251";
408 0 0 0     0 if (%Stat->{$Coding}{GoodTokensCount} > 0 && %Stat->{$Coding}{CharsUkr} / %Stat->{$Coding}{GoodTokensCount} > $UkrTokensShare )
409 0         0 { $Language = "Ukr"; } else { $Language = "Rus"; }
  0         0  
410 0         0 $Algorithm = 33;
411 0         0 return;
412             }
413             }
414            
415 0         0 for $arrayref (@BestHashRating) {
416 0 0       0 if ( $arrayref ->[0] =~ /koi/ ) {
417 0         0 $Coding = "koi8-r";
418 0         0 $Language = "Rus";
419 0         0 $Algorithm = 34;
420 0         0 return;
421             }
422             }
423            
424             # Ничего не подошло. Устанавливаем первую победившую кодировку и язык.
425 0         0 $Coding = $BestHashRating[0]->[0];
426 0         0 $Language = $BestHashRating[0]->[1];;
427 0         0 $Algorithm = 40;
428            
429            
430 0         0 return;
431             } #end _AnalyzeStat()
432            
433            
434            
435            
436             sub _ParseString {
437 14     14   27 my ($Coding, $String, $Hash) = @_;
438             # Перевели строку в кодировку win1251 и убрали знаки новой строки
439 14         31 $String = TranslateCyr($Coding,"win1251",$String);
440 14         34 $String =~ s/[\n\r]//go;
441            
442             ## Разбитие на слова
443             ## \xAB\xBB - полиграфические кавычки, \x93\x94 - кавычки-"лапки",
444             ## \xB9 - знак номера, \x96\x97 - полиграфические тире
445 14         192 for (split (/[\xAB\xBB\x93\x94\xB9\x96\x97\.\,\-\s\:\;\?\!\"\(\)\d<>]+/o, $String)) {
446 42         67 s/^\'+(.*)\'+$/$1/; # Убрали начальные и конечные апострофы
447            
448 42 100       71 if ( !$FullStat ) {
449            
450             # Определяем, "правильный" ли это токен, т.е. содержит только кириллицу
451             # и либо строчные буквы, либо ПРОПИСНЫЕ, либо начинается с Прописной.
452 21 50 33     867 if (/^[$RusCharset{'Lower'}]+$/ || /^[$RusCharset{'Upper'}]{1}[$RusCharset{'Lower'}]+$/ || /^[$RusCharset{'Upper'}]+$/ ) {
      33        
453 0         0 $$Hash{GoodTokensChars}+=length();
454             # Для UTF умножаем число кириллических символов на два.
455 0 0       0 if ( $Coding eq "utf" ) { $$Hash{GoodTokensChars}+=length(); }
  0         0  
456 0         0 $$Hash{GoodTokensCount}++;
457             # Если токен содержит украинские символы, увеличить счетчик украинских токенов
458 0 0 0     0 if ( $Args{DetectAllLang} && /[$RusCharset{'Ukrainian'}]/ ) { $$Hash{CharsUkr}++;}
  0         0  
459            
460             }
461            
462             } else { # !$FullStat
463            
464             # Определяем, можно ли вообще проводить над этим токеном какие-либо действия.
465             # Для этого он должен содержать хотя бы одну правильную кириллическую букву,
466             # английские буквы и цифры в любой смеси.
467 21 50 33     252 if (/[$RusCharset{'All'}]/ && /^[\w\d$RusCharset{'All'}]+$/) {
468 0           $$Hash{AllTokensChars}+=length();
469             # Для UTF умножаем число символов на два.
470 0 0         if ( $Coding eq "utf" ) { $$Hash{AllTokensChars}+=length(); }
  0            
471             # Если токен содержит украинские символы, увеличить счетчик украинских токенов
472 0 0 0       if ( $Args{DetectAllLang} && /[$RusCharset{'Ukrainian'}]/ ) { $$Hash{CharsUkr}++; }
  0            
473            
474             # Теперь приступаем к обработке хэша и словарей
475             # Переводим токен в нижний регистр - и словарь, и хэши у нас в нижнем регистре
476 0           $_=toLowerCyr($_);
477            
478 0 0         if ($DictRus{$_}) { $$Hash{WordsRus}++; }
  0            
479 0 0 0       if ($Args{DetectAllLang} && $DictUkr{$_}) { $$Hash{WordsUkr}++; }
  0            
480            
481 0           for $i (0..length()-1) {
482 0 0         if ( $WordHash2Rus{substr($_,$i,2)} ) { $$Hash{HashScore2Rus}++; }
  0            
483 0 0 0       if ( $Args{DetectAllLang} && $WordHash2Ukr{substr($_,$i,2)} ) { $$Hash{HashScore2Ukr}++; }
  0            
484             } # end for
485            
486             } # end if (/^[\w\d$RusCharset{'All'}]+$/)
487            
488             } # !$FullStat
489            
490             } # end for (split...
491            
492            
493             } # end routine
494            
495            
496            
497             1;
498            
499             __END__