瞎扯淡 通过研究 open-uri 源码,终于搞定编码

u4crella · 2020年05月09日 · 4272 次阅读

下载一个 gbk 编码的 xml,用 openuri 默认方式:text=URI.open(link, header).read,得出来的编码是 ascii-8bit。用以下代码转成 utf-8,偶尔提示:ascii-8bit 不能转换成 gbk: "\x???\x???"(???是具体的字符)。但是先把 text 写入到文件,然后再以 File.open(filename, 'r:gbk') 方式读取文件,又能读取到 gbk 文本。这让我郁闷不已。

encode('utf-8', 'gbk', {:invalid => :replace, :undef => :replace, :replace => '?'})

openuri 的文档也没讲怎么怎么指定编码。偶尔发现 URI.open(link, header).class=StringIO,也就是说 openuri 可能 不是先下载二进制流,然后才转成文本的。所以我全盘搜索 openuri,找到 openuri.rb。从 URI.open 跳到 Kernel.open,里面有个 uri.buffer。我知道 OpenURI 是扩展了 URI 模块,所以直接跳到 OpenURI.open_uri,发现第 148 行开始:

if /\Arb?(?:\Z|:([^:]+))/ =~ mode
  encoding, = $1,Encoding.find($1) if $1
  mode = nil
end
if options.has_key? :encoding
  if !encoding.nil?
    raise ArgumentError, "encoding specified twice"
  end
  encoding = Encoding.find(options[:encoding])
end

结合上下文,发现这个 options 就是 header,只不过我一开始把 URI.open() 的第二参数理解为发送给服务器的 header,却没想到 OpenURI 还从这个第二参数获取指定编码的值。于是我就在 http 的 header 里指定:

header[:encoding]='gbk'

然后重试,终于成功了。OpenURI.open(link, header).read 返回的是 gbk 文本,然后再用开头提到的 encode() 函数,转成了 utf-8,然后不管是在内存里比较还是先写入文件再比较,都能得到正确的结果。

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