2013-02-11 120 views
29

我有一些需要非轉義的轉義字符串。我想用Python做到這一點。我如何在Python3中使用.decode('string-escape')?

例如,在python2.7我可以這樣做:

>>> "\123omething special".decode('string-escape') 
'Something special' 
>>> 

如何做到在Python3?這不起作用:

>>> b"\123omething special".decode('string-escape') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
LookupError: unknown encoding: string-escape 
>>> 

我的目標是成爲阿貝爾採取這樣的字符串:

s\000u\000p\000p\000o\000r\000t\[email protected]\000p\000s\000i\000l\000o\000c\000.\000c\000o\000m\000 

,把它變成:

"[email protected]" 

我做轉換後,我會探測一下,看看我的字符串是用UTF-8還是UTF-16編碼的。

+1

試試這個:'字節( 「\ 123omething特」, 「UTF-8」)解碼( 「unicode_escape」)' – 2013-02-11 20:41:07

+0

你是絕對肯定的是逃逸,而不是字面字節? – 2013-02-11 20:47:05

+0

它們是字面字節!有一個反斜槓,然後是0,然後是0,然後是第三個0 ...我有一個程序讀取二進制文件並輸出這樣的信息。它輸出實際在文件中的二進制文件。有時文件的內容是UTF-8編碼的,它只是通過。但是如果它不是有效的UTF-8,它就會以這種方式進行編碼。 – vy32 2013-02-11 20:48:18

回答

28

你將不得不使用unicode_escape代替:

>>> b"\\123omething special".decode('unicode_escape') 

如果str對象開始代替(相當於Python 2.7版的Unicode),你需要先編碼爲字節,然後解碼與unicode_escape

如果需要字節最終的結果,就必須再次進行編碼,以一個合適的編碼方式(.encode('latin1')例如,如果需要保留字面字節值;前255個Unicode碼位映射1對1) 。

你的例子實際上是帶有轉義的UTF-16數據。從unicode_escape,回到latin1解碼保存字節,然後從utf-16-le(UTF-16小端無BOM):

>>> value = b's\\000u\\000p\\000p\\000o\\000r\\000t\\[email protected]\\000p\\000s\\000i\\000l\\000o\\000c\\000.\\000c\\000o\\000m\\000' 
>>> value.decode('unicode_escape').encode('latin1') # convert to bytes 
b's\x00u\x00p\x00p\x00o\x00r\x00t\[email protected]\x00p\x00s\x00i\x00l\x00o\x00c\x00.\x00c\x00o\x00m\x00' 
>>> _.decode('utf-16-le') # decode from UTF-16-LE 
'[email protected]' 
+0

這將我的二進制對象變成一個Unicode對象。我想保留它一個二進制對象。任何方式來做到這一點? – vy32 2013-02-11 20:42:37

+0

@ vy32:解碼後進行編碼?你期望這個編碼適合什麼編碼? ASCII,拉丁語1? – 2013-02-11 20:44:30

+0

它可能是任何東西。該程序探測各種可能的編碼。它可能是ASCII,UTF-8,UTF-16,Latin 1或其他十幾種可能性。 – vy32 2013-02-11 20:49:19

3

不能使用的字節字符串unicode_escape(或者說,你可以,但它不不會總是返回與Python 2上的string_escape相同的東西) - 小心!

該函數使用正則表達式和自定義替換邏輯實現string_escape

def unescape(text): 
    regex = re.compile(b'\\\\(\\\\|[0-7]{1,3}|x.[0-9a-f]?|[\'"abfnrt]|.|$)') 
    def replace(m): 
     b = m.group(1) 
     if len(b) == 0: 
      raise ValueError("Invalid character escape: '\\'.") 
     i = b[0] 
     if i == 120: 
      v = int(b[1:], 16) 
     elif 48 <= i <= 55: 
      v = int(b, 8) 
     elif i == 34: return b'"' 
     elif i == 39: return b"'" 
     elif i == 92: return b'\\' 
     elif i == 97: return b'\a' 
     elif i == 98: return b'\b' 
     elif i == 102: return b'\f' 
     elif i == 110: return b'\n' 
     elif i == 114: return b'\r' 
     elif i == 116: return b'\t' 
     else: 
      s = b.decode('ascii') 
      raise UnicodeDecodeError(
       'stringescape', text, m.start(), m.end(), "Invalid escape: %r" % s 
      ) 
     return bytes((v,)) 
    result = regex.sub(replace, text) 
12

老「字符串轉義」解碼器映射字節串到字節串,並有過很多爭論,關於如何處理這樣的編解碼器做的,所以它不是目前可以通過標準的編碼/解碼接口。

但是,代碼仍然存在於C-API中(如PyBytes_En/DecodeEscape),並且這仍然通過未公開的codecs.escape_encodecodecs.escape_decode暴露給Python。

>>> import codecs 
>>> codecs.escape_decode(b"ab\\xff") 
(b'ab\xff', 6) 
>>> codecs.escape_encode(b"ab\xff") 
(b'ab\\xff', 3) 

這些函數返回轉化bytes對象,再加上一個數字,表示了多少字節處理......你可以忽略後者。

>>> value = b's\\000u\\000p\\000p\\000o\\000r\\000t\\[email protected]\\000p\\000s\\000i\\000l\\000o\\000c\\000.\\000c\\000o\\000m\\000' 
>>> codecs.escape_decode(value)[0] 
b's\x00u\x00p\x00p\x00o\x00r\x00t\[email protected]\x00p\x00s\x00i\x00l\x00o\x00c\x00.\x00c\x00o\x00m\x00'