溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

由 Python2 和 Python3 中 socket.inet_aton() 實(shí)現(xiàn)不同引發(fā)的血案

發(fā)布時(shí)間:2020-07-05 08:11:22 來源:網(wǎng)絡(luò) 閱讀:840 作者:sylan215 欄目:編程語言

這幾天在做一個(gè)功能實(shí)現(xiàn)的時(shí)候,需要把別人用 Python2.6 寫好的腳步轉(zhuǎn)成 Python3.4 實(shí)現(xiàn),大部分地方轉(zhuǎn)化都沒啥問題,但是在 socket.inet_aton() 轉(zhuǎn)化的過程中出了點(diǎn)問題,花費(fèi)我不少的精力去解決,先做個(gè)記錄備忘,同時(shí)給后續(xù)需要的人做個(gè)提醒。

首先說一下,我在解決這個(gè)問題前期的思路有點(diǎn)問題,所以請(qǐng)關(guān)注最后的總結(jié)。

需求目的:把一個(gè) ip 地址使用 socket.inet_aton() 轉(zhuǎn)化后和一個(gè)字符串組合,然后算出 MD5。

下面是 Python2.6 的實(shí)現(xiàn)代碼:

#!python2
# -*- coding: utf-8 -*-

import socket
import hashlib

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    ip_md5 = hashlib.md5(socket.inet_aton(ip) + base_str).digest().encode('hex')
    print(ip_md5)

運(yùn)行后的輸出結(jié)果為:

fc138bb4748a18f885cc321c2c6396e2

如果原封不動(dòng)的使用 Python3.4 運(yùn)行后,報(bào)錯(cuò)如下:

Traceback (most recent call last):
  File "socket34.py", line 25, in <module>
    test1()
  File "socket34.py", line 10, in test1
    ip_md5 = hashlib.md5(socket.inet_aton(ip) + base_str).digest().encode('hex')
TypeError: can't concat bytes to str

提示說的是,socket.inet_aton(ip) 的返回值是 bytes 類型,所以不能和 str 類型的 base_str 直接進(jìn)行連接操作。
也就是說 Python2.6 和 python3.4 中對(duì)于 socket.inet_aton(ip) 的實(shí)現(xiàn)是有差異的,查官方文檔吧。
python2.6文檔說明:

socket.inet_aton(ip_string)
Convert an IPv4 address from dotted-quad string format (for example, ‘123.45.67.89’) to 32-bit packed binary format, as a bytes object four characters in length.

python3.4 文檔說明:

socket.inet_aton(ip_string)
Convert an IPv4 address from dotted-quad string format (for example, ‘123.45.67.89’) to 32-bit packed binary format, as a string four characters in length.

好吧,返回值類型不同,為了保證和原腳本邏輯一致,我就做個(gè)轉(zhuǎn)化,把 bytes 主動(dòng)轉(zhuǎn)換為 str 類型再連接,修改后的代碼如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip).decode('gbk') + base_str
    ip_md5 = hashlib.md5(str_md5).digest().encode('hex')
    print(ip_md5)

代碼通過 decode 把 bytes 使用 gbk 的方式解碼成 str,至于為什么用 gbk,是因?yàn)槲覍?duì)比了下,只有 gbk 編碼方式解碼后的輸出才和 python2.6 中的 str 返回值結(jié)果一致。

行,趕緊運(yùn)行一把試試看。。。還是報(bào)錯(cuò)了,這次的報(bào)錯(cuò)內(nèi)容變了:

Traceback (most recent call last):
  File "socket34.py", line 34, in <module>
    test1()
  File "socket34.py", line 12, in test1
    ip_md5 = hashlib.md5(str_md5).digest().encode('hex')
TypeError: Unicode-objects must be encoded before hashing

看起來 hashlib.md5() 在 Python2.6 和 Python3.4 中的實(shí)現(xiàn)也有差異,繼續(xù)看文檔。
python2.6文檔說明:

You can now feed this object with arbitrary strings using the update() method.

python3.4 文檔說明:

You can now feed this object with bytes-like objects (normally bytes) using the update() method.

依然是編碼格式的問題,Python2.6 中參數(shù)傳入的是 str,但是 Python3.4 中參數(shù)需要傳入 bytes,那就繼續(xù)轉(zhuǎn)碼吧。
再次轉(zhuǎn)碼后的代碼如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip).decode('gbk') + base_str
    ip_md5 = hashlib.md5(str_md5.encode('gbk')).digest().encode('hex')
    print(ip_md5)

運(yùn)行后再次報(bào)錯(cuò):

Traceback (most recent call last):
  File "socket34.py", line 33, in <module>
    test1()
  File "socket34.py", line 11, in test1
    ip_md5 = hashlib.md5(str_md5.encode('gbk')).digest().encode('hex')
AttributeError: 'bytes' object has no attribute 'encode'

好吧,繼續(xù)看文檔。
python2.6文檔說明:

hash.digest()
Return the digest of the strings passed to the update() method so far. This is a string of digest_size bytes which may contain non-ASCII characters, including null bytes.

python3.4 文檔說明:

hash.digest()
Return the digest of the data passed to the update() method so far. This is a bytes object of size digest_size which may contain bytes in the whole range from 0 to 255.

這次更嚴(yán)重,encode() 直接用不了,換方法吧,更新后的代碼如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib
import binascii

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip).decode('gbk') + base_str
    ip_md5 = binascii.hexlify(hashlib.md5(str_md5.encode('gbk')).digest()).decode()
    print(ip_md5)

運(yùn)行后的輸出結(jié)果:

fc138bb4748a18f885cc321c2c6396e2

終于得到了最終結(jié)果,激動(dòng),不過再回頭一看,如果知道這幾個(gè)函數(shù)的使用方式的話,就不需要 decode() 然后又 encode(),比如稍微優(yōu)化后的代碼如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib
import binascii

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip) + base_str.encode()
    ip_md5 = binascii.hexlify(hashlib.md5(str_md5).digest()).decode()
    print(ip_md5)

總結(jié):

  1. Python3 新增了 bytes 類型,對(duì)于 bytes 的轉(zhuǎn)換邏輯要特別清楚,這地方涉及了編碼類型,要特別關(guān)注;
  2. 在使用一些函數(shù)前,一定要搞清楚這個(gè)函數(shù)的具體實(shí)現(xiàn),必須清楚的知道使用了這個(gè)函數(shù)是什么效果,而不僅僅是看到暫時(shí)的效果,或者經(jīng)驗(yàn)主義的去調(diào)用(上面例子的最后一步,其實(shí)我一開始不是用的 binascii,而是用的現(xiàn)成的 md5 轉(zhuǎn)換函數(shù),導(dǎo)致 encode() 成了 utf-8 格式,而浪費(fèi)了不少時(shí)間去定位);
  3. 解決問題過程中,思路一定要清晰,不能靠猜,越猜越錯(cuò);
  4. 先弄明白問題的根本原因,直接從根源上去解決,比一步步的就錯(cuò)解錯(cuò),效果更好。

本文原創(chuàng)發(fā)布于公眾號(hào)「sylan215」,十年測(cè)試?yán)媳脑瓌?chuàng)干貨,關(guān)注我,漲姿勢(shì)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI