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

时间:2016-05-06 19:17:32

标签: ruby regex mongodb ruby-on-rails-5

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

我当前的代码类似于

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

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

我现在正以法语为目标,但如果我还能将其修复为其他语言,那将很酷。

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

我意识到我的要求实际上有点强,我还需要捕捉破折号等等。我基本上是导入学校数据库(URL here,标签是<nom>),我希望人们能够通过输入其名称来找到他们的学校。搜索查询和搜索请求都可能包含重音,我认为最简单的方法是使“两者”不敏感。

  • “Télécom”应与“Telecom”相匹配
  • “établissement”应与“etablissement”匹配
  • “国家艺术学院 - 摩泽尔艺术学院和艺术学院”应与“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&amp; Tourism Management Academy”应该与“Hotel Tourism”相匹配(注意&amp;实际上是在{XML中写成&amp;

1 个答案:

答案 0 :(得分:2)

看起来MongoDB的解决方案是使用text indexdiacritic insensitive。法语是supported

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

index(name: "text")

...然后像这样搜索:

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

有关详细信息,请参阅$text query operator的文档。

原始(错误)回答

  

事实证明我正在考虑向后的问题,并且最初写了这个答案。我保留它,因为它可能仍然派上用场。如果您使用的数据库没有提供这种功能(例如,似乎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的拉丁文小写字母E.在I18n.transliterate(前者)上拨打会产生e?,这可能不是您想要的,因此normalize之前的transliterate步骤非常重要。 (如果您对其工作方式感兴趣,请阅读Unicode equivalence and normalization。)