How to parse kanji numeric characters using ICU?
我正在使用 ICU 编写一个函数来解析由汉字数字字符组成的 Unicode 字符串,并希望返回字符串的整数值。
"五" => 5
"三十一" => 31
"五千九百七十二" => 5972
我将语言环境设置为 Locale::getJapan() 并使用 NumberFormat::parse() 来解析字符串。但是,每当我向它传递任何汉字字符时, parse() 方法都会返回 U_INVALID_FORMAT_ERROR.
有谁知道ICU是否支持NumberFormat::parse()方法中的汉字字符串?我希望因为我将语言环境设置为日语,所以它能够解析汉字数值。
谢谢!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <iostream> #include <unicode/numfmt.h> using namespace std; int main(int argc, char **argv) { const Locale &jaLocale = Locale::getJapan(); UErrorCode status = U_ZERO_ERROR; NumberFormat *nf = NumberFormat::createInstance(jaLocale, status); UChar number[] = {0x4E94}; // Character for '5' in Japanese '五' UnicodeString numStr(number); Formattable formattable; nf->parse(numStr, formattable, status); if (U_FAILURE(status)) { cout <<"error parsing as number:" << u_errorName(status) << endl; return(1); } cout <<"long value:" << formattable.getLong() << endl; } |
您可以使用 ICU 基于规则的数字格式 (RBNF) 模块 rbnf.h (C) 或对于 C,在 unum.h 中使用 UNUM_SPELLOUT 选项,两者都带有日语的 "ja" 语言环境。 Atryom 为您的 C 代码提供了更正:
我不久前创建了一个小的 perl 模块来执行此操作。它可以转换阿拉伯语<=>日语,虽然我没有对它进行详尽的测试,但我认为它非常全面。随时改进它。
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 | package kanjiArabic; use strict; use warnings; our $VERSION ="1.00"; use utf8; our %big = ( 十 => 10,百 => 100,千 => 1000, ); our %bigger = ( 万 => 10000,億 => 100000000, 兆 => 1000000000000,京 => 10000000000000000, 垓 => 100000000000000000000, ); #precompile regexes our $qr = qr/[0-9]/; our $bigqr = qr/[十百千]/; our $biggerqr = qr/[万億兆京垓]/; #this routine does most of the real work. sub kanji2arabic{ $_ = shift; tr/〇一二三四五六七八九/0123456789/; #optionally precompile for performance boost s/(?<=${qr})(${bigqr})/\\*${1}/g; s/(?<=${bigqr})(${bigqr})/\\+${1}/g; s/(${bigqr})(?=${qr})/${1}\\+/g; s/(${bigqr})(?=${bigqr})/${1}\\+/g; s/(${bigqr})/${big{$1}}/g; s/([0-9\\+\\*]+)/\\(${1}\\)/g; s/(?"〇", 1 =>"一", 2 =>"二", 3 =>"三", 4 =>"四", 5 =>"五", 6 =>"六", 7 =>"七", 8 =>"八", 9 =>"九", ); our %places = ( 1 => 10, 2 => 100, 3 => 1000, 4 => 10000, 8 => 100000000, 12 => 1000000000000, 16 => 10000000000000000, 20 => 100000000000000000000, ); our %abig = ( 10 =>"十", 100 =>"百", 1000 =>"千", 10000 =>"万", 100000000 =>"億", 1000000000000 =>"兆", 10000000000000000 =>"京", 100000000000000000000 =>"垓", ); our $MAX = 24; #We only support numbers up to 24 digits! sub arabic2kanji{ my @number = reverse(split(//,$_[0])); my @kanji; for(my $i=$#number;$i>=0;$i--){ if( $i==0 ){push(@kanji,$asmall{$number[$i]});} elsif( $i % 4 == 0 ){ if( $number[$i] !~ m/[01]/ ){ push(@kanji,$asmall{$number[$i]}); } push(@kanji,$abig{$places{$i}}); }else{ my $p = $i % 4; if( $number[$i]==0 ){ next; }elsif( $number[$i]==1 ){ push(@kanji,$abig{$places{$p}}); }else{ push(@kanji,$asmall{$number[$i]}); push(@kanji,$abig{$places{$p}}); } } } return join("",@kanji); } sub eval_k2a{ #feed me utf-8! if($_[0] !~ m/^[〇一二三四五六七八九十百千万億兆京垓]+$/){ print"Error:".$_[0]. " not a Kanji number.\ " if defined($_[1])&&$_[1]==1; return -1; } my $expression = kanji2arabic($_[0]); print $expression."\ " if defined($_[1])&&$_[1]==1; return eval($expression); } 1; |
然后你会像这样从另一个脚本调用它,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/usr/bin/perl -w use strict; use warnings; use Encode; use kanjiArabic; my $kanji = kanjiArabic::arabic2kanji($ARGV[0]); print"Kanji:".encode("utf8",$kanji)."\ "; my $arabic = kanjiArabic::eval_k2a($kanji); print"Back to arabic...\ "; print"Arabic:".$arabic."\ "; |
并像这样使用这个脚本,
1 2 3 4 | kettle:~/k2a$ ./k2a.pl 5000215 Kanji: 五百万二百十五 Back to arabic... Arabic: 5000215 |
摇滚吧。
你的问题启发了我使用 Python 解决这个问题。
如果您没有找到 C 解决方案,那么将其调整为 C 应该不会太难。
这实际上相当困难,尤其是当您开始查看非常大的数字的晦涩汉字时。
在 perl 中,Lingua::JA::Numbers 中有一个非常完整的实现。如果您想将其移植到 C 中,它的来源可能会鼓舞人心。