79

我有一个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 文字放入我的代码中。实际的正则表达式会看起来更丑陋。

4

16 回答 16

99

ActiveSupport::Inflector.transliterate(需要 Rails 2.2.1+ 和 Ruby 1.9 或 1.8.7)

例子:

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

于 2011-08-23T11:47:19.760 回答
63

Rails 已经有一个用于规范化的内置函数,您只需使用它来规范化您的字符串以形成 KD,然后像这样删除其他字符(即重音符号):

>> "àáâãäå".mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.to_s
=> "aaaaaa"
于 2008-11-15T14:18:52.243 回答
43

更好的是使用 I18n:

1.9.3-p392 :001 > require "i18n"
 => false
1.9.3-p392 :002 > I18n.transliterate("Olá Mundo!")
 => "Ola Mundo!"
于 2013-12-14T18:33:34.740 回答
20

我已经尝试了很多这样的方法,但他们没有达到这些要求中的一个或几个:

  • 尊重空间
  • 尊重 'ñ' 字符
  • 尊重大小写(我知道不是原始问题的要求,但将字符串移动到lowcase并不难)

一直是这样的:

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

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

您必须稍微修改字符列表以尊重“ñ”字符,但这很容易。

于 2012-09-10T14:43:03.430 回答
12

我的回答: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"
于 2013-07-23T12:07:55.117 回答
7

我认为您可能并不真正走这条路。如果您正在为具有此类字母的市场进行开发,您的用户可能会认为您是一种……点子。因为对于用户而言,“å”在任何意义上都不接近“a”。走一条不同的路,阅读有关以非 ascii 方式进行搜索的信息。这只是有人发明 unicode 和collat ​​ion 的案例之一。

一个很晚的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/

于 2008-10-23T07:41:08.327 回答
7

分解字符串并从中删除非间距标记

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

如果在 .rb 文件中使用,您可能还需要它。

# coding: utf-8

此处的normalize(:kd)部分在可能的情况下拆分出变音符号(例如:“n with tilda”单个字符被拆分为 n,后跟一个组合变音符号 tilda 字符),然后该gsub部分删除所有变音字符。

于 2012-01-15T15:37:14.523 回答
4

这假设您使用 Rails。

"anything".parameterize.underscore.humanize.downcase

鉴于您的要求,这可能是我会做的……我认为它简洁、简单,并且会在 Rails 和 Ruby 的未来版本中保持最新。

更新:dgilperez 指出parameterize采用分隔符参数,因此"anything".parameterize(" ")(已弃用)"anything".parameterize(separator: " ")或更短且更清晰。

于 2013-04-29T07:54:21.480 回答
3

将文本转换为规范化形式 D,删除所有带有 unicode 类别非间距标记 (Mn) 的代码点,然后将其转换回规范化形式 C。这将去除所有变音符号,您的问题将简化为不区分大小写的搜索。

详见http://www.siao2.com/2005/02/19/376617.aspxhttp://www.siao2.com/2007/05/14/2629747.aspx

于 2008-10-22T14:52:42.077 回答
3

关键是在数据库中使用两列:canonical_textoriginal_text. 用于original_text显示和canonical_text搜索。这样,如果用户搜索“Visual Café”,她就会看到“Visual Café”结果。如果她真的想要一个不同的物品,叫做“视觉咖啡馆”,可以单独保存。

要在 Ruby 1.8 源文件中获取 canonical_text 字符,请执行以下操作:

register_replacement([0x008A].pack('U'), 'S')
于 2009-01-23T18:59:30.800 回答
2

您可能需要 Unicode 分解(“NFD”)。分解字符串后,只需过滤掉不在 [A-Za-z] 中的任何内容。æ 将分解为“ae”,ã 分解为“a~”(大约 - 变音符号将成为单独的字符),因此过滤会留下合理的近似值。

于 2008-10-22T12:14:25.513 回答
1

图标v:

http://groups.google.com/group/ruby-talk-google/browse_frm/thread/8064dcac15d688ce

==============

我无法理解的 perl 模块:

http://www.ahinea.com/en/tech/accented-translate.html

=============

蛮力(有很多 htose 小动物!:

http://projects.jkraemer.net/acts_as_ferret/wiki#UTF-8support

http://snippets.dzone.com/posts/show/2384

于 2008-10-22T12:50:21.417 回答
1

对于任何阅读本文并希望删除所有非 ascii 字符的人来说,可能很有用,我成功地使用了第一个示例。

于 2010-08-29T12:48:51.453 回答
0

我无法让 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) 的作用相同。它是否正确?谢谢!

于 2012-09-19T11:04:07.273 回答
0

如果您使用 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,也许您可​​以使用这个简单的解决方案,但我还没有测试过。

于 2018-12-08T21:06:55.850 回答
-3

大声笑..我刚试过这个..它正在工作..我仍然不太清楚为什么..但是当我使用这4行代码时:

  • str = str.gsub(/[^a-zA-Z0-9 ]/,"")
  • str = str.gsub(/[ ]+/," ")
  • str = str.gsub(/ /,"-")
  • str = str.downcase

它会自动从文件名中删除任何重音..我试图删除(文件名中的重音并重命名它们)希望它有所帮助:)

于 2009-03-29T08:02:25.407 回答