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

By [program tech record](https://paragraph.com/@program-tech-record) · 2022-01-05

---

字节流 <---> 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.  [官方参考文档](https://docs.python.org/zh-cn/3.7/library/stdtypes.html?highlight=bytes#bytes)（python3）
    
2.  [官方参考文档](https://docs.python.org/2.7/library/stdtypes.html?highlight=decode#str.decode)\](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官方文档](https://docs.python.org/zh-cn/3.7/library/stdtypes.html#bytes)
    
2.  [struct官方文档](https://docs.python.org/zh-cn/3.7/library/struct.html?highlight=struct#module-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编码官方文档](https://docs.python.org/zh-cn/3.7/library/stdtypes.html?highlight=encode#str.encode)
    
2.  [bytes解码官方文档](https://docs.python.org/zh-cn/3.7/library/stdtypes.html?highlight=decode#bytes.decode)

---

*Originally published on [program tech record](https://paragraph.com/@program-tech-record/python-hex)*
