2012-04-26 42 views
2

我使用URI.unescape反轉義文字,不幸的是我遇到奇怪的錯誤:在URI.unescape崩潰,因爲它是試圖轉換「%C3%9FA」到「SSA」

# 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>' 

爲什麼?

回答

5

URI.unescape的實施打破了非ASCII輸入。該1.9.3 version看起來是這樣的:

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試圖插入的strgsub的UTF-8編碼的複製工作,你會得到你的錯誤:

incompatible character encodings: ASCII-8BIT and 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在某些情況下失敗,但在其他情況下成功。

+0

http://bugs.ruby-lang.org/ - 它是一個正確的地方報告標準ruby庫中的錯誤? – Bulwersator 2012-04-28 13:17:08

+0

@Bulwersator:http://bugs.ruby-lang.org/projects/ruby/wiki/HowtoReport – 2012-04-28 18:15:16

8

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

# encoding: utf-8 
require 'cgi' 
CGI.unescape("%C3%9Fą") 
+0

呃,下一個問題 - 是否有可能阻止它轉換爲UTF-8中的無效字符(它將「%EA%E6」轉換爲\ xEA \ xE6字符,導致gsub崩潰,UTF-8中的無效字節序列「) – Bulwersator 2012-04-26 08:45:01

+0

我在ruby腳本中用CGI.escape代替了URI.escape,它幫助thx – daniel 2013-10-22 18:06:20

相關問題