Ruby 字符串的 “split” 编码问题

haiya2016 · 2016年11月26日 · 最后由 haiya2016 回复于 2016年11月28日 · 2094 次阅读

我是一个 ruby 新手,在用 ruby 练习写一个脚本对文件夹下面的文件进行重命名的时候,控制台用的 Windows PowerShell,提示下面这个错误:

T1.rb:8:in `split': invalid byte sequence in UTF-8 (ArgumentError)
        from T1.rb:8:in `block in change_filename'
        from T1.rb:4:in `foreach'
        from T1.rb:4:in `change_filename'
        from T1.rb:19:in `<main>'

代码如下:

#encoding: UTF-8
def change_filename(dirpath)
    if File.directory? dirpath       #判断路径是否存在
        Dir.foreach(dirpath) do |file|       #对每个文件执行块
            if file != "." and file != ".."          #排除当前目录和上级目录
                file_name = file.force_encoding('UTF-8')     #文件名转码
                puts file_name.encoding         #打印转码后的文件名的编码
                temp = file_name.split("_")      #用下划线分割字符串
                puts "你好".encoding               #打印字符串的编码
                temp[1] = "你好".force_encoding('UTF-8')
                file_rename = temp.join("_")       #修改部分内容后重新组合文件名
                puts temp
            end
            # File.rename(file_name,file_rename)
        end
    end
end

change_filename('F:\Desktop\Temp\2016\10')

控制台只输出了一个:UTF-8 就报上面那个错误了

奇怪的是,我单独执行循环里面的块,却没有报错。 单独执行的代码如下:

file_name = "王尼玛组_L王尼玛_2016-10-01-2016-10-31.xlsx"
puts file_name.encoding
temp = file_name.split("_")
puts "你好".encoding
temp[1] = "你好".force_encoding('UTF-8')
file_rename = temp.join("_")
puts file_rename

请教下各位大大,这是什么原因呢?

Ruby 的编码分 external encoding, internal encoding。三言两语也说不清楚,你看官方文档吧:https://docs.ruby-lang.org/en/2.3.0/Encoding.html

本例中,Windows 系统默认编码是 GBK,因此 external encoding 默认即是 GBK;internal encoding 默认是 nil,即不转码外部读取的信息。

External encoding 和 internal encoding 共同作用,因此 Dir.foreach 获取到的文件名是 GBK 编码,你用了 force_encoding "转码" 成 UTF-8,然而事实上 force_encoding 并不会对数据进行转码,只是尝试以新的编码解读同样的二进制序列。GBK 中合法的字节序列可能在 UTF-8 中并不合法,因此就出错了。

正确的转码方式是 file_name = file.encode('utf-8')

另外,测试中发现个奇怪的地方,Dir['*'] 获取到的文件名是 UTF-8 编码的。

#1 楼 @bianjp 哇,第一次提问就遇到大神这么详细的解答,问题解决了,非常感谢!external encoding, internal encoding 的区别我在研究下官方文档看看,感谢你的指路😆

需要 登录 后方可回复, 如果你还没有账号请 注册新账号