如何在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

我需要填写"something here"来替换重音字符。有什么比更好的吗

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

而且,由于我不使用Ruby1.9,所以我不能在代码中放入那些Unicode文字。实际的正则表达式看起来会更难看。

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

示例:

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

Rails已经有了一个用于规范化的内置程序,您只需要使用它将字符串规范化为KD,然后删除其他字符(即重音标记),如下所示:

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

更好的方法是使用I18n:

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

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

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

一直是这样:

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

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

你必须稍微修改一下字符列表,以尊重"ñ"字符,但这是一项简单的工作。

我的答案:String#参数化方法:

"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--314xC2àáâã3D".parameterize
# => "a-s-3-3d"

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

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

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

# coding: utf-8

这里的normalize(:kd)部分在可能的情况下分割出变音符号(例如:"n with tilda"单个字符被分割成一个n,后面跟着一个组合变音tilda字符),然后gsub部分删除所有变音字符。

我想你可能真的不知道该怎么走。如果你正在为一个有这种字母的市场开发,你的用户可能会认为你是一个pip。因为对用户来说,"å"甚至在任何意义上都不接近"a"。走另一条路,以非ascii的方式阅读有关搜索的信息。这只是有人发明了unicode和排序规则的案例之一。

非常晚的PS

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

除此之外,我无法将整理链接转到msdn页面,但我将其留在了那里。应该是http://www.unicode.org/reports/tr10/

这假设您使用Rails

"anything".parameterize.underscore.humanize.downcase

考虑到您的需求,我可能会这么做……我认为它简洁、简单,并且在未来的Rails和Ruby版本中保持最新。

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

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

请参阅http://www.siao2.com/2005/02/19/376617.aspx和http://www.siao2.com/2007/05/14/2629747.aspx详细信息。

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

要获得Ruby 1.8源文件中的规范文本字符,请执行以下操作:

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

您可能想要Unicode分解("NFD")。分解字符串后,只需过滤掉[A-Za-z]中没有的任何内容。æ将分解为"ae",ã为"a~"(近似-二临界将成为一个单独的字符),因此滤波留下了一个合理的近似值。

图标:

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

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

一个我无法理解的perl模块:

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

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

蛮力(有很多这种小动物!:

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

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

我在获取foo.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.toos解决方案时遇到问题。我没有使用Rails,与我的activesupport/ruby版本有一些冲突,我无法深入了解。

使用ruby unf gem似乎是一个很好的替代品:

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

据我所知,这与.mb_chars.normalize(:kd)的作用相同。这正确吗?谢谢

如果你使用PostgreSQL=>9.4作为你的数据库适配器,也许你可以在迁移中添加一个"未使用"的扩展,我认为它可以满足你的需求,比如:

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

lol。。我刚试过这个。。它正在发挥作用。。我还是不太清楚为什么。。但当我使用这4行代码时:

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

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

相关内容

  • 没有找到相关文章

最新更新