首先说明下我用的是python3.7, 可能不能兼容所有的python3,比如early python3不一定有这些方法,但是newer python3应该是有的,具体有没有我以下用的方法要查官方文档。
我们来看一下什么是hex字符串,望文生义,它肯定是字符串类型,但是literal形式是hex,即16进制样子。
比如下面就是hex字符串
hexstring = ‘2b230948b6a30834017b6c80cd5773b2c4f08f13e424ca9a4fee1f11a9bfd9f4a8d9e8833ddabc8b09df8286’
我们可以分析出每两个字符代表一个字节,先在我要将其转成字节流,什么,你不知道字节流,好吧,我们再来看一下什么是字节流,
在python中要定义一个字节流很简单,如下:
bytesstream1 = b'ABC' # 一个字符占一个字节,表现出形式就是对应的ASCII编码
bytesstream2 = b'\xe4\xb8\xad\xe6\x96\x87'
他们在网络中都是一个字节一个字节地传输。
那么问题来了,在python中要怎么相互转换呢?
bytes.fromhex(string), 此 bytes 类方法返回一个解码给定字符串的 bytes 对象。 字符串必须由表示每个字节的两个十六进制数码构成,其中的 ASCII 空白符会被忽略。
>>>bytes.fromhex('2Ef0 F1f2 ')
b'.\xf0\xf1\xf2'
hex(),返回一个字符串对象,该对象包含实例中每个字节的两个十六进制数字。
>>>b'\xf0\xf1\xf2'.hex()
'f0f1f2'
另外可以参看bytearray,区别可能就是,bytearray对应的对象是可变对应物,而bytes对应对象是不可变。
python2和python3有很大区别,上面说的函数好像是只有python3才有的(没有仔细了解),所以到了python2,那些函数基本上不可用,或者说用法也有很大的不一样。ok,接下来我们看看python2怎么去实现字节流和hex字符串相互转换。
python2 字符串对象中有两个函数,分别叫decode和encode,顾名思义decode是解码,encode是编码,解码就是以指定的格式解码成本来的样子,而编码就是以指定的方式编码成所要的样子(比如ASCII,unicode等等),我这样说的如果看不懂,就不要写代码了吧,回家种田养猪去吧。
假定我们现在有一串hex字符串:
>> hexstring = ‘2b230948b6a30834017b6c80cd5773b2c4f08f13e424ca9a4fee1f11a9bfd9f4a8d9e8833ddabc8b09df8286’
每两个字符为一个字节,那么总共有88个字符,那就是44个字节,所以我们要怎么将其变成b'\x2b\x23...',刚刚说了这个hex字符串是经过编码的,编码方式是hex。
str.decode([encoding[, errors]])
>>> hexstring.decode('hex')
'+#\tH\xb6\xa3\x084\x01{l\x80\xcdWs\xb2\xc4\xf0\x8f\x13\xe4$\xca\x9aO\xee\x1f\x11\xa9\xbf\xd9\xf4\xa8\xd9\xe8\x83=\xda\xbc\x8b\t\xdf\x82\x86'
以上得到不过是字符串,并不是字节流,经过查资料,发现并没有很好地方法转成b' '这种形式,所以只能先转成整形列表,这目前来看是最方便的,有其他更好的方法后续再更新(立个flag,大概率会忘记)
map(ord,hexsring.decode('hex'))
>>> a =b'\x0b\xaa\xff\xee\xe0\x30\x6a
>>> a.encode('hex')
'0baaffeee0306a'
这样就很好将字节流转成hex字符串,what's more,发现了一个很奇怪的问题,请看代码:
>>> a ='\x0b\xaa\xff\xee\xe0\x30\x6a'
>>> a.encode('hex')
'0baaffeee0306a'
有没有发现,python2不分字节码,前面加b和不加b没有任何区别,单个字符本来也是ASCII编码也是一个字节,所以用字符串表示的也可以看做是字节流,所以字符串也是可以用encode()直接转成hex字符串。
字节流我们已经很清楚了,就不再多说了,至于整形列表,简单说就是python 列表中元素都是整形数字,例如l=[100, 255, 129, 8, 90, 99, 71], 由于一个字节是8位,所以列表中数字我们一般认为范围在0 <= x <256,超过256或者小于0的我们会有其他方法进行转换,后面再说。
字节流转换成整形列表是很容易的:
>>> a = b'\x0b\xaa\x77\x88\x55\x01'
>>> list(a)
[11, 170, 119, 136, 85, 1]
1. bytes 字面值中只允许 ASCII 字符(无论源代码声明的编码为何)。 任何超出 127 的二进制值必须使用相应的转义序列形式加入 bytes 字面值。
2. bytes 对象的表示使用字面值格式 (b'...'),因为它通常都要比像 bytes([46, 46, 46]) 这样的格式更好用。 你总是可以使用 list(b) 将 bytes 对象转换为一个由整数构成的列表
上述抄取官方文档,使用list函数就能将bytes对象转换成整数列表。
说到这个,我脑海第一时间想到便是struct的库,但是又觉得应该还有其他方法,不管怎样,咱们先来看看strcut如何使用的,talk is cheap,show you the code:
import struct
b = struct.pack('!{}B'.format(len(a)), *a)
print(b)
b'\x0b\xaaw\x88U\x01'
其中!是大端序,一般用于网络传输,需要其它字节序,请更改,B是unsigned char 整形占一个字节,字节长度就是列表长度,特别注意的是,传入参数,由于列表长度是6个,当你如果传入参数只写a,会提示需要6个参数,但是只传了1个,所以需要加以改正,但是我们总不可能手动一个一个的写传入的参数,假设列表是100个,手写100个参数是不是要累死。在python中,* 会自动将参数打包成tuple传给函数,多参数可以这样做,如果是要传入指定参数,比如a = 1,b = 2,这种可以用**
除了这种方法外,参考官方文档后,发现还要更好的方法:
a = bytes([11, 170, 119, 136, 85, 1])
print(a)
b'\x0b\xaaw\x88U\x01'
字面值:a = b'saa\x0b\xaa\x77\x88\x55\x01'除了字面值形式,bytes 对象还可以通过其他几种方式来创建:
当然还有bytearray对象也可以像bytes一样 ,唯一不同就是bytearray对象是可变序列。
另外使用int.to_bytes函数是将一个整数分解成一个字节一个字节形式。
咱们再来看看使用同样的方法应用在python2上,发现结果完全不一样,这是为什么?
>>> a = b'\x0b\xaa\x77\x88\x55\x01'
>>> list(a)
['\x0b', '\xaa', 'w', '\x88', 'U', '\x01']
经过一番仔细探究,咱们再来看看
>>> a = b'\x0b\xaa\x77\x88\x55\x01'
>>> type(a)
<type 'str'>
在python2中,是没有字节串这一说的,也就说字节串和字符串是一个意思。
咱们再来看看python3。
a = b'\x0b\xaa\x77\x88\x55\x01'
type(a)
<class 'bytes'>
看到没有在python3中是有bytes这个类型的。
所以要想在python2中将字节串(其实还是字符串啦)转成整数列表,得曲线救国了。
here it is:
>>> map(ord, a)
[11, 170, 119, 136, 85, 1]
另外多说一句,map函数在python2中返回时列表,在python3中有所不同,具体参考官方文档。
当然,python2中也是有struct的,所以咱们还是先来试试struct效果
>>> b = struct.pack('!{}B'.format(len(a)), *a)
>>> print(b)
獁圲
>>> print(repr(b))
'\x0b\xaaw\x88U\x01'
可以看到python2 和 python3 有所不同,python3使用print可以直接打印出字节串,但是用直接python2打印会乱码,所以用repr转换婴一下就能看到效果了,刚刚说过python2中不存在字节这一说的,所以显示的直接是字符串而不是以b开头的字节串。
字符串经过某种编码方式就转换成了字节串,解码后就变成它原本的样子,比如s = ‘hello,world',这个是字符串,但是这个仅仅是我们看到的样子,或者说是它对外表现的形式,但是在计算机内存中肯定是一堆二进制数码,那么s对应的数码应该是多少了,所以这个时候编码就派上用场了。大家还记得大明湖畔的夏雨荷.....错了,还记得每次写py文件开头写的是东西么,比如这个
-*- coding:utf-8 -*-
这个就说明文件默认以utf-8编码,文件中字符串变量那么就是以utf-8编码的。知道这个后,我们就知道如何将字符串变成字节串了。
由于ascii只有128个,可显示的95,不可显示33,所以字节转字符,字节数值只能小于等于128
话不多说,看代码:
a = b'\x20\x24\x26\x2c\x2f\x4b\x50\x6e'
a.decode('utf-8')
' $&,/KPn'
字节串就是经过utf-8编码后的存储形式,所以通过utf-8解码得到字符串。其实你用其他的方式解码也能得到正确的字符串,这是为什么呢?
仔细想想吧,反正我知道我也不想告诉你,自己动手查查。
当然所有转成字节流都可以用struct去搞定,这里我就不再赘述了,各位看官自己尝试着用struct搞定。这里我用其他方法搞定,当然是bytes.
a = 'hello,world'
a.encode('utf-8')
b'hello,world'
当然这样也可以:
bytes(a, 'utf-8')
b'hello,world'
说真的,我已经不太想研究python2了,毕竟马上就是2020年了,python2的末日已然来临,还有什么理由不拥抱python3,是为了那些在python3 上不能用的库吗,还是习惯使然,如果是第二种,那么请改变自己保守的性格,去迎接新事物吧。吐槽归吐槽,代码还是要写的。
刚刚说过python2中字符串和字节串是不区分的,所以基本上可以认为字节串。当然你也可以用decode。
刚刚说过python2中字符串和字节串是不区分的,所以基本上可以认为字节串。当然你也可以用encode。