Come posso sostituire i caratteri latini accentati in Ruby?
-
03-07-2019 - |
Domanda
Ho un modello ActiveRecord
, Foo
, che ha un campo name
. Vorrei che gli utenti potessero cercare per nome, ma vorrei che la ricerca ignorasse il caso e tutti gli accenti. Pertanto, sto anche memorizzando un campo canonical_name
sul quale cercare:
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
Devo compilare il "quot qui qualcosa" " per sostituire i caratteri accentati. C'è qualcosa di meglio di
x.downcase.gsub(/[àáâãäå]/,'a').gsub(/æ/,'ae').gsub(/ç/, 'c').gsub(/[èéêë]/,'e')....
E, del resto, dal momento che non sono su Ruby 1.9, non posso inserire quei letterali Unicode nel mio codice. Le espressioni regolari effettive appariranno molto più brutte.
Soluzione
Rails ha già un builtin per la normalizzazione, devi solo usarlo per normalizzare la tua stringa per formare KD e quindi rimuovere gli altri caratteri (cioè i segni di accento) in questo modo:
>> "àáâãäå".mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.to_s
=> "aaaaaa"
Altri suggerimenti
ActiveSupport :: Inflector.transliterate
(richiede Rails 2.2.1+ e Ruby 1.9 o 1.8.7)
esempio:
> > ActiveSupport :: Inflector.transliterate (" Aaaaaa ") to_s.
= & Gt; & Quot; aaaaaa "
Meglio ancora è usare I18n:
1.9.3-p392 :001 > require "i18n"
=> false
1.9.3-p392 :002 > I18n.transliterate("Olá Mundo!")
=> "Ola Mundo!"
Ho provato molti di questi approcci ma non stavano raggiungendo uno o più di questi requisiti:
- Rispetta gli spazi
- Rispetto '& # 241;' carattere
- Caso di rispetto (so che non è un requisito per la domanda originale ma non è difficile spostare una stringa in lettere minuscole )
È stato questo:
# coding: utf-8
string.tr(
"ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž",
"AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz"
)
Devi modificare un po 'l'elenco dei caratteri per rispettare' & # 241; ' personaggio ma è un lavoro facile.
La mia risposta: il metodo String # parameterize :
"Le cœur de la crémiére".parameterize
=> "le-coeur-de-la-cremiere"
Per i programmi non Rails:
Installa activesupport: gem install activesupport
quindi:
require 'active_support/inflector'
"a&]'s--3\014\xC2àáâã3D".parameterize
# => "a-s-3-3d"
Penso che forse non sai davvero cosa percorrere quel sentiero. Se stai sviluppando per un mercato che ha questo tipo di lettere, i tuoi utenti probabilmente penseranno che sei una sorta di ... pip . Perché "å" non è nemmeno vicino a "a" in alcun senso per un utente. Prendi una strada diversa e leggi di più sulla ricerca in modo non ascii. Questo è solo uno di quei casi in cui qualcuno ha inventato Unicode e collation .
Un PS molto in ritardo :
http://www.w3.org/International/wiki/Case_folding http://www.w3.org/TR/charmod-norm/# sec-WhyNormalization
Oltre a ciò, non ho alcun modo ide il collegamento alla collation vada a una pagina msdn ma lo lascio lì. Avrebbe dovuto essere http://www.unicode.org/reports/tr10/
Decomponi la stringa e rimuovi segni di non spaziatura da essa.
irb -ractive_support/all
> "àáâãäå".mb_chars.normalize(:kd).gsub(/\p{Mn}/, '')
aaaaaa
Potresti anche averne bisogno se usato in un file .rb.
# coding: utf-8
la parte normalize (: kd)
qui suddivide i segni diacritici ove possibile (es: il carattere singolo "t con la tilda" è diviso in un n seguito da un carattere tilda diacritico combinato), e la parte gsub
rimuove quindi tutti i caratteri diacritici.
Questo presuppone che tu usi Rails.
"anything".parameterize.underscore.humanize.downcase
Date le vostre esigenze, questo è probabilmente quello che farei ... Penso che sia pulito, semplice e rimanga aggiornato nelle future versioni di Rails e Ruby.
Aggiornamento: dgilperez ha sottolineato che parametrizza
accetta un argomento separatore, quindi " niente " .parameterize (" ")
(deprecato) o " ; qualunque cosa " .parameterize (separator: " ")
è più breve e più pulito.
Converti il ??testo nel modulo di normalizzazione D, rimuovi tutti i punti di codice con segno di non spaziatura della categoria unicode (Mn) e convertilo nuovamente nel modulo di normalizzazione C. Questo eliminerà tutti i segni diacritici e il tuo problema verrà ridotto a una ricerca senza distinzione tra maiuscole e minuscole.
Vedi http://www.siao2.com/2005/02/ 19 / 376617.aspx e http: //www.siao2. com / 2007/05/14 / 2629747.aspx per i dettagli.
La chiave è utilizzare due colonne nel database: canonical_text
e original_text
. Usa original_text
per la visualizzazione e canonical_text
per le ricerche. In questo modo, se un utente cerca " Visual Cafe " vede il " Visual Caf & # 233; " risultato. Se davvero desidera un oggetto diverso chiamato " Visual Cafe " può essere salvato separatamente.
Per ottenere i caratteri canonical_text in un file sorgente di Ruby 1.8, fai qualcosa del genere:
register_replacement([0x008A].pack('U'), 'S')
Probabilmente vuoi la decomposizione Unicode (" NFD "). Dopo aver decomposto la stringa, è sufficiente filtrare qualsiasi cosa non in [A-Za-z]. æ si decompone in "ae", "in" a ~ "; (approssimativamente - il diacritico diventerà un carattere separato) quindi il filtro lascia una ragionevole approssimazione.
iconv:
http://groups.google.com/group / ruby-talk-google / browse_frm / thread / 8064dcac15d688ce ?
=============
un modulo perl che non capisco:
http://www.ahinea.com/en/tech/accented -translate.html
============
forza bruta (ci sono molte creature htose !:
http://projects.jkraemer.net/acts_as_ferret/wiki#UTF-8support
Per chiunque legga questo desiderio di eliminare tutti i caratteri non ascii questo potrebbe essere utile, ho usato il primo esempio con successo.
Ho avuto problemi a far funzionare la soluzione foo.mb_chars.normalize (: kd) .gsub (/ [^ \ x00- \ x7F] / n, ''). downcase.to_s. Non sto usando Rails e c'è stato un conflitto con le mie versioni di activesupport / ruby ??che non sono riuscito a raggiungere in fondo.
L'uso della gemma ruby-unf sembra essere un buon sostituto:
require 'unf'
foo.to_nfd.gsub(/[^\x00-\x7F]/n,'').downcase
Per quanto ne so, fa la stessa cosa di .mb_chars.normalize (: kd). È corretto? Grazie!
Se stai usando PostgreSQL = > 9.4 come adattatore DB, forse potresti aggiungere in una migrazione " unaccent " estensione che penso faccia quello che vuoi, in questo modo:
def self.up
enable_extension "unaccent" # No falla si ya existe
end
Per testare, nella console:
2.3.1 :045 > ActiveRecord::Base.connection.execute("SELECT unaccent('unaccent', 'àáâãäåÁÄ')").first
=> {"unaccent"=>"aaaaaaAA"}
Si noti che finora è sensibile al maiuscolo / minuscolo.
Quindi, magari usarlo in un ambito, come:
scope :with_canonical_name, -> (name) {
where("unaccent(foos.name) iLIKE unaccent('#{name}')")
}
L'operatore iLIKE rende insensibile il caso di ricerca. Esiste un altro approccio che utilizza il tipo di dati citext . Qui c'è una discussione su questi due approcci. Si noti inoltre che l'uso della funzione inferiore () di PosgreSQL non è raccomandato .
Questo ti farà risparmiare un po 'di spazio DB, poiché non avrai più bisogno del campo cannonical_name, e forse renderai il tuo modello più semplice, a costo di qualche elaborazione extra in ogni query, in una quantità a seconda che tu stia usando iLIKE o citext e il tuo set di dati.
Se stai usando MySQL forse puoi utilizzare questa semplice soluzione , ma ho non testato.
lol .. ho appena provato questo .. e sta funzionando .. iam non sono ancora abbastanza sicuro del perché .. ma quando uso queste 4 righe di codice:
- str = str.gsub (/ [^ a-zA-Z0-9] /, " ")
- str = str.gsub (/ [] + /, " ")
- str = str.gsub (/ /, " - ")
- str = str.downcase
rimuove automaticamente qualsiasi accento dai nomi dei file .. che stavo cercando di rimuovere (accento dai nomi dei file e rinominandoli di) spero che abbia aiutato :)