File Coverage

blib/lib/AozoraBunko/Tools/Checkerkun.pm
Criterion Covered Total %
statement 88 88 100.0
branch 45 48 93.7
condition 31 42 73.8
subroutine 15 15 100.0
pod 2 2 100.0
total 181 195 92.8


line stmt bran cond sub pod time code
1             package AozoraBunko::Tools::Checkerkun;
2             our $VERSION = "0.01";
3              
4 3     3   3859 use 5.008001;
  3         10  
5 3     3   15 use strict;
  3         3  
  3         63  
6 3     3   23 use warnings;
  3         5  
  3         70  
7 3     3   893 use utf8;
  3         13  
  3         13  
8              
9 3     3   70 use Carp qw//;
  3         5  
  3         54  
10 3     3   2265 use File::ShareDir qw//;
  3         18129  
  3         68  
11 3     3   2777 use YAML::Tiny qw//;
  3         17746  
  3         78  
12 3     3   2730 use Encode qw//;
  3         33384  
  3         74  
13 3     3   2248 use Lingua::JA::Halfwidth::Katakana;
  3         616  
  3         2621  
14              
15             my $YAML_FILE = File::ShareDir::dist_file('AozoraBunko-Tools-Checkerkun', 'hiden_no_tare.yml');
16             my $YAML = YAML::Tiny->read($YAML_FILE)->[0];
17             my $ENC = Encode::find_encoding("Shift_JIS");
18              
19             # [78hosetsu_tekiyo] 78互換包摂の対象となる不要な外字注記をチェックする
20             our $KUTENMEN_78HOSETSU_TEKIYO = $YAML->{'kutenmen_78hosetsu_tekiyo'};
21              
22             # [hosetsu_tekiyo] 包摂の対象となる不要な外字注記をチェックする
23             our $KUTENMEN_HOSETSU_TEKIYO = $YAML->{'kutenmen_hosetsu_tekiyo'};
24              
25             # 新JIS漢字で包摂基準の適用除外となる104字
26             our $JYOGAI = $YAML->{'jyogai'};
27              
28             # 78互換文字
29             our $J78 = $YAML->{'j78'};
30              
31             # 間違えやすい文字
32             # かとうかおりさんの「誤認識されやすい文字リスト」から
33             # http://plaza.users.to/katokao/digipr/digipr_charlist.html
34             our $GONIN1 = $YAML->{'gonin1'};
35              
36             # 誤認2
37             our $GONIN2 = $YAML->{'gonin2'};
38              
39             # 誤認3
40             # (砂場清隆さんの入力による)
41             our $GONIN3 = $YAML->{'gonin3'};
42              
43             sub _default_options
44             {
45             return {
46 35     35   258 'gaiji' => 1, # JIS外字をチェックする
47             'hansp' => 1, # 半角スペースをチェックする
48             'hanpar' => 1, # 半角カッコをチェックする
49             'zensp' => 0, # 全角スペースをチェックする
50             '78hosetsu_tekiyo' => 1, # 78互換包摂の対象となる不要な外字注記をチェックする
51             'hosetsu_tekiyo' => 1, # 包摂の対象となる不要な外字注記をチェックする
52             '78' => 0, # 78互換包摂29字をチェックする
53             'jyogai' => 0, # 新JIS漢字で包摂規準の適用除外となる104字をチェックする
54             'gonin1' => 0, # 誤認しやすい文字をチェックする(1)
55             'gonin2' => 0, # 誤認しやすい文字をチェックする(2)
56             'gonin3' => 0, # 誤認しやすい文字をチェックする(3)
57             'simplesp' => 0, # 半角スペースは「_」で、全角スペースは「□」で出力する
58             };
59             }
60              
61             sub new
62             {
63 35     35 1 35297 my $class = shift;
64 35 100       129 my %args = (ref $_[0] eq 'HASH' ? %{$_[0]} : @_);
  29         192  
65              
66 35         166 my $options = $class->_default_options;
67              
68 35         115 for my $key (keys %args)
69             {
70 371 100       711 if ( ! exists $options->{$key} ) { Carp::croak "Unknown option: '$key'"; }
  2         345  
71 369         633 else { $options->{$key} = $args{$key}; }
72             }
73              
74 33         173 bless $options, $class;
75             }
76              
77             # 例:
78             #
79             # [#「口+亞」、第3水準1-15-8、144-上-9]
80             # が
81             # [#「口+亞」、第3水準1-15-8、144-上-9] → [78hosetsu_tekiyo]【唖】
82             # に変換され、
83             #
84             #[#「にんべん+曾」、第3水準1-14-41、144-上-9]
85             # が
86             #[#「にんべん+曾」、第3水準1-14-41、144-上-9]→[hosetsu_tekiyo]【僧】
87             # に変換される。
88             #
89             sub _check_all_hosetsu_tekiyo
90             {
91 2004     2004   2829 my ($self, $chars_ref, $index) = @_;
92              
93 2004         2655 my ($replace, $usedlen);
94              
95 2004         2810 my $rear_index = $index + 80;
96 2004 100       2428 $rear_index = $#{$chars_ref} if $rear_index > $#{$chars_ref};
  7         11  
  2004         4587  
97              
98 2004 50       5737 if ( join("", @{$chars_ref}[$index .. $rear_index]) =~ /^([#.*?水準(\d+\-\d+\-\d+).*?])/ )
  2004         22347  
99             {
100 2004         3790 my $match = $1;
101 2004         3233 my $kutenmen = $2;
102              
103 2004 100 66     10348 if ( $self->{'78hosetsu_tekiyo'} && exists $KUTENMEN_78HOSETSU_TEKIYO->{$kutenmen} )
    50 33        
104             {
105 1002         2276 $replace = $match . ' → [78hosetsu_tekiyo]【' . $KUTENMEN_78HOSETSU_TEKIYO->{$kutenmen} . '】 ';
106 1002         2253 $usedlen = length $match;
107             }
108             elsif ( $self->{'hosetsu_tekiyo'} && exists $KUTENMEN_HOSETSU_TEKIYO->{$kutenmen} )
109             {
110              
111 1002         2280 $replace = $match . ' → [hosetsu_tekiyo]【' . $KUTENMEN_HOSETSU_TEKIYO->{$kutenmen} . '】 ';
112 1002         2353 $usedlen = length $match;
113             }
114             }
115              
116 2004         8126 return ($replace, $usedlen);
117             }
118              
119             sub _is_gaiji
120             {
121             # UTF-8からSJISに変換できなければ外字と判定
122 2010     2010   2639 eval { $ENC->encode($_[0], Encode::FB_CROAK) };
  2010         14563  
123 2010 100       13888 return 1 if $@;
124 8         45 return 0;
125             }
126              
127             sub check
128             {
129 30     30 1 152 my ($self, $text) = @_;
130              
131 30 100       76 return undef unless defined $text;
132              
133 29         9632 my @chars = split(//, $text);
134              
135 29         54 my $checked_text = '';
136              
137 29         96 for (my $i = 0; $i < @chars; $i++)
138             {
139 4320         6097 my $char = $chars[$i];
140              
141 4320 100       9135 if ($self->{simplesp})
142             {
143 32 100       100 $char = '_' if $char eq "\x{0020}";
144 32 100       92 $char = '□' if $char eq "\x{3000}";
145             }
146              
147 4320         5753 $checked_text .= $char;
148              
149 4320 100 100     41431 if ($char =~ /[\x{0000}-\x{0009}\x{000B}\x{000C}\x{000E}-\x{001F}\x{007F}-\x{009F}]/)
    100 100        
    100 100        
    100 66        
    100 66        
    100 66        
150             {
151             # 改行は含まない
152 2         14 $checked_text .= " [ctrl]【" . sprintf("U+%04X", ord $char) . "】 ";
153             }
154 3     3   39 elsif ($char =~ /\p{InHalfwidthKatakana}/)
  3         6  
  3         46  
155             {
156 2         6 $checked_text .= " [hankata]【$char】 ";
157             }
158             elsif ($self->{'hansp'} && $char =~ "\x{0020}")
159             {
160 2         5 $checked_text .= " [hansp]【$char】 ";
161             }
162             elsif ($self->{'zensp'} && $char eq "\x{3000}")
163             {
164 2         6 $checked_text .= " [zensp]【$char】 ";
165             }
166             elsif ( $self->{hanpar} && ($char eq '(' || $char eq ')') )
167             {
168 4         9 $checked_text .= " [hanpar]【$char】 ";
169             }
170             elsif ( $char eq '※' && ($self->{'78hosetsu_tekiyo'} || $self->{'hosetsu_tekiyo'}) )
171             {
172 2004         4996 my ($replace, $usedlen) = $self->_check_all_hosetsu_tekiyo(\@chars, $i + 1);
173              
174 2004 50       4424 if ($replace)
175             {
176 2004         3280 $checked_text .= $replace;
177 2004         2282 $i += $usedlen;
178 2004         5365 next;
179             }
180             }
181             else
182             {
183 2304 100 66     19526 if ($self->{'78'} && $J78->{$char})
    100 66        
    100 66        
    100 66        
    100 66        
184             {
185 2         9 $checked_text .= " [78]【$char】(" . $J78->{$char} . ") ";
186             }
187             elsif ($self->{'jyogai'} && $JYOGAI->{$char})
188             {
189 2         6 $checked_text .= " [jyogai]【$char】 ";
190             }
191             elsif ($self->{'gonin1'} && $GONIN1->{$char})
192             {
193 4         15 $checked_text .= " [gonin1]【$char】(" . $GONIN1->{$char} . ") ";
194             }
195             elsif ($self->{'gonin2'} && $GONIN2->{$char})
196             {
197 4         14 $checked_text .= " [gonin2]【$char】(" . $GONIN2->{$char} . ") ";
198             }
199             elsif ($self->{'gonin3'} && $GONIN3->{$char})
200             {
201 4         14 $checked_text .= " [gonin3]【$char】(" . $GONIN3->{$char} . ") ";
202             }
203             }
204              
205 2316 100 100     6906 $checked_text .= " [gaiji]【$char】 " if $self->{'gaiji'} && _is_gaiji($char);
206             }
207              
208              
209 29         4061 return $checked_text;
210             }
211              
212             1;
213              
214             __END__