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.

È stato utile?

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"
)

& # 8211; http: // blog. slashpoundbang.com/post/12938588984/remove-all-accents-and-diacritics-from-string-in-ruby

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.

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 :)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top