Как заменить символы латинского алфавита в Ruby?
У меня есть модель ActiveRecord
, Foo
, которая имеет поле name
. Я бы хотел, чтобы пользователи могли искать по имени, но я хотел бы, чтобы поиск игнорировал случай и любые акценты. Таким образом, я также сохраняю поле canonical_name
, по которому нужно искать:
class Foo
validates_presence_of :name
before_validate :set_canonical_name
private
def set_canonical_name
self.canonical_name ||= canonicalize(self.name) if self.name
end
def canonicalize(x)
x.downcase. # something here
end
end
Мне нужно заполнить "что-то здесь", чтобы заменить акцентированные символы. Есть ли что-нибудь лучше, чем
x.downcase.gsub(/[àáâãäå]/,'a').gsub(/æ/,'ae').gsub(/ç/, 'c').gsub(/[èéêë]/,'e')....
И, если уж на то пошло, поскольку я не на Ruby 1.9, я не могу поместить эти символы Unicode в свой код. Фактические регулярные выражения будут выглядеть намного уродливее.
Ответы
Ответ 1
Rails уже встроен для нормализации, вам просто нужно использовать это, чтобы нормализовать вашу строку, чтобы сформировать KD, а затем удалить другие символы (например, акцентные знаки) следующим образом:
>> "àáâãäå".mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.to_s
=> "aaaaaa"
Ответ 2
ActiveSupport::Inflector.transliterate
(требуется Rails 2.2.1+ и Ruby 1.9 или 1.8.7)
пример:
>> ActiveSupport::Inflector.transliterate("àáâãäå").to_s
=> "aaaaaa"
Ответ 3
Еще лучше использовать I18n:
1.9.3-p392 :001 > require "i18n"
=> false
1.9.3-p392 :002 > I18n.transliterate("Olá Mundo!")
=> "Ola Mundo!"
Ответ 4
Я пробовал много таких подходов, но они не достигли одного или нескольких из этих требований:
- Уважать пробелы
- Символ уважения '-'
- Респект (я знаю, не является обязательным для исходного вопроса, но нетрудно переместить строку в нижний регистр)
Было ли это:
# coding: utf-8
string.tr(
"ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž",
"AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz"
)
- http://blog.slashpoundbang.com/post/12938588984/remove-all-accents-and-diacritics-from-string-in-ruby
Вам нужно немного изменить список символов, чтобы уважать символ "-", но это легкая работа.
Ответ 5
Мой ответ: метод String # параметризации:
"Le cœur de la crémiére".parameterize
=> "le-coeur-de-la-cremiere"
Для не-Rails программ:
Установите activesupport: gem install activesupport
затем:
require 'active_support/inflector'
"a&]'s--3\014\xC2àáâã3D".parameterize
# => "a-s-3-3d"
Ответ 6
Я думаю, что вы, может быть, действительно не можете пойти по этому пути. Если вы развиваетесь на рынке, у которого есть такие письма, ваши пользователи, вероятно, подумают, что вы своего рода... пип.
Потому что "å" даже не приближается к "a" в каком-либо смысле для пользователя.
Возьмите другую дорогу и прочитайте о поиске по-не-ascii. Это всего лишь один из тех случаев, когда кто-то изобрел юникод и сопоставление.
Очень поздний PS:
http://www.w3.org/International/wiki/Case_folding
http://www.w3.org/TR/charmod-norm/#sec-WhyNormalization
Кроме того, у меня нет никакого способа, чтобы ссылка на сопоставление переходила на страницу msdn, но я оставляю ее там. Это должно было быть http://www.unicode.org/reports/tr10/
Ответ 7
Разложите строку и удалите из нее неразрывные метки.
irb -ractive_support/all
> "àáâãäå".mb_chars.normalize(:kd).gsub(/\p{Mn}/, '')
aaaaaa
Вам также может понадобиться это, если используется в файле.rb.
# coding: utf-8
часть normalize(:kd)
здесь разделяет диакритические знаки, где это возможно (например, одиночный символ "n с тильдой" разделяется на n, за которым следует объединенный диакритический символ тильды), а затем часть gsub
удаляет все диакритические знаки.
Ответ 8
Предполагается, что вы используете Rails.
"anything".parameterize.underscore.humanize.downcase
Учитывая ваши требования, это, вероятно, то, что я бы сделал... Я думаю, что это аккуратно, просто и будет оставаться в курсе будущих версий Rails и Ruby.
Обновление: dgilperez указал, что parameterize
принимает аргумент разделителя, поэтому "anything".parameterize(" ")
(устаревший) или "anything".parameterize(separator: " ")
короче и чище.
Ответ 9
Преобразуйте текст в форму нормализации D, удалите все кодовые страницы с меткой un-интервала unicode (Mn) и преобразуйте ее обратно в форму нормализации C. Это разделит все диакритики, и ваша проблема будет уменьшена до поиска без учета регистра.
См. http://www.siao2.com/2005/02/19/376617.aspx и http://www.siao2.com/2007/05/14/2629747.aspx для деталей.
Ответ 10
Ключ состоит в использовании двух столбцов в вашей базе данных: canonical_text
и original_text
. Используйте original_text
для отображения и canonical_text
для поиска. Таким образом, если пользователь ищет "Visual Cafe", она видит результат "Visual Café". Если она действительно хочет другой элемент под названием "Visual Cafe", его можно сохранить отдельно.
Чтобы получить символы canonical_text в исходном файле Ruby 1.8, сделайте следующее:
register_replacement([0x008A].pack('U'), 'S')
Ответ 11
Вероятно, вам нужно разделить Unicode ( "NFD" ). После разложения строки просто отфильтруйте что-либо не в [A-Za-z]. æ будет разлагаться на "ae", ã на "a" (приблизительно - диакритический станет отдельным символом), поэтому фильтрация оставляет разумное приближение.
Ответ 12
Iconv:
http://groups.google.com/group/ruby-talk-google/browse_frm/thread/8064dcac15d688ce?
=============
модуль perl, который я не могу понять:
http://www.ahinea.com/en/tech/accented-translate.html
============
грубая сила (там много жужжащих тварей!:
http://projects.jkraemer.net/acts_as_ferret/wiki#UTF-8support
http://snippets.dzone.com/posts/show/2384
Ответ 13
Для тех, кто читает это, желая удалить все символы не-ascii этот мог бы быть полезен, я успешно использовал первый пример.
Ответ 14
У меня были проблемы с получением foo.mb_chars.normalize(: kd).gsub(/[^\x00-\x7F]/n, ''). Решение downcase.to_s для работы. Я не использую Rails, и я столкнулся с некоторыми конфликтами с моими версиями activivesupport/ruby, которые я не мог получить в нижней части.
Использование драгоценного камня ruby-unf кажется хорошей заменой:
require 'unf'
foo.to_nfd.gsub(/[^\x00-\x7F]/n,'').downcase
Насколько я могу сказать, это делает то же самое, что и .mb_chars.normalize(: kd). Это верно? Спасибо!
Ответ 15
Если вы используете PostgreSQL => 9.4 в качестве адаптера БД, возможно, вы могли бы добавить в миграцию расширение "unaccent", которое, я думаю, делает то, что вы хотите, например, так:
def self.up
enable_extension "unaccent" # No falla si ya existe
end
Для тестирования в консоли:
2.3.1 :045 > ActiveRecord::Base.connection.execute("SELECT unaccent('unaccent', 'àáâãäåÁÄ')").first
=> {"unaccent"=>"aaaaaaAA"}
Обратите внимание, что до сих пор чувствительны к регистру.
Затем, возможно, используйте его в области видимости, например:
scope :with_canonical_name, -> (name) {
where("unaccent(foos.name) iLIKE unaccent('#{name}')")
}
Оператор iLIKE делает регистр поиска нечувствительным. Существует другой подход, использующий тип данных citext. Вот обсуждение этих двух подходов. Также обратите внимание на то, что использование функции PosgreSQL lower() не рекомендуется.
Это сэкономит вам некоторое пространство БД, поскольку вам больше не потребуется поле cannonical_name и, возможно, сделает вашу модель проще, за счет некоторой дополнительной обработки в каждом запросе, в размере, зависящем от того, используете ли вы iLIKE или citext, и ваш набор данных.
Если вы используете MySQL, возможно, вы можете использовать это простое решение, но я не проверял его.
Ответ 16
lol.. я просто попробовал это.. и он работает.. я все еще не совсем уверен, почему.. но когда я использую эти 4 строки кода:
- str = str.gsub(/[^ a-zA-Z0-9]/, "")
- str = str.gsub(/[] +/, "")
- str = str.gsub(//, "-" )
- str = str.downcase
он автоматически удаляет любой акцент с именами файлов, которые я пытался удалить (акцент с именами файлов и их переименование) надеюсь, что это помогло:)