Ruby super 不敏感的正则表达式,用于将学校名称与重音符号和其他变音符号匹配

标签 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”匹配
  • “Institut supérieur national de l'artisanat - Chambre de métiers et de l'Artisanat en Moselle”应与“artisanat chambre de métiers”相匹配
  • “Ecole hôtelière d'Avignon (CCI du Vaucluse)”应与 Ecole hoteliere d'avignon 匹配(对于括号可以跳过它)
  • “Ecole française d'hôtesses”应与“ecole francaise d'hot”匹配

我在那个数据库中发现了一些疯狂的东西,我会考虑清理我认为的这个输入

  • “Académie internationale de management - Hotel & Tourism Management Academy”应与“Hotel Tourism”匹配(注意 & 实际上在 XML 中写为 &amp;)

最佳答案

看起来 MongoDB 的解决方案是使用 text index ,即 diacritic insensitive .法语是supported .

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

index(name: "text")

...然后像这样搜索:

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

查阅 $text query operator 的文档获取更多信息。

原始(错误)答案

As it turns out I was thinking about the question backwards, and wrote this answer initially. I'm preserving it since it might still come in handy. If you were using a database that didn't offer this kind of functionality (like, it seems, MongoDB does), a possible workaround would be to use the following technique to store a sanitized name along with the original name in the database, and then likewise sanitize queries.

由于您使用的是 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 和 COMBINING CIRCUMFLEX ACCENT。调用 I18n.transliterate("ê") 会产生 e?,这可能不是您想要的,因此调用 normalize 以转ê 转换为 ê,这只是一个字符:带有 CIRCUMFLEX 的拉丁文小写字母 E。在 ê(前者)上调用 I18n.transliterate 会产生 e?,这可能不是您想要的,因此 transliterate 之前的 normalize 步骤很重要。 (如果您对其工作原理感兴趣,请阅读 Unicode equivalence and normalization。)

关于Ruby super 不敏感的正则表达式,用于将学校名称与重音符号和其他变音符号匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37079888/

相关文章:

ruby - Ruby 中的字符串连接

ruby : 的用法?在 if 条件下

c++ - 使用 QRegularExpression 提取双引号内的字符串

ios - 解析迁移噩梦语句前缺少分号

mongodb - 国外收藏的查找和排序

mongodb - 如何确保 mongodb 地理空间坐标中的小数精度?

java - 有没有办法在 JRuby 中为 Java 对象获得良好的 `#inspect` 输出?

ruby-on-rails - 如何使用大量可选 URL 参数在 Rails 3.x 中构建 SEO url?

regex - 用正则表达式替换csv中的千位分隔符

regex - 在 bash 中匹配正则表达式