如何在Ruby中替换重音拉丁字符?

时间:2008-10-22 12:03:23

标签: ruby-on-rails ruby activerecord unicode utf-8

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

16 个答案:

答案 0 :(得分:86)

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

示例:

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

答案 1 :(得分:56)

Rails已经内置了规范化,你只需要使用它来规范化你的字符串以形成KD,然后删除其他字符(即重音标记),如下所示:

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

答案 2 :(得分:38)

更好的是使用I18n:

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

答案 3 :(得分:18)

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

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

一直是这样:

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

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

你必须修改一下角色列表以尊重'ñ'字符,但这是一件容易的事。

答案 4 :(得分:10)

我的回答:String#parameterize方法:

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

对于非R​​ails程序:

安装activesupport:gem install activesupport然后:

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

答案 5 :(得分:7)

我认为你可能不会真正走下那条道路。如果您正在开发具有这类信件的市场,您的用户可能会认为您是...... pip 。 因为'å'在用户的任何意义上都不接近'a'。 走另一条路,阅读非ascii方式的搜索。这只是某人发明unicode和collation的案例之一。

非常晚的PS

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

除此之外,我没有ide方式,通往校对的链接转到msdn页面,但我把它留在那里。应该是http://www.unicode.org/reports/tr10/

答案 6 :(得分:6)

分解字符串并从中删除non-spacing marks

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

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

# coding: utf-8

此处的normalize(:kd)部分尽可能地分离出变音符号(例如:“n with tilda”单个字符被分成n后跟一个组合的变音tilda字符),然后是gsub部分删除所有变音字符。

答案 7 :(得分:4)

这假设您使用Rails。

"anything".parameterize.underscore.humanize.downcase

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

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

答案 8 :(得分:3)

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

有关详细信息,请参阅http://www.siao2.com/2005/02/19/376617.aspxhttp://www.siao2.com/2007/05/14/2629747.aspx

答案 9 :(得分:3)

关键是在数据库中使用两列:canonical_textoriginal_text。使用original_text进行显示,使用canonical_text进行搜索。这样,如果用户搜索“Visual Cafe”,她会看到“VisualCafé”结果。如果她真的想要一个名为“Visual Cafe”的不同项目,它可以单独保存。

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

register_replacement([0x008A].pack('U'), 'S')

答案 10 :(得分:2)

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

答案 11 :(得分:1)

答案 12 :(得分:1)

对于任何想要删除所有非ascii字符this的人来说,这可能是有用的,我成功地使用了第一个例子。

答案 13 :(得分: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)相同。它是否正确?谢谢!

答案 14 :(得分:0)

如果您使用的是PostgreSQL => 9.4作为数据库适配器,也许您可​​以在迁移中添加"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数据类型的方法。 Here是关于这两种方法的讨论。还要注意,使用PosgreSQL的lower()函数is not recommended

这将为您节省一些数据库空间,因为您将不再需要cannonical_name字段,并且可能使模型更简单,但需要在每个查询中进行一些额外处理,具体取决于您使用的是iLIKE还是iLIKE。 citext和您的数据集。

如果您使用的是MySQL ,也许可以use this simple solution,但我尚未对其进行测试。

答案 15 :(得分:-3)

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

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

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