web-dev-qa-db-ja.com

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リテラルをコードに入れることはできません。実際の正規表現はずっと見苦しくなります。

69
James A. Rosen

Railsにはすでに正規化のためのビルトインがあり、これを使用して文字列を正規化してKDを形成し、次に他の文字(つまりアクセント記号)を削除する必要があります:

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

_ActiveSupport::Inflector.transliterate_(Rails 2.2.1+およびRuby 1.9または1.8.7)が必要です)

例:

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

86
Mark Wilden

さらに良いのは、I18nを使用することです。

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

私はこのアプローチを多数試しましたが、これらの要件の1つまたはいくつかを達成していませんでした。

  • スペースを尊重する
  • 「ñ」文字を尊重する
  • 大文字と小文字の区別(元の質問の要件ではないが、文字列をlowcaseに移動することは難しくないことを知っています)

これがされています:

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

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

「ñ」文字を尊重するには、文字リストを少し修正する必要がありますが、簡単な作業です。

18
fguillen

私の答え: String#parameterize メソッド:

"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"
10
Dorian

私はあなたがたぶんその道をどう進むべきか本当にわからないと思います。このような種類の文字を含む市場向けに開発している場合、ユーザーはおそらく...pipのように思うでしょう。ユーザーにとって「å」は「a」に近い意味でもないからです。別の道を進み、非ASCIIの方法での検索について読んでください。これは、誰かがユニコードと 照合 を発明したケースの1つにすぎません。

非常に遅いPS

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

それに加えて、照合へのリンクがmsdnページに行く理想的な方法はありませんが、そこに残します。 http://www.unicode.org/reports/tr10/

7
Jonke

文字列を分解して、 非スペースマーク を削除します。

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

.rbファイルで使用する場合、これも必要になる場合があります。

_# coding: utf-8
_

ここのnormalize(:kd)部分は、可能であれば分音記号を分割します(例:「n with tilda」単一文字はnに分割され、その後に結合分音記号チルダ文字が続きます)、そしてgsub部分すべての発音区別文字を削除します。

6
Cheng

これはRailsを使用することを前提としています

_"anything".parameterize.underscore.humanize.downcase
_

あなたの要件を考えると、これはおそらく私がやることです...私はそれがきちんとしていて、シンプルだと思いますRails and Ruby。

更新:dgilperezは、parameterizeがセパレーター引数を取るため、"anything".parameterize(" ")(非推奨)または"anything".parameterize(separator: " ")がより短く、よりクリーンであることを指摘しました。

4
Sudhir Jonathan

テキストを正規化フォームDに変換し、Unicodeカテゴリーの非スペーシングマーク(Mn)を持つすべてのコードポイントを削除し、正規化フォームCに変換します。これにより、すべての発音区別符号が削除され、大文字と小文字を区別しない検索になります。

http://www.siao2.com/2005/02/19/376617.aspx および http://www.siao2.com/2007/05/14/2629747を参照してください。詳細については、aspx .

3
CesarB

重要な点は、データベースで2つの列を使用することです:canonical_textおよびoriginal_text。使用する original_text表示およびcanonical_text検索用。これにより、ユーザーが「Visual Cafe」を検索すると、「VisualCafé」の結果が表示されます。彼女がreallyで「Visual Cafe」という別のアイテムが必要な場合は、個別に保存できます。

Ruby 1.8ソースファイルのcanonical_text文字を取得するには、次のようにします。

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

おそらくUnicode分解( "NFD")が必要でしょう。文字列を分解した後、[A-Za-z]に含まれていないものをすべて除外します。 æは「ae」に、ãは「a〜」に分解されます(およそ-ダイアクリティカルマークは別の文字になります)。したがって、フィルタリングは妥当な近似を残します。

2
MSalters

これを読んでいるすべての非ASCII文字を削除したい場合 this が役に立つかもしれません。最初の例をうまく使いました。

1
Kris
1
Gene T

Foo.mb_chars.normalize(:kd).gsub(/ [^\x00-\x7F]/n、 '')。downcase.to_sソリューションを機能させるのに問題がありました。 Railsを使用していないため、activesupport/Rubyのバージョンとの競合が発生したため、最後まで到達できませんでした。

Ruby-unf gemを使用するのは良い代替策のようです:

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

私が知る限り、これは.mb_chars.normalize(:kd)と同じことを行います。これは正しいです?ありがとう!

0

PostgreSQLを使用している場合=> 9.4 DBアダプタとして、移行に追加することができます "unaccent" extension 私はあなたが望むことをこのように思うと思う:

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 データ型を使用する別のアプローチがあります。 ここ は、この2つのアプローチに関する議論です。また、PosgreSQLのlower()関数の使用 推奨されません にも注意してください。

これにより、cannonical_nameフィールドが不要になり、おそらく各クエリで追加の処理が必要になりますが、iLIKEまたはcitextのどちらを使用しているかに応じて、モデルを単純化するため、DBスペースが節約されます。データセット。

MySQLを使用している場合は、おそらく この単純なソリューションを使用 が可能ですが、テストは行っていません。

0
user2553863