Ruby超级不敏感的Regex,用于将学校名称与重音和其他变音符号相匹配

在其他编程语言中已经提出了这个问题,但是如何在Ruby上执行重音不敏感的正则表达式呢?

我目前的代码是这样的

scope :by_registered_name, ->(regex){ where(:name => /#{Regexp.escape(regex)}/i) } 

我想也许我可以用点替换非字母数字+空格字符,并删除escape ,但是没有更好的方法吗? 如果我这样做,恐怕我能抓到奇怪的东西……

我现在正在瞄准法语,但是如果我还可以将其修复为其他很酷的语言。

我正在使用Ruby 2.3,如果这可以帮助。


我意识到我的要求实际上有点强,我还需要捕捉像破折号等等。我基本上是导入一个学校数据库( URL在这里 ,标签是 ),我希望人们能够找到他们的学校输入名字。 搜索查询和搜索请求都可能包含重音,我相信最简单的方法是使“两者”不敏感。

  • “Télécom”应与“电信”相匹配
  • “établissement”应与“etablissement”相匹配
  • “Institutsenérieurnationalde l’artisanat – Chambredemétiersetde l’Artisanat en Moselle”应与“artisanat chambredemétiers”相匹配
  • “Ecolehôtelièred’Avignon(CCI du Vaucluse)”应与Ecole hoteliere d’avignon相匹配“(对于括号,可以跳过它)
  • “Ecolefrançaised’hôtesses”应与“ecole francaise d’hot”相匹配

我在那个数据库中找到了疯狂的东西,我会考虑对这个输入进行消毒

  • “Académieinternationalede management – Hotel&Tourism Management Academy”应该与“Hotel Tourism”相匹配(请注意&实际上是用XML编写的)

看起来MongoDB的解决方案是使用text索引 ,这是变音符号不敏感的 。 支持法语。

自从我上次使用MongoDB以来已经很长时间了,但是如果你使用的是Mongoid,我想你会在模型中创建一个text索引,如下所示:

 index(name: "text") 

…然后像这样搜索:

 scope :by_registered_name, ->(str) { where(:$text => { :$search => str }) } 

有关更多信息,请参阅$text查询运算符的文档。

原(错)答案

事实certificate我正在考虑倒退的问题,并且最初写了这个答案。 我保留它,因为它可能仍然派上用场。 如果您使用的数据库不提供此类function(例如,似乎MongoDB确实如此),可能的解决方法是使用以下技术将已清理的名称与原始名称一起存储在数据库中,并且然后同样清理查询。

由于您使用的是Rails,因此您可以使用方便的ActiveSupport::Inflector.transliterate

 regex = /aäoöuü/ transliterated = ActiveSupport::Inflector.transliterate(regex.source, '\?') # => "aaoouu" new_regex = Regexp.new(transliterated) # => /aaoouu/ 

或者干脆:

 Regexp.new(ActiveSupport::Inflector.transliterate(regex.source, '\?')) 

你会注意到我提供'\?' 作为第二个参数,它是替换任何无效的UTF-8字符的替换字符串。 这是因为默认替换字符串是"?" ,你知道它在正则表达式中有特殊含义。

另请注意, ActiveSupport::Inflector.transliterate比类似的I18n.transliterate稍微多一点。 这是它的来源:

 def transliterate(string, replacement = "?") I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize( ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c), :replacement => replacement) end 

最内层的方法调用ActiveSupport::Multibyte::Unicode.tidy_bytes清除任何无效的UTF-8字符。

更重要的是, ActiveSupport::Multibyte::Unicode.normalize “规范化”字符。 例如, 看起来像一个字符,但它实际上是两个:拉丁文小写字母E和组合CIRCUMFLEX ACCENT。 调用I18n.transliterate("ê")会产生e? ,这可能不是你想要的,所以normalize被称为把变成ê ,这只是一个字符:带有CIRCUMFLEX的LATIN SMALL LETTER E。 在 (前者)上调用I18n.transliterate会产生e? ,这可能不是你想要的,所以在transliterate之前normalize步骤很重要。 (如果您对它的工作方式感兴趣,请阅读有关Unicode等效和规范化的内容 。)