免费视频教程!零基础学Python系列(7) - 数据类型之bytes(上)

本节我们开始讲python数据类型之bytes类型,我们分为上下两个章节。

你可以直接到这个页面观看本节视频:免费视频教程!零基础学Python系列(7) - 数据类型之bytes(上)

以下为对应的课件内容:


Bytes是python3新增的一个数据类型,用于表示一个字节串,它是一个有序的序列。

通常有两种方式来构造一个bytes类型的对象:

1、通过bytes()函数构造

bytes_1 = bytes('hello', 'utf-8')
bytes_2 = bytes([1, 200, 80, 50])

2、通过b后面跟字符串的方式

bytes_3 = b'world'
bytes_4 = b'\x77\x6f\x72\x6c\x64'

我们在print一个bytes类型数据时,python会以/x的格式依次打印每个字节的值,以两位16进制来显示。但是python对于一些字符会直接字符编码转换,所以造成打印出来的结果看起来很混乱,比如:

​​​​​​​bytes_2 = bytes([1, 200, 80, 50])

print('bytes_2:', bytes_2)

输出结果为:

bytes_2: b'\x01\xc8P2'

最后两个数值80、50,被转换为了字符P、2,看起来很混乱。

 

这时,我们可以写一个简单的方法,让它不做这种转换:

# bytes 按照16进制输出,强制不ascii转码
def trans(s):
    return "b'%s'" % ''.join('\\x%.2x' % x for x in s)

bytes_2 = bytes([1, 200, 80, 50])

print('bytes_2:', trans(bytes_2))

输出结果为:

bytes_2: b'\x01\xc8\x50\x32'

这样我们看到,bytes里面包含了一个一个的字节。

因为我们还没有学函数的概念,所以大家只要知道在输出的时候调用这个方法即可。

 

bytes类型,存储的是一系列的字节,它并不关注这些字节具体表示什么含义(字符、网络数据、图片、音视频等)。Bytes并不约束你如果使用这些字节数据,你可以按照你自己的功能逻辑做任意的转换。这个转换逻辑,不是bytes数据类型的功能范畴。

 

比如:对于字符,通常我们需要对其做一个编码转换,将字节类型转换为有意义的字符串。这个转换规则,就是字符编码,紧接着下一小节我们会介绍字符编码。

 

我们可以看到,bytes类型也是一种序列,所以它的大多数操作方法和String一致。

# 操作方法
print(bytes_3[0: 3])
print(bytes_1 + bytes_3)
print(b'h' in bytes_1)
print(bytes_1.split(b'l'))
print(bytes_1.find(b'll'))
print(bytes_1.replace(b'l', b't'))

输出结果为:

b'wor'

b'helloworld'

True

[b'he', b'', b'o']

2

b'hetto'

是不是和string类型高度一致? bytes类型和string类型的对比如下:

  • string的基本单位是字符,bytes的基本单位是字节;
  • 他们都是属于一种序列,所以对于序列的操作方法,对他们基本都适用;
  • String和bytes都是不可变类型,不能对其元素进行修改。

 

注意,虽然bytes通常会和string一起使用,但是bytes并不只是给string用,它本质上是一个字节串。Bytes适合那种面向二进制流的存储数据,比如图片、视频等多媒体,或者网络通信等二进制报文流。

 

  • 字节序

字节序,顾名思义就是字节存储的顺序。大家可能觉得奇怪,字节不都是“从左到右”依次存储的吗?怎么会有字节序的问题?大家看看下面的例子:

#  author: Tiger,    wx ID:tiger-python

# file: ./6/6_2.py

# bytes 按照16进制输出,强制不ascii转码
def trans(s):
    return "b'%s'" % ''.join('\\x%.2x' % x for x in s)


# 字节序
byte_1 = 'python'.encode('utf-8')
print(trans(byte_1))

print('Big endian: ', hex(int.from_bytes(byte_1, byteorder='big', signed=False)))
print('Little endian: ', hex(int.from_bytes(byte_1, byteorder='little', signed=False)))

输出结果为:

b'\x70\x79\x74\x68\x6f\x6e'

Big endian:  0x707974686f6e

Little endian:  0x6e6f68747970

上面的实例中,我们将bytes类型b’python’强制转换为int类型,在转换过程中分别指定其字节序为big和little。从打印结果可以看出,这两种类型对应的输出结果完全相反。它们对应的就是大端字节序(Big endian,BE)小端字节序(Little endian,LE)

比如我要存储一个字节串:b’\x12\x34\x56\x78’:

大端字节序:从低地址到高地址,依次存储数据字节;

小端字节序:相反,从高地址到低地址,依次存储数据字节。因为我们查看内存通常是由低位地址向高位地址看,所以大端字节序是更加符合我们的习惯的,而小端则相反。

为什么计算机会产生两种不同的字节序呢?

因为字节序是由CPU架构决定,而在计算机技术发展初期,CPU架构的两大阵营X86和PowerPC分别采用了完全相反的两种字节序,X86采用了LE,PowerPC采用了BE。所以,才会导致我们现在需要面对字节序的问题。

 

我们可以下面的方法获取当前cpu的字节序类型:

# 获取当前cpu的字节序类型

import sys
print('endian of cur env:', sys.byteorder)

输出为:

endian of cur env: little

我使用的环境是X86的CPU,对应的是小端字节序。

 

如果你的程序只会在本地运行,不会涉及到跨主机(跨不同类型CPU)的操作,那么你不需要关注字节序。反之,你需要特别关注字节序,因为它容易出错。

如果计算机A采用了BE架构的CPU,计算机B采用了LE架构的CPU。我们有一段程序,在计算机A发送一个bytes : b’\x12\x34\x56\x78’给计算机B,那么计算机B解析出来的数据将是bytes : b’\x78\x56\x34\x12’,这就完全错了。

 

在这种跨主机的数据传输中的字节序,我们通常称之为网络字节序,网络字节序和CPU无关,它是网络通信协议定义的一套规范。几乎所有的网络字节序都采用了大端字节序BE。计算机将数据发送给网络协议之前,需要统一转换为网络字节序,同样,接收端的计算机从网络接收到数据后,也会统一将其由网络字节序转换为本机字节序。这样,我们就解决了跨主机的字节序问题。Python的网络编程里面,我们还会涉及到字节序,到时候我们可以回头来看看。

 

下一节我们继续讲字符编码和string与bytes直接的转换。


本节课程的视频和实例源码下载方式:点击->我的主页,查看个人简介。

我尽量坚持每日更新一节。

猜你喜欢

转载自blog.csdn.net/j00105840/article/details/105932863