在RoR中对UTF-8字符串进行排序

时间:2009-06-11 18:55:59

标签: ruby-on-rails ruby sorting utf-8

我试图找出一种在Ruby on Rails中对UTF-8字符串进行排序的“正确”方法。

在我的应用程序中,我有一个填充了国家/地区的选择框。由于我的应用程序已本地化,因此每个现有语言环境都有一个countries.yml文件,该文件将国家/地区的ID与该国家/地区的本地化名称相关联。我无法在yml文件中手动对字符串进行排序,因为我需要ID在所有语言环境中保持一致。

我所做的是创建一个ascii_name方法,该方法使用unidecode gem将重音和非拉丁字符转换为其ascii等效字符(例如,“Afeganistão”将成为“Afeganistao”) ,然后对此进行排序:

require 'unidecode'

class Country
  def ascii_name
    Unidecoder.decode(name).gsub("[?]", "").gsub(/`/, "'").strip
  end
end

Country.all.sort_by(:&ascii_name)

然而,这有明显的问题:

  • 它无法正确排序非拉丁语区域设置,因为可能没有直接类似的拉丁字符。
  • 它没有区分字母和该字母的所有重音形式(例如,A和Ä可以互换)

有没有人知道我可以对字符串进行排序的更好方法?

7 个答案:

答案 0 :(得分:10)

Ruby根据字符的字节值进行字符串比较:

%w[à a e].sort
# => ["a", "e", "à"]

要根据区域设置正确整理字符串,可以使用ffi-icu gem:

require "ffi-icu"

ICU::Collation.collate("it_IT", %w[à a e])
# => ["a", "à", "e"]

ICU::Collation.collate("de", %w[a s x ß])
# => ["a", "s", "ß", "x"]

作为替代方案:

collator = ICU::Collation::Collator.new("it_IT")
%w[à a e].sort { |a, b| collator.compare(a, b) }
# => %w[a à e]

更新要测试字符串应如何根据区域设置规则进行整理,ICU项目会提供this nice tool

答案 1 :(得分:8)

http://github.com/grosser/sort_alphabetical

这个宝石应该有所帮助。它会将sort_alphabeticalsort_alphabetical_by方法添加到Enumberable。

答案 2 :(得分:4)

到目前为止,我找到的唯一解决方案是使用ActiveSupport::Inflector.transliterate(string)将ASCII字符替换为unicode字符并进行排序:

Country.all.sort_by do |country|
  ActiveSupport::Inflector.transliterate country.name
end

现在唯一的问题是,这使“ä”与“a”(DIN 5007-1)相等,我最终在“阿尔巴尼亚”之前得到了“Ägypten”,而我希望它是另一种方式。值得庆幸的是,音译可以配置如何替换字符。

请参阅文档:http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate

答案 3 :(得分:1)

有几种方法可以去。您可能希望将UTF字符串转换为十六进制字符串,然后对它们进行排序:

s.split(//).collect { |x| x.unpack('U').to_s }.join

或者您可以使用库iconv。阅读并适当使用它(来自dzone):

#add this to environment.rb
#call to_iso on any UTF8 string to get a ISO string back
#example : "Cédez le passage aux français".to_iso

class String
  require 'iconv' #this line is not needed in rails !
  def to_iso
    Iconv.conv('ISO-8859-1', 'utf-8', self)
  end
end

答案 4 :(得分:1)

到目前为止我找到的唯一可行的解​​决方案(至少对于Ruby 1.8,因为Ruby 1.9应该更好地处理Unicode)是Unicode by Yoshida Masato。你可以在那里找到Unicode.strcmp方法。

编辑:很抱歉,此解决方案还使用了NFD分解及其所有限制。

答案 5 :(得分:0)

你要做的是一个非常混乱的主张。没有办法对所有Unicode字符进行透明音译,因为有向图的含义从语言环境变为语言环境,字符串可以增长很大(如果你用它们的语音等价物替换10个中文符号)。不要去那里。

为什么你首先想要音译名称?对于URL?浏览器现在可以很好地处理Unicode URL,因此您无法凭空创造一个巨大的问题。如果您需要ID,请预先处理列表以包含每个国家/地区的稳定数字ID,并将其用作标识符。或者将国家/地区的英文名称保存为identitifer(您可以免费下载可识别区域设置的ISO国家/地区列表)。

如果你真的想要对Unicode进行良好的音译(在这种情况下这不是你想要的),请参阅IBM ICU库,它们有一个休眠的宝石。

答案 6 :(得分:-2)

您是否尝试过为每个国家/地区字符串访问mb_chars方法? mb_chars是ActiveSupport添加的代理,它定义了所有String方法的Unicode安全版本。如果比较器支持Unicode,那么排序应该可以正常工作。