在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_alphabetical
和sort_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
文档