Ruby 1.9.3 Dir.glob没有返回NFC UTF-8字符串,而是返回NFD

时间:2012-11-02 10:21:18

标签: ruby

从Ruby 1.9.3读取文件名时,我看到一些奇怪的结果。例如,使用以下测试ruby脚本,在包含名为“Testé.txt”的文件的文件夹中运行

#!encoding:UTF-8
def inspect_string s
    puts "Source encoding: #{"".encoding}"
    puts "External encoding: #{Encoding.default_external}"
    puts "Name: #{s.inspect}"
    puts "Encoding: #{s.encoding}"
    puts "Chars: #{s.chars.to_a.inspect}"
    puts "Codepoints: #{s.codepoints.to_a.inspect}"
    puts "Bytes: #{s.bytes.to_a.inspect}"
end

def transform_string s
   puts "Testing string #{s}"
   puts s.gsub(/é/u,'TEST')
end

Dir.glob("./*.txt").each do |f|  

   puts RUBY_VERSION + RUBY_PLATFORM

   puts "Inline string works as expected" 
   s = "./Testé.txt" 
   inspect_string s
   puts transform_string s

   puts "File name from Dir.glob does not" 
   inspect_string f
   puts transform_string f

end

在Mac OS X Lion上,我看到以下结果:

1.9.3x86_64-darwin11.4.0
Inline string works as expected
Source encoding: UTF-8
External encoding: UTF-8
Name: "./Testé.txt"
Encoding: UTF-8
Chars: [".", "/", "T", "e", "s", "t", "é", ".", "t", "x", "t"]
Codepoints: [46, 47, 84, 101, 115, 116, 233, 46, 116, 120, 116]
Bytes: [46, 47, 84, 101, 115, 116, 195, 169, 46, 116, 120, 116]
Testing string ./Testé.txt
./TestTEST.txt

File name from Dir.glob does not
Source encoding: UTF-8
External encoding: UTF-8
Name: "./Testé.txt"
Encoding: UTF-8
Chars: [".", "/", "T", "e", "s", "t", "e", "́", ".", "t", "x", "t"]
Codepoints: [46, 47, 84, 101, 115, 116, 101, 769, 46, 116, 120, 116]
Bytes: [46, 47, 84, 101, 115, 116, 101, 204, 129, 46, 116, 120, 116]
Testing string ./Testé.txt
./Testé.txt

预期的最后一行是

./TestTEST.txt

返回的编码表明这是一个普通的UTF-8字符串,但是没有正确应用涉及unicode的任何regexp转换。

2 个答案:

答案 0 :(得分:3)

对此的更新:Ruby 2.2.0获得了String#unicode_normalize

f.unicode_normalize!

将转换从OSX返回的NFD分解字符串' HFS +文件系统变成了NFC组成的字符串。如果您需要其他规范化,则可以指定:nfd:nfkc:nfkd

答案 1 :(得分:0)

张贴以防万一这对其他任何人都有用:

如果您使用UTF-8编码,Ruby 1.9和2.0将使用组合的UTF-8字符串,但不会修改从OS接收的字符串。 Mac OS X使用分解的字符串(两个字节用于许多常见的重音符号,如UTF-8中的é,它们被组合用于显示)。因此,文件系统方法通常会返回意外的字符串格式,这些格式严格为UTF-8,但是是分解形式。

为了解决这个问题,你需要通过从'UTF8-MAC'编码转换为UTF-8来分解它们:

f.encode!('UTF-8','UTF8-MAC')

在使用它们之前,否则您最终可能会对使用组合的本机ruby字符串的已分解字符串运行检查。

此行为会影响所有文件系统调用,例如文件名为包含unicode字符的文件和文件夹。

Apple docs:

http://developer.apple.com/library/mac/#qa/qa1235/_index.html