File Coverage

blib/lib/Lingua/ZH/Numbers.pm
Criterion Covered Total %
statement 61 75 81.3
branch 17 30 56.6
condition 16 29 55.1
subroutine 10 14 71.4
pod 0 6 0.0
total 104 154 67.5


line stmt bran cond sub pod time code
1             package Lingua::ZH::Numbers;
2             $Lingua::ZH::Numbers::VERSION = '0.04';
3              
4 1     1   1391 use 5.001;
  1         3  
  1         171  
5 1     1   6 use strict;
  1         2  
  1         34  
6 1     1   5 use Exporter;
  1         2  
  1         47  
7 1     1   5 use base 'Exporter';
  1         2  
  1         121  
8 1     1   6 use vars qw($Charset %MAP $VERSION @EXPORT);
  1         2  
  1         747  
9             @EXPORT = 'number_to_zh';
10              
11             =head1 NAME
12              
13             Lingua::ZH::Numbers - Converts numeric values into their Chinese string equivalents
14              
15             =head1 VERSION
16              
17             This document describes version 0.04 of Lingua::ZH::Numbers, released
18             September 8, 2004.
19              
20             =head1 SYNOPSIS
21              
22             # OO Style
23             use Lingua::ZH::Numbers 'pinyin';
24             my $shuzi = Lingua::ZH::Numbers->new( 123 );
25             print $shuzi->get_string;
26              
27             my $lingyige_shuzi = Lingua::ZH::Numbers->new;
28             $lingyige_shuzi->parse( 7340 );
29             $chinese_string = $lingyige_shuzi->get_string;
30              
31             # Function style
32             print number_to_zh( 345 ); # automatically exported
33              
34             # Change output format
35             Lingua::ZH::Numbers->charset('big5');
36              
37             =head1 DESCRIPTION
38              
39             This module tries to convert a number into Chinese cardinal number.
40             It supports decimals number, and five representation systems
41             (I): C, C, C, C and
42             C. The first two are returned as unicode strings; hence
43             they are only available for Perl 5.6 and later versions.
44              
45             The interface conforms to the one defined in B,
46             but you can also use this module in a functionnal manner by invoking
47             the C function.
48              
49             =cut
50              
51             # Global Constants {{{
52              
53             $Charset = 'pinyin';
54              
55             %MAP = (
56             ($] >= 5.006) ? eval q(
57             'traditional' => {
58             mag => [ '', split(' ', "\x{842c} \x{5104} \x{5146} \x{4eac} \x{5793} \x{79ed} \x{7a70} \x{6e9d} \x{6f97} \x{6b63} \x{8f09} \x{6975} \x{6046}\x{6cb3}\x{6c99} \x{963f}\x{50e7}\x{7947} \x{90a3}\x{7531}\x{4ed6} \x{4e0d}\x{53ef}\x{601d}\x{8b70} \x{7121}\x{91cf}\x{5927}\x{6578}") ],
59             ord => [ '', split(' ', "\x{5341} \x{767e} \x{5343}") ],
60             dig => [ split(' ', "\x{96f6} \x{4e00} \x{4e8c} \x{4e09} \x{56db} \x{4e94} \x{516d} \x{4e03} \x{516b} \x{4e5d} \x{5341}") ],
61             dot => "\x{9ede}",
62             neg => "\x{8ca0}",
63             },
64             'simplified' => {
65             mag => [ '', split(' ', "\x{4e07} \x{4ebf} \x{5146} \x{4eac} \x{5793} \x{79ed} \x{7a70} \x{6c9f} \x{6da7} \x{6b63} \x{8f7d} \x{6781} \x{6052}\x{6cb3}\x{6c99} \x{963f}\x{50e7}\x{7957} \x{90a3}\x{7531}\x{4ed6} \x{4e0d}\x{53ef}\x{601d}\x{8bae} \x{65e0}\x{91cf}\x{5927}\x{6570}") ],
66             ord => [ '', split(' ', "\x{5341} \x{767e} \x{5343}") ],
67             dig => [ split(' ', "\x{96f6} \x{4e00} \x{4e8c} \x{4e09} \x{56db} \x{4e94} \x{516d} \x{4e03} \x{516b} \x{4e5d} \x{5341}") ],
68             dot => "\x{70b9}",
69             neg => "\x{8d1f}",
70             },
71             ) : (),
72             'big5' => {
73             mag => [ '', split(' ', "\xB8U \xBB\xF5 \xA5\xFC \xA8\xCA \xAB\xB2 \xD2\xF1 \xF6\xF8 \xB7\xBE \xBC\xEE \xA5\xBF \xB8\xFC \xB7\xA5 \xAB\xED\xAAe\xA8F \xAA\xFC\xB9\xAC\xAC\xE9 \xA8\xBA\xA5\xD1\xA5L \xA4\xA3\xA5i\xAB\xE4\xC4\xB3 \xB5L\xB6q\xA4j\xBC\xC6") ],
74             ord => [ '', split(' ', "\xA4Q \xA6\xCA \xA4d") ],
75             dig => [ split(' ', "\xB9s \xA4\@ \xA4G \xA4T \xA5| \xA4\xAD \xA4\xBB \xA4C \xA4K \xA4E \xA4Q") ],
76             dot => "\xC2I",
77             neg => "\xADt",
78             },
79             'gb' => {
80             mag => [ '', split(' ', "\xCD\xF2 \xD2\xDA \xD5\xD7 \xBE\xA9 \xDB\xF2 \xEF\xF6 \xF0\xA6 \xB9\xB5 \xBD\xA7 \xD5\xFD \xD4\xD8 \xBC\xAB \xBA\xE3\xBA\xD3\xC9\xB3 \xB0\xA2\xC9\xAE\xEC\xF3 \xC4\xC7\xD3\xC9\xCB\xFB \xB2\xBB\xBF\xC9\xCB\xBC\xD2\xE9 \xCE\xDE\xC1\xBF\xB4\xF3\xCA\xFD") ],
81             ord => [ '', split(' ', "\xCA\xAE \xB0\xD9 \xC7\xA7") ],
82             dig => [ split(' ', "\xC1\xE3 \xD2\xBB \xB6\xFE \xC8\xFD \xCB\xC4 \xCE\xE5 \xC1\xF9 \xC6\xDF \xB0\xCB \xBE\xC5 \xCA\xAE") ],
83             dot => "\xB5\xE3",
84             neg => "\xB8\xBA",
85             },
86             'pinyin' => {
87             mag => [ '', map "$_ ", qw(
88             Wan Yi Zhao Jing Gai Zi Rang Gou Jian Zheng Zai Ji
89             HengHeSha AZengZhi NaYouTa BuKeSiYi WuLiangDaShu
90             ) ],
91             ord => [ '', map "$_ ", qw(Shi Bai Qian) ],
92             dig => [ qw(Ling Yi Er San Si Wu Liu Qi Ba Jiu Shi) ],
93             dot => ' Dian ',
94             neg => 'Fu ',
95             },
96             );
97             # }}}
98              
99             sub import {
100 0     0   0 my ($class, $charset) = @_;
101 0         0 $class->charset($charset);
102 0         0 $class->export_to_level(1, $class);
103             }
104              
105             sub charset {
106 2     2 0 20 my ($class, $charset) = @_;
107              
108 1     1   7 no strict 'refs';
  1         1  
  1         1526  
109 2 50       8 return ${"$class\::Charset"} unless defined $charset;
  0         0  
110              
111 2 50 33     27 $charset = 'gb' if $charset =~ /^gb/i or $charset =~ /^euc-cn$/i;
112 2 50       13 $charset = 'big5' if $charset =~ /big5/i;
113 2 50       3 ${"$class\::Charset"} = lc($charset) if exists ${"$class\::MAP"}{lc($charset)};
  2         8  
  2         18  
114             }
115              
116             sub map {
117 1     1 0 7 return \%MAP;
118             }
119              
120             sub new {
121 0     0 0 0 my ($class, $num) = @_;
122 0         0 bless (\$num, $class);
123             }
124              
125             sub parse {
126 0     0 0 0 my ($self, $num) = @_;
127 0         0 ${$self} = $num;
  0         0  
128             }
129              
130             sub get_string {
131 0     0 0 0 my ($self) = @_;
132 0         0 return number_to_zh($$self);
133             }
134              
135             sub number_to_zh {
136 3     3 0 24 __PACKAGE__->_convert($MAP{$Charset}, @_);
137             }
138              
139             sub _convert {
140 4     4   9 my ($class, $map, $input) = @_;
141 4         10 $input =~ s/[^\d\.\-]//;
142              
143 4         6 my @dig = @{$map->{dig}};
  4         17  
144 4         5 my @ord = @{$map->{ord}};
  4         12  
145 4         6 my @mag = @{$map->{mag}};
  4         20  
146 4         5 my $dot = $map->{dot};
147 4         5 my $neg = $map->{neg};
148              
149 4         7 my $out = '';
150 4 50       12 my $delta = $1 if $input =~ s/\.(.*)//;
151 4 50       10 $out = $neg if $input =~ s/^\-//;
152 4         7 $input =~ s/^0+//;
153 4   100     16 $input ||= '0';
154              
155 4         6 my @chunks;
156 4         43 unshift @chunks, $1 while ($input =~ s/(\d{1,4})$//g);
157 4         7 my $mag = $#chunks;
158 4 50       372 my $zero = ($] >= 5.005) ? eval 'qr/$dig[0]$/' : quotemeta($dig[0]) . '$';
159              
160 4         16 foreach my $num (@chunks) {
161 7         11 my $tmp = '';
162              
163 7         15 for (reverse 0..3) {
164 28         50 my $n = int($num / (10 ** $_)) % 10;
165 28 100 100     172 next unless $tmp or $n;
166 15 50 33     79 $tmp .= $dig[$n] unless ($n == 0 and $tmp =~ $zero)
      66        
      33        
      33        
167             or ($_ == 1 and $n == 1 and !$tmp);
168 15 50       41 $tmp .= $ord[$_] if $n;
169             }
170              
171 7 50       29 $tmp =~ s/$zero// unless $tmp eq $dig[0];
172 7 100       280 $tmp .= $mag[$mag] if $tmp;
173 7 50 66     31 $tmp = $dig[0].$tmp if $num < 1000 and $mag != $#chunks
      33        
174             and $out !~ $zero;
175 7         86 $out .= $tmp;
176 7         13 $mag--;
177             }
178              
179 4 50       20 $out =~ s/$zero// unless $out eq $dig[0];
180              
181 4 50       33 if ($delta) {
182 0         0 $delta =~ s/^0\.//;
183 0         0 $out .= $dot;
184 0         0 $out .= $dig[$_] for split(//, $delta);
185             }
186              
187 4   66     277 return $out || $dig[0];
188             }
189              
190             1;
191              
192             =head1 SEE ALSO
193              
194             L
195              
196             =head1 ACKNOWLEDGMENTS
197              
198             Sean Burke for suggesting me to write this module.
199              
200             =head1 AUTHORS
201              
202             Autrijus Tang Eautrijus@autrijus.orgE
203              
204             =head1 COPYRIGHT
205              
206             Copyright 2002, 2003, 2004 by Autrijus Tang Eautrijus@autrijus.orgE.
207              
208             This program is free software; you can redistribute it and/or modify it
209             under the same terms as Perl itself.
210              
211             See L
212              
213             =cut