关于C#:如何使用ICU解析汉字数字字符?

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 代码提供了更正:new RuleBasedNumberFormat(URBNF_SPELLOUT,jaLocale, status);


我不久前创建了一个小的 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 中,它的来源可能会鼓舞人心。