webentwicklung-frage-antwort-db.com.de

Wie ersetze ich lateinische Akzentzeichen in Ruby?

Ich habe ein ActiveRecord-Modell, Foo, das ein name-Feld hat. Ich möchte, dass Benutzer nach Namen suchen können, aber ich möchte, dass bei der Suche die Groß- und Kleinschreibung und Akzente ignoriert werden. Daher speichere ich auch ein canonical_name-Feld, nach dem gesucht werden soll:

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

Ich muss das "etwas hier" ausfüllen, um die Akzentzeichen zu ersetzen. Gibt es etwas Besseres als

x.downcase.gsub(/[àáâãäå]/,'a').gsub(/æ/,'ae').gsub(/ç/, 'c').gsub(/[èéêë]/,'e')....

Und da ich nicht auf Ruby 1.9 bin, kann ich diese Unicode-Literale nicht in meinen Code einfügen. Die tatsächlichen regulären Ausdrücke werden viel hässlicher aussehen.

66
James A. Rosen

Rails verfügt bereits über ein integriertes Element zum Normalisieren. Sie müssen dies nur verwenden, um Ihre Zeichenfolge zu KD zu normalisieren und dann die anderen Zeichen (d. H. Akzentzeichen) wie folgt zu entfernen:

>> "àáâãäå".mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.to_s
=> "aaaaaa"
56
unexist

ActiveSupport::Inflector.transliterate (erfordert Rails 2.2.1+ und Ruby 1.9 oder 1.8.7)

beispiel: 

>> ActiveSupport::Inflector.transliterate("àáâãäå").to_s => "aaaaaa"

85
Mark Wilden

Noch besser ist es, I18n zu verwenden:

1.9.3-p392 :001 > require "i18n"
 => false
1.9.3-p392 :002 > I18n.transliterate("Olá Mundo!")
 => "Ola Mundo!"
38
Diego Moreira

Ich habe viele dieser Ansätze ausprobiert, aber sie haben eine oder mehrere dieser Anforderungen nicht erfüllt:

  • Räume respektieren
  • Respektieren Sie 'ñ' Zeichen
  • Fall respektieren (Ich weiß, ist keine Voraussetzung für die ursprüngliche Frage, aber es ist nicht schwierig, einen String nach lowcase zu verschieben).

War das hier:

# coding: utf-8
string.tr(
  "ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž",
  "AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz"
)

- http://blog.slashpoundbang.com/post/12938588984/remove-all-accents-and-diacritics-from-string-in-Ruby

Sie müssen die Zeichenliste ein wenig ändern, um 'ñ' Zeichen zu respektieren, aber es ist ein einfacher Job.

19
fguillen

Meine Antwort: die String # parameterize Methode:

"Le cœur de la crémiére".parameterize
=> "le-coeur-de-la-cremiere"

Für Nicht-Rails-Programme:

Installieren Sie activesupport: gem install activesupport und dann:

require 'active_support/inflector'
"a&]'s--3\014\xC2àáâã3D".parameterize
# => "a-s-3-3d"
10
Dorian

Ich denke, dass Sie vielleicht nicht wirklich wissen, was Sie diesen Weg gehen sollen. Wenn Sie sich für einen Markt mit solchen Briefen entwickeln, werden Ihre Benutzer wahrscheinlich denken, dass Sie eine Art von ...pip sind. Weil 'å' in der Bedeutung eines Benutzers nicht in der Nähe von 'a' liegt . Nehmen Sie einen anderen Weg und informieren Sie sich über das Suchen auf nicht-ascii-Weise. Dies ist nur einer der Fälle, in denen jemand Unicode und Kollatierung erfunden hat.

A sehr spät PS:

http://www.w3.org/International/wiki/Case_foldinghttp://www.w3.org/TR/charmod-norm/#sec-WhyNormalization

Außerdem habe ich keine Möglichkeit, den Link zur Kollatierung zu einer Msdn-Seite zu führen, aber ich lasse es dort. Es hätte http://www.unicode.org/reports/tr10/ sein sollen. _ 

7
Jonke

Zerlegen Sie den String und entfernen Sie Nicht-Abstand-Markierungen daraus.

irb -ractive_support/all
> "àáâãäå".mb_chars.normalize(:kd).gsub(/\p{Mn}/, '')
aaaaaa

Möglicherweise benötigen Sie dies auch, wenn Sie in einer .rb-Datei verwendet werden.

# coding: utf-8

der normalize(:kd)-Teil teilt hier diakritische Zeichen auf, wo dies möglich ist (z. B. das Einzelzeichen "n with tilda" wird in ein n gefolgt von einem kombinierten diakritischen Tilda-Zeichen aufgeteilt), und der gsub-Teil entfernt dann alle diakritischen Zeichen.

5
Cheng

Dies setzt voraus, dass Sie Rails verwenden.

"anything".parameterize.underscore.humanize.downcase

In Anbetracht Ihrer Anforderungen würde ich dies wahrscheinlich tun. Ich denke, es ist ordentlich, einfach und wird in zukünftigen Versionen von Rails und Ruby auf dem neuesten Stand bleiben.

Update: dgilperez wies darauf hin, dass parameterize ein Trennungsargument benötigt, sodass "anything".parameterize(" ") (veraltet) oder "anything".parameterize(separator: " ") kürzer und sauberer ist.

4
Sudhir Jonathan

Konvertieren Sie den Text in die Normalisierungsform D, entfernen Sie alle Codepunkte mit der Nichtkennzeichnungsmarke (Mn) für die Unicode-Kategorie und konvertieren Sie ihn zurück in die Normalisierungsform C. Dadurch werden alle Diakritiken entfernt und Ihr Problem wird auf eine Groß-/Kleinschreibung reduziert.

Siehe http://www.siao2.com/2005/02/19/376617.aspx und http://www.siao2.com/2007/05/14/2629747.aspx für Einzelheiten .

3
CesarB

Der Schlüssel ist die Verwendung von zwei Spalten in Ihrer Datenbank: canonical_text und original_text. Verwenden Sie original_text für die Anzeige und canonical_text für Suchen. Wenn ein Benutzer nach "Visual Cafe" sucht, sieht er auf diese Weise das Ergebnis "Visual Café". Wenn sie wirklich ein anderes Element namens "Visual Cafe" möchte, kann es separat gespeichert werden.

Um die kanonischen Textzeichen in einer Ruby 1.8-Quelldatei abzurufen, gehen Sie folgendermaßen vor:

register_replacement([0x008A].pack('U'), 'S')
3
James A. Rosen

Möglicherweise möchten Sie die Unicode-Zerlegung ("NFD"). Nachdem Sie den String zerlegt haben, filtern Sie einfach alles, was nicht in [A-Za-z] ist. æ zerfällt in "ae", ã in "a ~" (ungefähr - der Diakritische wird zu einem eigenen Zeichen), so dass die Filterung eine vernünftige Annäherung hinterlässt.

2
MSalters

Für alle, die dies lesen möchten, um alle nicht-ASCII-Zeichen zu entfernen dies könnte nützlich sein, habe ich das erste Beispiel erfolgreich verwendet.

1
Kris
1
Gene T

Ich hatte Probleme, die Lösung foo.mb_chars.normalize (: kd) .gsub (/ [^\x00-\x7F]/n, ''). Downcase.to_s zu installieren. Ich benutze kein Rails und es gab einen Konflikt mit meiner Activesupport/Ruby-Version, bei der ich nicht auf den Grund gehen konnte.

Die Verwendung des Ruby-unf-Gems scheint ein guter Ersatz zu sein:

require 'unf'
foo.to_nfd.gsub(/[^\x00-\x7F]/n,'').downcase

Soweit ich das beurteilen kann, tut dies dasselbe wie .mb_chars.normalize (: kd). Ist das richtig? Vielen Dank!

0

Wenn Sie PostgreSQL => 9.4 als DB-Adapter verwenden, können Sie bei einer Migration möglicherweise "unaccent" -Erweiterung hinzufügen dass ich denke macht was du willst, so:

def self.up
   enable_extension "unaccent" # No falla si ya existe
end

Zum Testen in der Konsole:

2.3.1 :045 > ActiveRecord::Base.connection.execute("SELECT unaccent('unaccent', 'àáâãäåÁÄ')").first
 => {"unaccent"=>"aaaaaaAA"}

Beachten Sie, dass bis jetzt zwischen Groß- und Kleinschreibung unterschieden wird.

Dann verwenden Sie es vielleicht in einem Bereich wie:

scope :with_canonical_name, -> (name) {
   where("unaccent(foos.name) iLIKE unaccent('#{name}')")
}

Der iLIKE-Operator unterscheidet nicht zwischen Groß- und Kleinschreibung. Es gibt einen anderen Ansatz, bei dem der Datentyp citext verwendet wird. Hier ist eine Diskussion über diese beiden Ansätze. Beachten Sie auch, dass die Verwendung der lower () - Funktion von PosgreSQL nicht empfohlen .

Dies spart Ihnen Speicherplatz in der Datenbank, da Sie das Feld cannonical_name nicht mehr benötigen und möglicherweise Ihr Modell auf Kosten einer zusätzlichen Verarbeitung in jeder Abfrage in einer Menge vereinfachen, die davon abhängt, ob Sie iLIKE oder citext verwenden, und Ihr Datensatz.

Wenn Sie MySQL verwenden , können Sie vielleicht verwenden Sie diese einfache Lösung , aber ich habe es nicht getestet.

0
user2553863