1

我有一个表示数字的任意 Unicode 字符串,例如“2”、“٢”(U+0662,阿拉伯印度数字二)或“Ⅱ”(U+2161,罗马数字二)。我想将该字符串转换为int。我不关心特定的语言环境(输入可能不在当前语言环境中);如果它是一个有效的数字,那么它应该被转换。

我试过QString.toIntand QLocale.toInt,但他们似乎没有完成工作。例子:

bool ok;
int n;
QString s = QChar(0x0662); // ARABIC-INDIC DIGIT TWO

n = s.toInt(&ok); // n == 0; ok == false

QLocale anyLocale(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
n = anyLocale.toInt(s, &ok); // n == 0; ok == false

QLocale cLocale = QLocale::C;
n = cLocale.toInt(s, &ok); // n == 0; ok == false

QLocale arabicLocale = QLocale::Arabic; // Specific locale. I don't want that.
n = arabicLocale.toInt(s, &ok); // n == 2; ok == true

有我缺少的功能吗?

我可以尝试所有语言环境:

QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
for(int i = 0; i < allLocales.size(); i++)
{
    n = allLocales[i].toInt(s, &ok);
    if(ok)
        break;
}

但这感觉有点骇人听闻。此外,它不适用于所有字符串(例如罗马数字,但这是可以接受的限制)。这样做是否有任何陷阱,例如不同语言环境中的冲突规则(参见土耳其语与非土耳其语字母大小写规则)?

4

2 回答 2

4

我不知道有任何现成的包可以做到这一点(但也许 ICU 支持它),但如果你真的想这样做并不难。首先,您应该从http://www.unicode.org/Public/UNIDATA/UnicodeData.txt下载 UnicodeData.txt 文件。这是一个易于解析的 ASCII 文件;确切的语法在http://www.unicode.org/reports/tr44/tr44-10.html中进行了描述,但出于您的目的,您只需要知道文件中的每一行都由分号分隔的字段组成. 第一个字段包含十六进制的字符代码,第三个字段是“一般类别”,如果第三个字段是“Nd”(数字,十进制),第七个字段包含十进制值。

可以使用 Python 或许多其他脚本语言轻松解析此文件,以构建映射表。您将需要某种稀疏表示,因为有超过一百万个 Unicode 字符,其中很少(几百个)是十进制数字。以下 Python 脚本将为您提供一个 C++ 表,该表可用于初始化 std::map<int, int>;。如果字符在映射中,映射的元素就是它的值。

这是否足够取决于您的应用程序。它有几个弱点:

  • 当两个连续的数字在不同的字母表中时,它需要额外的逻辑来识别。据推测,一个序列"1١" 应该被视为两个数字(1 和 1),而不是一个(11)。(因为所有的十进制数字集都在 10 个连续的代码中,一旦你知道了数字,就很容易检查前面的数字字符是否在同一个集合中。)

  • 它会忽略非十进制数字,例如 ௰ 或 ൱(泰米尔语 10 和马来语 100)。它们的数量并不多,而且它们也在 UnicodeData.txt 文件中,因此可以手动找到它们并将它们添加到表中。但是,我不知道自己在组成数字时如何与其他数字结合。

  • 如果你正在转换数字,你可能不得不担心方向。我不确定这是如何处理的(但 Unicode 站点上有文档);通常,文本会以其自然顺序出现。在阿拉伯语和相关语言的情况下,当以自然顺序阅读时,低位数字首先出现:类似"١٢"(字面意思是"12",但由于书写是从右到左,数字将按顺序出现"21")应该解释为12,而不是 21。除了我不确定是否存在更改方向标记。(确切的规则在 Unicode 站点的文档中进行了描述;在 UnicodeData.txt 文件中,第五个字段——索引 4——给出了这个信息。我想如果它不是"AN",您可以假设欧洲使用的大端标准,但我不确定。)

只是为了说明这是多么简单,这是用于解析 UnicodeData.txt 文件以获取数字值的 Python 脚本:

print('std::pair<int, int> initUnicodeMap[] = {')
for line in open("UnicodeData.txt"):
    fields = line.split(';')
    if fields[2] == 'Nd':
        print('    {{{:d}, {:d}}},'.format(int(fields[0], 16), int(fields[7])))
print('};')

如果您正在使用 Unicode 进行任何工作,那么此文件是生成各种有用表格的金矿。

于 2012-12-08T19:38:05.323 回答
2

您可以使用以下方法获取 unicode 字符的数字等价物QChar::digitValue

int value = QChar::digitValue((uint)0x0662);

-1如果字符没有数值,它将返回。

如果您需要更多帮助,请参阅文档,我对 c++/qt 不太了解


那篇维基百科文章中提到的中文数字属于0x4E00-0x9FCC. 此范围内没有关于单个字符的有用元数据:

4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
9FCC;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;

所以如果你想把中国数字映射成整数,你必须自己做映射,就这么简单。

这是维基百科文章中符号的简单映射,其中单个符号映射到某个单个数字:

0x96f6,0x3007 = 0
0x58f9,0x4e00,0x5f0c = 1
0x8cb3,0x8d30,0x4e8c,0x5f0d,0x5169,0x4e24 = 2
0x53c3,0x53c1,0x4e09,0x5f0e,0x53c3,0x53c2,0x53c4,0x53c1 = 3
0x8086,0x56db,0x4989 = 4
0x4f0d,0x4e94 = 5
0x9678,0x9646,0x516d = 6
0x67d2,0x4e03 = 7
0x634c,0x516b = 8
0x7396,0x4e5d = 9
0x62fe,0x5341,0x4ec0 = 10
0x4f70,0x767e = 100
0x4edf,0x5343 = 1000
0x842c,0x842c,0x4e07 = 10000
0x5104,0x5104,0x4ebf = 100000000

0x5e7a = 1
0x5169,0x4e24 = 2
0x5440 = 10
0x5ff5,0x5eff = 20
0x5345 = 30
0x534c = 40
0x7695 = 200

0x6d1e = 0
0x5e7a = 1
0x4e24 = 2
0x5200 = 4
0x62d0 = 7
0x52fe = 9
于 2012-12-08T19:25:10.700 回答