代码之家  ›  专栏  ›  技术社区  ›  Daniel Vandersluis

在ror中对utf-8字符串进行排序

  •  15
  • Daniel Vandersluis  · 技术社区  · 16 年前

    我正试图找出一种在RubyonRails中对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 回复  |  直到 16 年前
        1
  •  8
  •   Paweł Gościcki Ywain    10 年前

    http://github.com/grosser/sort_alphabetical

    这个宝石应该有用。它补充说 sort_alphabetical sort_alphabetical_by 方法可枚举。

        2
  •  10
  •   toro2k    12 年前

    基于字符字节值的Ruby Peforms字符串比较:

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

    要根据区域设置正确地排序字符串,请 ffi-icu 宝石可用于:

    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 .

        3
  •  4
  •   Kostas    12 年前

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

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

    现在唯一的问题是,这等于“A”(DIN5007-1)的“_”和“A”(DIN5007-1),而我最终以“阿尔巴尼亚”之前的“_ Gypten”结束,而我希望它是相反的。谢天谢地,对于如何替换字符,音译是可配置的。

    见文件: http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate

        4
  •  1
  •   Ryan Oberoi    16 年前

    有两种方法。您可能需要将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
    
        5
  •  1
  •   skalee    14 年前

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

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

        6
  •  0
  •   Julik    15 年前

    你想做的是一个非常混乱的提议。没有办法对所有Unicode字符进行透明的音译,因为有向图的含义会随着语言环境的变化而变化,字符串也会变得越来越大(如果说用YR语音等价物替换10个中文符号)。别去那儿。

    你为什么要把音译的名字放在第一位?对于URL?浏览器现在可以很好地处理unicode URL,所以你在凭空创造一个巨大的问题。如果您需要ID,请预处理您的列表以包括每个国家的稳定数字ID,并将其用作标识符。或者将国家的英文名称另存为Identifier(您可以免费下载支持地区的ISO国家列表)。

    如果您真的想要Unicode的好的音译(在本例中这不是您想要的),请参阅IBMICU库,它们有一个休眠的gem。

        7
  •  -2
  •   John Topley    16 年前

    您是否尝试访问 mb_chars 每个国家字符串的方法? 姆斯查尔斯 是ActiveSupport添加的代理,它定义所有 String 方法。如果比较器支持Unicode,那么排序应该可以正常工作。