关于python字节流,整形,字符串,hex字符串一些转换方法

字节流 <---> hex字符串

python3

首先说明下我用的是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中要怎么相互转换呢?

hex字符串 -> 字节流:

bytes.fromhex(string), 此 bytes 类方法返回一个解码给定字符串的 bytes 对象。 字符串必须由表示每个字节的两个十六进制数码构成,其中的 ASCII 空白符会被忽略。

>>>bytes.fromhex('2Ef0 F1f2  ')
b'.\xf0\xf1\xf2'

字节流 -> hex字符串:

hex(),返回一个字符串对象,该对象包含实例中每个字节的两个十六进制数字。

>>>b'\xf0\xf1\xf2'.hex()
'f0f1f2'

另外可以参看bytearray,区别可能就是,bytearray对应的对象是可变对应物,而bytes对应对象是不可变

python2

python2和python3有很大区别,上面说的函数好像是只有python3才有的(没有仔细了解),所以到了python2,那些函数基本上不可用,或者说用法也有很大的不一样。ok,接下来我们看看python2怎么去实现字节流和hex字符串相互转换。

python2 字符串对象中有两个函数,分别叫decode和encode,顾名思义decode是解码,encode是编码,解码就是以指定的格式解码成本来的样子,而编码就是以指定的方式编码成所要的样子(比如ASCII,unicode等等),我这样说的如果看不懂,就不要写代码了吧,回家种田养猪去吧。 假定我们现在有一串hex字符串:

>> hexstring = ‘2b230948b6a30834017b6c80cd5773b2c4f08f13e424ca9a4fee1f11a9bfd9f4a8d9e8833ddabc8b09df8286’

每两个字符为一个字节,那么总共有88个字符,那就是44个字节,所以我们要怎么将其变成b'\x2b\x23...',刚刚说了这个hex字符串是经过编码的,编码方式是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'))

字节流 -> 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字符串。

参考

  1. 官方参考文档(python3)

  2. 官方参考文档](python2)

字节流<---->整形列表

python3

字节流我们已经很清楚了,就不再多说了,至于整形列表,简单说就是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 对象还可以通过其他几种方式来创建:

  • 指定长度的以零值填充的 bytes 对象: bytes(10)

  • 通过由整数组成的可迭代对象: bytes(range(20))

  • 通过缓冲区协议复制现有的二进制数据: bytes(obj)

当然还有bytearray对象也可以像bytes一样 ,唯一不同就是bytearray对象是可变序列。 另外使用int.to_bytes函数是将一个整数分解成一个字节一个字节形式。

python2

咱们再来看看使用同样的方法应用在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开头的字节串。

参考文档

  1. bytes官方文档

  2. struct官方文档

字节流 <----> 字符串

python3

字符串经过某种编码方式就转换成了字节串,解码后就变成它原本的样子,比如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

说真的,我已经不太想研究python2了,毕竟马上就是2020年了,python2的末日已然来临,还有什么理由不拥抱python3,是为了那些在python3 上不能用的库吗,还是习惯使然,如果是第二种,那么请改变自己保守的性格,去迎接新事物吧。吐槽归吐槽,代码还是要写的。

字节流 -> 字符串

刚刚说过python2中字符串和字节串是不区分的,所以基本上可以认为字节串。当然你也可以用decode。

字符串 -> 字节流

刚刚说过python2中字符串和字节串是不区分的,所以基本上可以认为字节串。当然你也可以用encode。

参考

  1. str编码官方文档

  2. bytes解码官方文档