我试图找出一种在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)
然而,这有明显的问题:
有没有人知道我可以对字符串进行排序的更好方法?
答案 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_alphabetical
和sort_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,那么排序应该可以正常工作。