安全圈 | 专注于最新网络信息安全讯息新闻

首页

python2編碼之殤續集

作者 eppolito 时间 2020-02-25
all

發表於2017-04-14 | 分類於程式設計之道 | 熱度℃

先說點題外話,在下班去看【速八】的路上發現昨晚知乎上分享的色情資源引發的百度網盤之戰因為違反法律法規被删除了,看來搞科技的果然還是得好好研究科技,研究什麼色情呢?另外補充一句:速八真難看!回歸正題吧,繼之前分析的python2.x編碼問題,再補充點疑難雜症,之前python2編碼分析文章請移步Python編碼之殤,這次補充的內容主要針對string與unicode編碼本身的問題,之前也困擾了我很久,最近凑空研究了下,明白了很多,在此補充分享,歡迎糾錯。

故事是這樣開始的

下午茶時間,某司(司機)扔給了我一個奇怪的字串,說是幫忙轉化成中文,看了看扔過來的這串奇怪字元,原本我是拒絕的,然而還沒等我答覆他便補充了句:已訂好速八,晚上約,並拋了個壞笑的表情(你懂的那種表情),我不明白壞笑是什麼意思,但我猜可能有某種特殊的含義,因為對方畢竟是比特老司機。沒轍,看在睡了幾晚的份上,還是决定好好研究下這串程式碼。

1

a=“\\u8fdd\\u6cd5\\u8fdd\\u89c4”

簡單分析下這串字元,感覺像是unicode編碼的內容,但有覺得少了點啥,於是我便開始了一系列的實驗。我想弄清楚這串到底是什麼東西,首先我對unicode編碼的字串進行了測試,看看其長啥樣。

1

2

3

4

5

6

7

>>> a=u“你好”

>>> a

u'\u4f60\u597d' #(unicode編碼)

>>> print type(a)

<type 'unicode'>

>>> print a

你好

實驗結果表示unicode字串長這樣:u’\u4f60\u597d’,但它實際代表的是中文:你好。至於為什麼輸入a,輸出的是unicode字元內容,而print a輸出的是str格式的中文:你好,原因想必是python中的print語句會自動將unicode字元轉化成str格式。如果您對unicode與string不瞭解,那麼請回到文章開頭,移步之前那篇分析編碼的文章,我想會對您有幫助。竟然知道了unicode字元長啥樣,那麼我們可以排除那個奇怪的字串並不是unicode字串了。為啥呢?很明顯,因為它前面沒有u啊。

看到這裡,您是不是有點迷糊了呢?雖然它前面沒有u(u”\u4f60….”),但是它長得確實很像unicode字元啊。不用著急,接下來讓我來好好介紹下字串變數編碼以及字串內容編碼的差异。

說明:以上兩個概念是我自己臨時取的,不代表官方解釋,如有偏差請諒解

所謂字串變數編碼就是我們平常所說的編碼,比如string、unicode,string又包含utf-8、gbk、gb2312等。判斷管道很簡單,用type函數即可。

1

2

3

4

5

6

>>> a=u“你好”

>>> print type(a)

<type 'unicode'>

>>> a=“你好”

>>> print type(a)

<type 'str'>

我們可以看到,unicode或者string代表的是a這個字串變數的一種編碼格式,跟其內容無關。我們知道定義a=”test”,那麼a是string編碼;反之定義a=u”test”,a便是unicode編碼,那麼我想問:test是什麼編碼的?(這裡問的是test,而不是a)有人會說,test就是一個普通的字串,沒錯它確實是一個字串,它表示a的內容。那麼同理當定義

1

a=“\\u8fdd\\u6cd5\\u8fdd\\u89c4”

時,a本身是str格式的字串,那麼

1

\\u8fdd\\u6cd5\\u8fdd\\u89c4

內容本身呢?沒錯,其內容本身是一個unicode編碼後的字串。好了,還是讓我們做實驗測試吧。

我們先看看被常見的幾種編碼格式編碼後的字串內容:

1

2

3

4

5

6

7

8

9

10

11

12

>>> a=u“你好”.encode(“gbk”)

>>> a

'\xc4\xe3\xba\xc3' #內容為gbk編碼

>>> a=u“你好”.encode(“utf-8”)

>>> a

'\xe4\xbd\xa0\xe5\xa5\xbd' #內容為utf-8編碼

>>> a=u“你好”.encode(“gb2312”)

>>> a

'\xc4\xe3\xba\xc3' #內容為gb2312編碼

>>> a=u“你好”

>>> a

u'\u4f60\u597d' #內容為unicode編碼

請注意以上幾種編碼的內容,觀察其特點,然後我們再來看下那個奇怪的字串。

1

2

3

4

5

6

7

8

>>> a=“\\u8fdd\\u6cd5\\u8fdd\\u89c4”

>>> a

'\\u8fdd\\u6cd5\\u8fdd\\u89c4'

>>> print type(a)

<type 'str'>

>>> print a

\u8fdd\u6cd5\u8fdd\u89c4

>>>

我們看到變數a是string格式的。

1

2

3

4

5

>>> a=u“\\u8fdd\\u6cd5\\u8fdd\\u89c4”#在前面加個u,將變數a變成unicode

>>> print type(a)

<type 'unicode'>

>>> print a #相當於a.encode(“utf-8”)

\u8fdd\u6cd5\u8fdd\u89c4

我們在變數””前面加個u,表示變數a為unicode字串,其內容為

1

\\u8fdd\\u6cd5\\u8fdd\\u89c4

接下print a,發現跟上一步的結果一樣,沒錯,因為print將a從unicode變成了string,而其內容看上去少了一些斜杠。

1

2

3

4

5

6

>>> b=u“\u8fdd\u6cd5\u8fdd\u89c4”

>>> print type(b)

<type 'unicode'>

>>> print b

違法違規

>>>

緊接著,我將a的內容,也就是\u8fdd\u6cd5\u8fdd\u89c4,重新賦值給變數b,此時””也加個u,讓其成為unicode格式,然後print b,神奇的一幕發生了,輸出的結果竟然轉化成中文了。其原因我想是,print語句不僅會將字串變數a轉為成string,也會將其內容轉化為string。

1

2

3

4

5

6

>>> a=“你好”

>>> a

'\xc4\xe3\xba\xc3'

>>> a=u“\xc4\xe3\xba\xc3”

>>> print a

ÄãºÃ

以上例子定義變數a為unicode編碼,而其內容為string-utf-8編碼,此時當print a時,print語句嘗試將a的內容轉化為string,但由於其本身就是string編碼,囙此出現了亂碼,反之是可以的。

1

2

3

4

5

6

7

8

>>> a=“你好”

>>> a

'\xc4\xe3\xba\xc3'

>>> b=“\xc4\xe3\xba\xc3”

>>> b.decode(“gbk”)

u'\u4f60\u597d'

>>> print b.decode(“gbk”)

你好

看到這您可能會覺得奇怪,我們定義變數a的內容是這樣的\u8fdd\u6cd5\u8fdd\u89c4,而那個奇怪的字串是這樣的

1

\\u8fdd\\u6cd5\\u8fdd\\u89c4

好像多了一些斜杠,錶急,看完以下這個測試,您就能明白兩者的區別。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

>>> b=“\\xc4\\xe3\\xba\\xc3”

>>> b.decode(“gbk”)

u'\\xc4\\xe3\\xba\\xc3'

>>> print b.decode(“gbk”)

\xc4\xe3\xba\xc3

>>> c=“\xc4\xe3\xba\xc3”

>>> print c.decode(“gbk”)

你好

#################

>>> a=u“\\u8fdd\\u6cd5\\u8fdd\\u89c4”

>>> print a

\u8fdd\u6cd5\u8fdd\u89c4

>>> b=u“\u8fdd\u6cd5\u8fdd\u89c4”

>>> print b

違法違規

>>>

簡單來說,那個奇怪的字串是經過2次unicode編碼後的內容。

內寘函數使用

當然讓其轉化為中文可以借助一個內寘的函數,我之所以分佈演示,是想更清楚得展示其具體含義。將unicode編碼的內容轉化為中文(注意是內容,而不是字串變數)

1

2

3

a=“\\u8fdd\\u6cd5\\u8fdd\u89c4”#變數a的內容為unicode編碼,變數a為string編碼(“”前不要加u)

b=a.decode('unicode-escape')

print b

將string編碼的內容轉化為中文(注意是內容,而不是字串變數)

1

2

3

a=“\\xe5\\x85\\xb3\\xe4\\xba\\x8e\\xe4”#變數a的內容為string編碼,變數a為string編碼(“”前不要加u)

b=a.decode('string-escape')

print b

unicode-escape與utf-8的區別

補充於2017年4月27日

1

2

3

4

5

6

7

8

>>>a=“\u4e0a\u4f20\u6210\u529f”

>>>b=a.decode('utf-8')

>>>print type(b)

<type 'unicode'>

>>>b

u'\\u4e0a\\u4f20\\u6210\\u529f'

>>>print b

\u4e0a\u4f20\u6210\u529f

當對變數a做decode(‘utf-8’)時,除了對把變數a的類型從str變成了unicode,a變數的內容也做了utf-8解碼,所以多了一些斜杠。

1

2

3

4

5

6

7

8

>>>a=“\u4e0a\u4f20\u6210\u529f”

>>>c=a.decode(“unicode-escape”)

>>>print type(c)

<type 'unicode'>

>>>c

u'\u4e0a\u4f20\u6210\u529f'

>>>print c

上傳成功

而對變數a做decode(‘unicode-escape’)時,貌似只有變數本身被decode成unicode了,其內容沒有發生改變。

我們知道print函數會將變數以及變數內容都encode成str,囙此第二個例子能輸出中文,而第一個例子輸出的還是unicode類型的內容,只不過少了一些斜杠,因為它還需要再encode一次。當然本例子的轉化,有更簡單的方法,如下:

1

2

3

>>> d=u“\u4e0a\u4f20\u6210\u529f”#定義變數d時,前面加個u,將其變成unicode

>>> print d

上傳成功

開了一輪飛車,不知道大家有沒有暈車,如果實在搞不清以上各種編碼關係,沒關係記住最後2個函數即可。

故事是這樣結束的

看著荧幕中輸出熟悉的中文字元,我激動地將轉碼後的內容拋給某司,並殷切地等待著酬勞,等待著欣賞速八大酒店頂層房間迎接的那一抹夕陽,以及細細品味著那一抹詭異的壞笑。直到最終荧幕跳出了一行字:速八8點場,影院見。

補充

2017年4月21號存在一個list清單,清單中的欄位是unicode格式的,當輸出這個list時,內容如下:

1

[u'\u827a\u672f\u9986',u'\u5b58\u50a8\u7ba1\u7406',u'\u609f\u8005',u'\u827a\u54c1',u'\u7ca4\u5907\u4eac',u'\u767e\u79cd',u'\u5fae\u55b7',u'\u827a\u672f\u4f5c\u54c1',u'\u57f9\u690d',u'\u6444\u5f71\u5bb6',u'\u666e\u53ca\u6559\ u80b2',u'\u5927\u9053\u81f3\u7b80',u'\u88c5\u5e27',u'\u96c5\u660c\u4ee5',u'\u9274\u8bc1',u'\u4e07\u6377',u'\u6838\u5fc3\u6280\u672f',u'\u884d\u751f\u54c1']

怎麼讓清單裡面的內容為中文?我猜想,輸出清單時,會自動將裡面的中文進行編碼,囙此可以這樣處理:

1

print str([i.encode(“utf-8”)for i in list_nokeyword]).decode('string-escape’)

輸出看看吧

1

['藝術館','存儲管理','悟者','藝品','粵備京','百種','微噴','藝術作品','培植','攝影家','普及教育','大道至簡','裝幀','雅昌以','鑒證','萬捷','核心技術','衍生品']

傳送門

Python2編碼之殤Python3編碼之美

歡迎您掃一掃上面的微信公眾號,訂閱我的部落格!

熱門文章推薦: