Python基础:bytes 与 bytearray 数据类型使用教程

bytesbytearray 是python非常重要的数据类型,但其重要性经常被我们忽视了。在实际开发过程中,又总是遇到 bytes 类型。举例,pickle 序列化, json序列化就是将对象转为bytes类型。字符串编码问题也是1个常见的bytes相关问题,图像数据都是bytes类型,等等。
另外,bytes, bytearray 直接处理二进制数据, 处理速度比str, list, tuple等类型要快很多,适合性能要求高的应用开发,如图像处理,网络通信等。memoryview提供了1种以数组方式访问内存数据的方式,进一步方便了bytes类型的使用。

本文将介绍bytes类型数据的创建及各类操作,与其它类型之间的转换,字符串编码解码原理,bytearraymemoryview 的基本语法以及常见使用方式。

1、生成bytes变量的方法:

可以多种方法生成bytes变量实例,如下面的例子

>>> bytes(b'hello world')  # 使用字节串实例创建
b'hello world'
bytes(10)   # 指定生成10个全零的字节串, 字节串空值就是0. 
>>> bytes(10)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
bytes(range(20))   # 生成1个序列
>>> bytes(range(20))
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13'
bytes(binary_obj) # 如果是字符串对象,需要 encoding参数

Bytes与各种数据类型之间的转换

字符串转bytes

字符串转bytes, 是用 encode() 方法
data = str_data.encode(encoding="utf-8")

>>> str = "你好,世界"
>>> byte_data = str.encode(encoding='utf-8')
>>> byte_data
b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c'

bytes 类型字符串在屏幕显示时,除了ascii字符外,其它字符无法以可读方式显示。

bytes 转字符串:

byte_data.decode(encoding="utf-8")

int 转 bytes

用int对象的to_bytes()方法

# int variable
num = 7
# int to bytes
num_bytes = num.to_bytes(2, byteorder='big')
# display result and type
print(num_bytes)
print(type(num_bytes))

第2个参数,表示双字节整数中高低字符顺序,big 表示正常顺序,即低字节在前,高字节在后。 本例输出:

b'\x00\x07'
<class 'bytes'>

little 表示,高字节在前,低字节在后。上例中,将2个参数改为little, 则显示出的数字为b’\x07\x00’

>>> num = 7
>>> num_bytes = num.to_bytes(2, byteorder='little')
>>> print(num_bytes)
b'\x07\x00'
>>> print(type(num_bytes))
<class 'bytes'>

bytes 转int , 使用 int.from_bytes()方法

>>> d=3324
>>> byte_data=d.to_bytes(5,"big")
>>> byte_data
b'\x00\x00\x00\x0c\xfc'
>>> int_data = int.from_bytes(byte_data,"big")
>>> int_data
3324

float类型无法直接转换为bytes, 可以先转为string, 再转为bytes.

dict, tuple, list 转bytes

dict, list, tuple 转bytes, 通常使用pickle序列化, 或用 json序列化
pickle 序列化就是将对象转为字节码,保存进文件,或者 bytes变量。
json也是字节串,只是各种软件都支持json格式识别,所以可以方便显示查看。

>>> import pickle
>>> dl
[30, 203, 3, 133333383]
>>> by10 = pickle.dumps(dl)
>>> type(by10)
<class 'bytes'>
>>> pickle.loads(by10)
[30, 203, 3, 133333383]
>>>

上面的例子可以看到,当用pick.dump(dl)序列化后,by10是1个bytes类型。

bytes 变量内容的操作

基本上字符串支持的操作,bytes 字节串也都支持,只不过是二进制方式,可读性较差。 .
如在字符串中查找、替换字符等

>>> a=b"alibaba"
>>> a.count(b'a')
3
>>> a.find(b'a',3)
4
>>> chr(a[4])
'a'
>>> b=a.replace(b'a',b'x')
>>> b
b'xlibxbx'

注意 bytes 类型是 immutable, 其值不可修改。

2、字符串编码与解码

字符串的编码与解码,其实就是将指定协议,字符串转换为bytes类型,以及从bytes类型转回字符串的过程。

字符编码协议

在计算机低层是用二进制码来运行的,只有0和1。计算机基本存储单位为字节, 8 比特(bit)等于1个字节(byte),即一个字节能表示的最大整数是 255(1111 1111)。最初的字符集格式为用1个字节来表示所有字符,也就是 ASCII 字符集,可以表示 256 个字符,支持英文字母,数字和少部分符号。
用1个字节用来表示1万个汉字显然不够,日文也存在同样问题。 为了统一编码方式来表示世界各国语言文字,出现了Unicode 字符集,它通常采用2个字节来表示1个字符。但有的字符多于2个字节。但对于法文等小字符集语言来说浪费了太多的比特位,因此在Unicode基础上又发展出可变长的UTF-8 编码方式,这也是python3默认的编码方式。

什么时候需要对字符串进行编码、解码?

字符串是可读方式的数据。但字符串内容在内存中也是以二进制方式存储。可以认为,字符串在内存中的形式是指定编码格式的1块内存数据。例如 "Python字符“这几个字符,5个英文字符 + 2个汉字,默认utf-8编码格式。Python UTF-8 编码,1个英文字母为1个字节,1个常用汉字是3个字节。
程序从字符串数据区读取数据的方式:

  • 第1-6字节,单字节转换为英文显示
  • 第7到12个字节,每3个字节为1个汉字编码,
>>> len(bytes('Python字符','utf-8'))
12
>>>

由于 string 类还封装了许多其它方法与属性,以及与系统的交互,最终实现汉字在屏幕上显示出来,所以包含Python字符 的string对象在内存中的占据字节数远不止12个字节。但转换成bytes后只有12个字节,保存在文件中,在网络发送时,非常节省资源,

需要显式地对字符串进行编码的场景主要有

  • 网络通信的发送侧, socket传输数据需要按字节传送,以占用更少网络资源, 因此通常需要将字符串提前转为字节码,也就是告知socket,,这块数据是用utf-8编码的,方式就是 data = strdata.encode( encoding="utf-8"), 这时data 的类型是 bytes
  • 在网络接收侧,收到数据,或者从文件中得到1块数据,且知道编码格式,用decode() 方法将字节数据转为字符串。
  • 用序列化方式保存文件,先将字符串转换为二进制编码,必须要指定编码格式,转换为二进制后保存,可以节省一些空间,同时提高了保密性。
  • 使用 ctyps 进行 c/c++函数参数转换时,c++字符串,指针等类型,对应的都是python的bytes类型。
  • Internet 邮件,HTTP URL请求参数只允许ASCII字符,需要将特殊字符,汉字等转码为ASCII. ( 但这种方式只是转码,不存在类型转变)

Base64 实际与 URL转码 性质很相似,也不会改变数据类型。 输入为byters,输出仍是。输入为str, 输出也是str. 因此不再赘述。

Python 编码和解码函数

encod()decode() 分别对应编码和解码函数,

编码:就是把可读的字符串,按指定编码格式,将字符逐一转换为二进制码,没有其它多余的数据。中文转换后,屏幕上显示为16进制数,只有ascii字符会显示,但在前面加个b’ ’ ,表示这是字节串。

my_b = '技能树'.encode('utf-8')
print('编码后',my_b) # 编码后 b'\xe6\x8a\x80\xe8\x83\xbd\xe6\xa0\x91'

>>> my_b = "hello world".encode('utf-8')
>>> my_b
b'hello world'

解码操作,就是把 bytes型数据转换为可读形式的数据类型,如下所示:

my_b = '技能树'.encode('utf-8')
print('编码后', my_b)  # 编码后 b'\xe6\x8a\x80\xe8\x83\xbd\xe6\xa0\x91'

my_str = my_b.decode('utf-8')
print("解码后", my_str)

解码后出现乱码,通常是解码指定的编码格式与编码时的不一致。 而且这种问题通常发生在网络接口上。 最常见问题:
HTTP GET请求有中文参数时,常遇到编解码问题在这里插入图片描述
主要原因:http协议对URL参数的编码要求是ASCII字符集,汉字是UTF-8。在发送时要进行两次编码才能将汉字转为ASCII字节码:

  • 第1次编码, 用 UTF-8 字符集,每个字符占3个字节。
  • 第2次编码,可以用 iso-8859-1,也可以用其它字符集,转为ASCII。

接收方也要进行两次解码,才能正确地还原汉字。

还好python 内置库urllib 提供了1条命令,1次就可以将汉字转为ASCII编码。
编码: urllib.parse.urlencode(dict ) 或者 urllib.parse.quote(string,encoding=“utf-8”)
解码: urllib.parse.unquote(encoded_str)
示例代码

keyword = "天气预报"
param_list = urllib.parse.urlencode( { 'q' : keyword } ) 
header = {'user-Agent':’haha‘}
url = 'http://www.baidu.com/s/'
response = request.get( url, params=param_list, headers = header )

字符编码值查看与转换

通过 ord() 函数获取字符的整数表示,通过 chr() 将整数转换为字符,例如下述代码

print(ord('爬')) # 29228
print(chr(29228))

3、Bytearray的使用

3.1 bytearray 类型的主要使用场景

bytearray 字节数组,使用方法与bytes类似,使用bytearray的主要原因:

  • 对bytes类型的数据进行增删改场景,如需要修改二进制文件,图片,音频文件等。
  • bytearray是 mutable,即可以修改元素值。也支持切片索引。sring类型是immutable,所以bytearray适用于对字符串进行增删改时。
  • 调用 C/C++ 函数时,用于获取二进制数据类型指针变量内容

3.2 bytearray 基础用法

创建bytearray变量语法:
variable_name = bytearray(source, encoding, errors),

每个元素为1个字符,如果不指定encoding, 默认是ascii,

>>> str = "Geeksforgeeks"
>>> array1 = bytearray(str, 'utf-8')
>>> print(array1)
bytearray(b'Geeksforgeeks')
>>> array1[3]
107
>>> chr(array1[3])
'k'
>>> len(array1)
13
>>> len(str)
13
>>>

中文字符转 bytearray

>>> str = "你好,世界"
>>> array2 = bytearray(str, 'utf-16')
>>> array2
bytearray(b'\xff\xfe`O}Y\x0c\xff\x16NLu')
>>>

int 转bytearray 类型
int型数据应放入1个数组,每个数据不大于255,否则报错


>>> dl = [ 30, 203, 3, 183]
>>> array3 = bytearray(dl)
>>> print(array3)
bytearray(b'\x1e\xcb\x03\xb7')
>>> len(array3)
4

>>> dl = [ 30, 203, 3, 133333383]
>>> array3 = bytearray(dl)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: byte must be in range(0, 256) 

3.3 对字符串进行操作

在bytearray二进制字符串中查找字符
命令格式 bytearray.find(sub[, start[, end]])

>>> arr1 = bytearray(b"aaaahellocccc")
>>> arr1.find(b'h',0,)     
4
>>>

string类型无法直接修改字符串,转为bytearray类型后可以进行修改。

>>> str1 = "Geeksforgeeks"
>>> array1 = bytearray(str1.encode())
>>> array1
bytearray(b'Geeksforgeeks')
>>> array1.replace(b's',b'x')
bytearray(b'Geekxforgeekx')

3.4 获取ctypes 指针变量内容

此示例,用ctypes 生成1个 c_ubyte类型数组,使用ctypes.memmove() 将该数组内容复制到barray变量,注意这是内存深拷贝方式。

data = (ctypes.c_ubyte *5)(0x11,0x22,0x33,0x44,0x55)
barray = bytearray(5)
ptr1 = (ctypes.c_ubyte * 5).from_buffer(barray)
ctypes.memmove(ptr1,data, 5)
print(f" barray[4] {barray[4]:#X}")

输出:

barray[4] 0X55

4. 内存视图 Memoryview

memoryview 对象允许 Python 代码访问一个对象的内部数据,只要该对象支持缓冲区协议而无需进行拷贝. 当前支持缓冲区协议的对象主要有bytes, bytearray, array.array数组。
可以理解为:memoryview 可用数组的方式来访问关联对象的数据,支持切片索引等数组的通用操作。从这点来看,memoryview 类似于C语言的指针功能。

示例,用 memoryview 来访问 bytes对象

>>> v = memoryview(b'abcefg')
>>> v[1]
98
>>>v[-1]
103
>>>v[1:4]
<memory at 0x7f3ddc9f4350>
>>>bytes(v[1:4])
b'bce

下层对象使用array的示例, 用数组方式访问成员,用tolist()方法可将数组转为list 类型

>>>import array
>>>a = array.array('l', [-11111111, 22222222, -33333333, 44444444])
>>>m = memoryview(a)
>>>m[0]
-11111111
>>>m[-1]
44444444
>>>m[::2].tolist()
[-11111111, -33333333]

如果下层对象是mutable, 也可以通过memory view来赋值

data = bytearray(b'abcefg')
v = memoryview(data)
v.readonly
False
v[0] = ord(b'z')
data
bytearray(b'zbcefg')

主要属性:

  • obj 内存视图的下层对象
  • readonly true/false
  • itemsize 每个元素的大小, 对于bytes, bytearray,此值为1
  • ndim 表示内存所代表的多维数组具有多少个维度
  • shape 表示 memoryview数组的维度
  • strides 一个整数元组,通过 ndim 的长度给出以字节表示的大小,以便访问数组中每个维度上的每个元素

主要方法

  • tolist(), 导出为list
a = array.array('I', [1, 2, 3, 4, 5])
x = memoryview(a)
x.tolist()
  • tobytes() 将缓冲区中的数据作为字节串返回。 这相当于在内存视图上调用 bytes 构造器。
  • hex() 用16进制表示缓冲区中的字节
  • release() 释放对象
  • 7
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
'Hellmann's writing has become an indispensable resource for me and many others as it fills a critical gap in Python Documentation with examples.' -- Jesse Noller, Python Core Developer and PSF Board Member Master the Powerful Python Standard Library through Real Code Examples The Python Standard Library contains hundreds of modules for interacting with the operating system, interpreter, and Internet--all extensively tested and ready to jump-start your application development. The Python Standard Library by Example introduces virtually every important area of the Python 2.7 library through concise, stand-alone source code/output examples, designed for easy learning and reuse. Building on his popular Python Module of the Week blog series, author and Python expert Doug Hellmann focuses on 'showing' not 'telling.' He explains code behavior through downloadable examples that fully demonstrate each feature. You'll find practical code for working with text, data types, algorithms, math, file systems, networking, the Internet, XML, email, cryptography, concurrency, runtime and language services, and much more. Each section fully covers one module, and links to valuable additional resources, making this book an ideal tutorial and reference. Coverage includes *Manipulating text with string, textwrap, re, and difflib*Implementing data structures: collections, array, queue, struct, copy, and more*Reading, writing, and manipulating files and directories*Regular expression pattern matching*Exchanging data and providing for persistence Archiving and data compression*Managing processes and threads*Using application 'building blocks': parsing command-line options, prompting for passwords, scheduling events, and logging*Testing, debugging, and compilation*Controlling runtime configuration*Using module and package utilities If you're new to Python, this book will quickly give you access to a whole new world of functionality. If you've worked with Python before, you'll discover new, powerful solutions and better ways to use the modules you've already tried.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值