在Ruby中将Unicode数字转换为整数

4

我有一些非ASCII数字的字符串需要转换为普通的Ruby数字进行数学计算。例如,如果作为字符串的数字“۱۹”输入,它实际上是19,但由“扩展阿拉伯印度数字1”和“扩展阿拉伯印度数字9”组成,我需要一种方法将其转换为Ruby整数Fixnum 19。

问题在于,根据这个网站,有55组0-9的这些扩展数字,即总共需要处理550个代码点。

我已经知道对于给定的组,连续数字的代码点是连续的。例如,“扩展阿拉伯印度数字0”是U+06F0,“扩展阿拉伯印度数字9”是U+06F9,因此我可以测试每个数字以查看它处于哪个范围,然后从正在查看的字符的代码点中减去零代码点作为整数,从而得到常规Ruby整数。例如,6F9 - 6F0 = 9(粗略地说,一旦它们被转换为它们的整数代码点)。

但是,要做到这一点,我需要为这55个范围创建一个巨大的查找哈希表,这是很多键入工作。我想我可以将上面链接中的HTML表格翻译成一个Ruby map,但这感觉很hacky。

我已经知道

"۱۹" =~ /[[:digit:]]+/

这两者确实是相匹配的,但问题是“如何将这些Unicode数字转换为常规的Ruby整数?”

一定有更好的方法!您有什么想法吗?

谢谢!

1个回答

6
这相对来说并不痛苦。
class DecimalToIntegerConverter
  altzeros = [0x06f0, 0xff10] # ... need all zeroes here
  @@digits = altzeros.flat_map { |z| ((z.chr(Encoding::UTF_8))..((z+9).chr(Encoding::UTF_8))).to_a }.join('')
  @@replacements = "0123456789" * altzeros.size
  def self.convert(str)
    str.tr(@@digits, @@replacements).to_i
  end
end

str = "۱۹ and 25?"
str.scan(/[[:digit:]]+/).map do |s|
  DecimalToIntegerConverter.convert(s)
end
# => [19, 25]

谢谢@Amadan,看起来这个方法很有效。我不必输入55个范围,但我仍然需要输入55个altzeros。虽然我可以做到...但是有没有另一种方法,甚至不需要我输入55个altzeros?是否有宝石或其他魔法可以实现? - Joel
@CarySwoveland "0123456789" * altzeros.size 确保两个字符串具有相同的大小。 - Stefan
@Stefan,哦,我明白了。谢谢。 - Cary Swoveland
你也可以创建一个替换哈希hash = {'۱' => '1', ..., '۹' => 9},并通过'۱۹'.gsub(/[[:digit:]]/, hash)替换数字。 - Stefan
我曾希望 Twitter I18N gem twitter_cldr 内置了这个功能,但是没有找到任何实现它的方法。还有其他人可以验证吗? - Keith Bennett
多谢 @Stefan,我可能最终就这么做了。只是得键入哈希值。我实际上会使用 Nokogiri 从一个包含所有 550 个扩展 Unicode 数字的 HTML 页面中解析出数字代码点,并生成相应的 Ruby 代码。:-) - Joel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接