当正则表达式中的umlaute(äöü)具有不同的字节表示形式时,该如何匹配它们?

时间:2019-12-12 15:59:44

标签: ruby-on-rails regex ruby

所以我有一个堰问题。我在S3上有文件,我想使用正则表达式将单词与文件名进行匹配。正则表达式应该明确匹配,但是不匹配:

irb(main):048:0> p.original_filename
=> "Küche.png"
irb(main):049:0> "Küche.png"
=> "Küche.png"
irb(main):013:0> reg = /(\A|\W|\d|_)#{Regexp.quote("Küche")}(\W|\z|\d|_)/i
=> /(\A|\W|\d|_)Küche(\W|\z|\d|_)/i
irb(main):014:0> reg.match?("Küche.png")
=> true
irb(main):015:0> reg.match?(p.original_filename)
=> false
irb(main):050:0> p.original_filename == "Küche.png"
=> false

所以我进一步检查并假设存在编码问题:

irb(main):017:0> p.original_filename.encoding
=> #<Encoding:UTF-8>
irb(main):018:0> "Küche.png".encoding
=> #<Encoding:UTF-8>

那很奇怪。但是,让我们看看表示形式后面是什么字符和字节:

irb(main):025:0> "Küche.png".chars
=> ["K", "ü", "c", "h", "e", ".", "p", "n", "g"]
irb(main):026:0> p.original_filename.chars
=> ["K", "u", "̈", "c", "h", "e", ".", "p", "n", "g"]
irb(main):032:0> p.original_filename.bytes
=> [75, 117, 204, 136, 99, 104, 101, 46, 112, 110, 103]
irb(main):033:0> "Küche.png".bytes
=> [75, 195, 188, 99, 104, 101, 46, 112, 110, 103]

这就是问题所在。我的问题:如何标准化文件名,使其与regexp /(\A|\W|\d|_)#{Regexp.quote("Küche")}(\W|\z|\d|_)/i匹配?由于字节不同,我尝试了force_encodingencode都没有成功。

注意:不能仅对文件名使用ASCII字符。这必须与变音符号配合使用。

1 个答案:

答案 0 :(得分:3)

在Unicode中,某些字符可以表示为基本字符和组合字符:

"\u0075\u0308" # LATIN SMALL LETTER U (U+0075) + COMBINING DIAERESIS (U+0308)
#=> "ü"

或作为单个组成的字符:

"\u00fc"       # LATIN SMALL LETTER U WITH DIAERESIS (U+00FC)
#=> "ü"

如果您在键盘上键入字符,通常会生成后者。

将给定字符串转换为这些表示形式之一的过程称为 normalization

在Ruby中,String#unicode_normalize的默认格式为后者:

a = "\u0075\u0308"
b = "\u00fc"

a.codepoints                   #=> [117, 776]
b.codepoints                   #=> [252]
a.unicode_normalize.codepoints #=> [252]

在您的示例中,您将使用:

reg.match?(p.original_filename.unicode_normalize)