Сортировка строк UTF-8 в RoR - PullRequest
15 голосов
/ 11 июня 2009

Я пытаюсь найти «правильный» способ сортировки строк UTF-8 в Ruby on Rails.

В моем приложении у меня есть поле выбора, заполненное странами. Поскольку мое приложение локализовано, в каждой существующей локали есть файл country.yml, который связывает идентификатор страны с локализованным именем этой страны. Я не могу отсортировать строки вручную в файле yml, потому что мне нужно, чтобы идентификатор был одинаковым во всех локалях.

Я создал метод ascii_name, который использует гем unidecode для преобразования акцентированных и нелатинских символов в их эквивалент 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 ]

10 голосов
/ 18 июня 2013

Сравнение строк в 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 предоставляет этот замечательный инструмент .

8 голосов
/ 05 августа 2009

http://github.com/grosser/sort_alphabetical

Этот драгоценный камень должен помочь. Он добавляет sort_alphabetical и sort_alphabetical_by методы в Enumberable.

4 голосов
/ 18 июня 2013

Единственное решение, которое я нашел до сих пор, - это использование ActiveSupport::Inflector.transliterate(string) для замены символов Юникода на символы ASCII и сортировки:

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

Теперь единственная проблема состоит в том, что это уравнивает «ä» с «a» (DIN 5007-1), и я получаю «Ägypten» перед «Albanien», тогда как я ожидаю, что это будет наоборот. К счастью, транслитерация настраивается для замены символов.

См. Документацию: http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate

1 голос
/ 09 сентября 2010

Единственное рабочее решение, которое я нашел до сих пор (по крайней мере для Ruby 1.8, потому что Ruby 1.9 должен лучше обрабатывать Unicode), это Unicode от Yoshida Masato . Вы можете найти метод Unicode.strcmp там.

РЕДАКТИРОВАТЬ: Извините, это решение использует разложение NFD, а также со всеми его ограничениями.

1 голос
/ 11 июня 2009

Есть несколько способов пойти. Вы можете преобразовать строки 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
0 голосов
/ 11 августа 2009

То, что вы пытаетесь сделать, - это очень грязное предложение. Невозможно сделать прозрачную транслитерацию для всех символов Юникода, потому что значение орграфов меняется от локали к локали, и строки могут вырасти ОГРОМНЫМИ (если, скажем, вы заменяете 10 китайских символов их фонетическими эквивалентами). Не ходи туда.

Почему вы хотите транслитерировать имена в первую очередь? Для URL? Браузеры теперь обрабатывают URL-адреса Unicode прилично, так что вы изобретаете огромную проблему из ничего. Если вам нужны идентификаторы, предварительно обработайте свои списки, чтобы включить стабильный числовой идентификатор для каждой страны, и используйте его в качестве идентификатора. Или сохраните английское название страны в качестве идентификатора (вы можете бесплатно скачать списки стран ISO с учетом локали).

Если вы действительно хотите хорошую транслитерацию для Unicode (а в данном случае это не то, что вам нужно), посмотрите библиотеки IBM ICU, для них есть спящий драгоценный камень.

0 голосов
/ 12 июня 2009

Вы пытались получить доступ к методу mb_chars для каждой строки вашей страны? mb_chars - это прокси, добавляемый ActiveSupport, который определяет безопасные версии Unicode для всех методов String. Если компаратор поддерживает Unicode, сортировка должна работать правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...