在RoR中对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) 

但是,这有明显的问题:

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

有谁知道一种更好的方法,我可以排序我的字符串?

http://github.com/grosser/sort_alphabetical

这个gem应该有帮助。 它将sort_alphabeticalsort_alphabetical_by方法添加到Enumberable。

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

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

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

 require "ffi-icu" ICU::Collation.collate("it_IT", %w[à ae]) # => ["a", "à", "e"] ICU::Collation.collate("de", %w[asx ß]) # => ["a", "s", "ß", "x"] 

作为备选:

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

更新为了测试字符串应如何根据区域设置规则进行整理,ICU项目提供了这个不错的工具 。

到目前为止,我找到的唯一解决方案是使用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

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

 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 

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

编辑:对不起,这个解决方案使用NFD分解以及它的所有限制。

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

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

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

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

  • mb_chars文档