其他编程语言已经提出了这个问题,但是如何在Ruby上执行重音不敏感的正则表达式呢?
我当前的代码类似于
scope :by_registered_name, ->(regex){
where(:name => /#{Regexp.escape(regex)}/i)
}
我想也许我可以用点替换非字母数字+空白字符,并删除escape
,但是没有更好的方法吗?如果我这样做,我恐怕会抓到奇怪的东西......
我现在正以法语为目标,但如果我还能将其修复为其他语言,那将很酷。
如果可以提供帮助,我正在使用Ruby 2.3。
我意识到我的要求实际上有点强,我还需要捕捉破折号等等。我基本上是导入学校数据库(URL here,标签是<nom>
),我希望人们能够通过输入其名称来找到他们的学校。搜索查询和搜索请求都可能包含重音,我认为最简单的方法是使“两者”不敏感。
我在那个数据库中找到了疯狂的东西,我会考虑清理这个输入,我认为
&
)答案 0 :(得分:2)
看起来MongoDB的解决方案是使用text
index,diacritic 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。)