我有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文字放在我的代码中。实际的正则表达式看起来会更加丑陋。
答案 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)
我尝试了很多方法,但他们没有达到这些要求中的一个或几个:
一直是这样:
# coding: utf-8
string.tr(
"ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž",
"AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz"
)
你必须修改一下角色列表以尊重'ñ'字符,但这是一件容易的事。
答案 4 :(得分:10)
我的回答: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"
答案 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.aspx和http://www.siao2.com/2007/05/14/2629747.aspx。
答案 9 :(得分:3)
关键是在数据库中使用两列:canonical_text
和original_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)
的iconv:
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-8support
答案 12 :(得分:1)
对于任何想要删除所有非ascii字符this的人来说,这可能是有用的,我成功地使用了第一个例子。
答案 13 :(得分:0)
我在使用foo.mb_chars.normalize(:kd).gsub(/ [^ \ x00- \ x7F] / n,'')。downcase.to_s解决方案时遇到了问题。我没有使用Rails,并且与我的activesupport / ruby版本存在一些冲突,我无法深入到底。 p>
使用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行代码时:
它会自动删除文件名中的任何重音..我试图删除它(从文件名重音并重命名)希望它有所帮助:)