2

我正在使用 URI.unescape转义文本,不幸的是我遇到了奇怪的错误:

 # encoding: utf-8
 require('uri')
 URI.unescape("%C3%9Fą")

结果是

 C:/Ruby193/lib/ruby/1.9.1/uri/common.rb:331:in `gsub': incompatible character encodings: ASCII-8BIT and UTF-8 (Encoding::CompatibilityError)
    from C:/Ruby193/lib/ruby/1.9.1/uri/common.rb:331:in `unescape'
    from C:/Ruby193/lib/ruby/1.9.1/uri/common.rb:649:in `unescape'
    from exe/fail.rb:3:in `<main>'

为什么?

4

3 回答 3

9

不知道为什么,但你可以使用CGI.unescape方法:

# encoding: utf-8
require 'cgi'
CGI.unescape("%C3%9Fą")
于 2012-04-26T06:16:49.517 回答
5

URI.unescape非 ASCII 输入的实现被破坏。1.9.3 版本如下所示:

def unescape(str, escaped = @regexp[:ESCAPED])
  str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(str.encoding)
end

使用的正则表达式是/%[a-fA-F\d]{2}/. 所以它通过字符串寻找一个百分号后跟两个十六进制数字;块$&中将是匹配的文本(例如'%C3')并且$&[1,2]是没有前导百分号('C3')的匹配文本。然后我们调用String#hex将该十六进制数转换为 Fixnum ( 195) 并将其包装在 Array ( [195]) 中,以便我们可以使用它Array#pack来为我们进行字节修饰。问题是这pack给了我们一个二进制字节:

> puts [195].pack('C').encoding
ASCII-8BIT

ASCII-8BIT 编码也称为“二进制”(即没有特定编码的纯字节)。然后该块返回该字节并String#gsub尝试插入正在处理的 UTF-8 编码副本中strgsub您会得到错误:

不兼容的字符编码:ASCII-8BIT 和 UTF-8 (Encoding::CompatibilityError)

因为您不能(通常)将二进制字节填充到 UTF-8 字符串中;你经常可以侥幸逃脱:

URI.unescape("%C3%9F")         # Works
URI.unescape("%C3µ")           # Fails
URI.unescape("µ")              # Works, but nothing to gsub here
URI.unescape("%C3%9Fµ")        # Fails
URI.unescape("%C3%9Fpancakes") # Works

一旦您开始将非 ASCII 数据混合到您的 URL 编码字符串中,事情就会开始分崩离析。

一个简单的解决方法是在尝试解码之前将字符串切换为二进制:

def unescape(str, escaped = @regexp[:ESCAPED])
  encoding = str.encoding
  str = str.dup.force_encoding('binary')
  str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(encoding)
end

另一种选择是将 推force_encoding入块中:

def unescape(str, escaped = @regexp[:ESCAPED])
  str.gsub(escaped) { [$&[1, 2].hex].pack('C').force_encoding(encoding) }
end

我不确定为什么gsub在某些情况下失败但在其他情况下成功。

于 2012-04-26T21:17:56.797 回答
0

为了扩展 Vasiliy 建议使用的答案CGI.unescape

从 Ruby 2.5.0 开始,URI.unescape已过时。

请参阅https://ruby-doc.org/stdlib-2.5.0/libdoc/uri/rdoc/URI/Escape.html#method-i-unescape

“此方法已过时,不应使用。取而代之的是,根据您的具体用例使用 CGI.unescape、URI.decode_www_form 或 URI.decode_www_form_component。”

于 2019-03-08T01:27:18.283 回答