脚本专家 发表于 2026-6-12 10:00:01

Python字节操作进阶:bytes与bytearray核心区别与实战用法

在Python开发中,处理字符串(str)时一切看起来都很自然,但当你需要读取二进制文件、解析网络数据包或进行加密计算时,就会遇到bytes和bytearray。不少初学者混淆它们与str的关系,甚至遇到类似“a bytes-like object is required”的错误。本文从底层原理出发,梳理bytes和bytearray的区别、创建与操作方式、编码解码规则,并给出文件I/O、网络编程、struct打包等典型场景的代码实践。

一、为什么需要bytes?

计算机只能存储二进制数据(0和1),8个bit组成一个byte,可以表示0~255的整数。人类使用的文字、符号需要经过编码转换为字节序列才能存储和传输。Python中str是Unicode字符序列,而bytes是0~255的整数序列。两者通过encode()和decode()相互转换。

同一个字符在不同编码下字节长度不同:

char = '中'
print(char.encode('utf-8'))   # b'\xe4\xb8\xad' (3字节)
print(char.encode('gbk'))   # b'\xd6\xd0' (2字节)
print(char.encode('ascii'))   # 报错UnicodeEncodeError


str的元素是长度为1的字符串,而bytes的元素是整数:

text = 'Python编程'
data = text.encode('utf-8')
print(data)   # 80 (整数)
print(text)   # 'P' (字符串)


二、bytes——不可变的字节序列

2.1 创建bytes的四种方式

1. 使用b前缀字面量(仅限ASCII字符或转义序列)
2. bytes()构造函数从整数序列创建
3. bytes()构造函数指定长度(全零)
4. 通过str.encode()方法


# 方式一:b前缀
b'hello'
b'\xe4\xbd\xa0\xe5\xa5\xbd'   # '你好'的UTF-8转义

# 方式二:整数列表
bytes()   # b'Hello'

# 方式三:指定长度
bytes(5)   # b'\x00\x00\x00\x00\x00'

# 方式四:encode
'Python编程'.encode('utf-8')# b'Python\xe7\xbc\x96\xe7\xa8\x8b'


2.2 基本操作(索引、切片、成员检查)


data = b'hello world'
print(data)      # 104 (整数)
print(data[:5])       # b'hello'
print(104 in data)    # True
print(b'hello' in data) # True
print(list(b'ABC'))   #


2.3 常用方法

bytes支持类似str的方法,但操作对象是字节:

data = b'hello world hello python'
print(data.find(b'hello'))      # 0
print(data.count(b'hello'))   # 2
print(data.replace(b'hello',b'hi')) # b'hi world hi python'
print(b' hello '.strip())       # b'hello'
print(b' '.join())   # b'a b'


三、bytearray——可变的字节序列

bytearray与bytes类似,但可以原地修改,适合需要频繁修改二进制数据的场景(如网络缓冲、图像处理)。


ba = bytearray(b'hello')
ba = 106               # 改为'j' (ASCII 106)
print(ba)               # bytearray(b'jello')

# bytes不可变,赋值会报错
# b'hello' = 106       # TypeError


3.1 创建与修改操作


ba = bytearray(b'hello world')
ba = ord('H')
ba = b'python'      # 切片赋值
ba.append(33)             # 追加感叹号
ba.extend(b' world')      # 扩展
ba.insert(5, ord(','))    # 插入
ba.pop()                  # 弹出末尾
ba.remove(ord('a'))       # 移除第一次出现的字节值
ba.reverse()            # 反转
ba.clear()                # 清空


3.2 特有方法

bytearray有copy()方法,返回独立拷贝:

ba = bytearray(b'Hello')
copied = ba.copy()
ba = 106
print(ba)      # bytearray(b'jello')
print(copied)    # bytearray(b'Hello')


四、编码与解码——str与bytes的桥梁

4.1 encode()——从str到bytes


text = 'Python编程'
print(text.encode('utf-8'))
print(text.encode('gbk'))
print(text.encode('ascii', errors='replace'))# b'Python????'

errors参数支持strict(默认)、ignore、replace、xmlcharrefreplace、backslashreplace、namereplace。

4.2 decode()——从bytes到str


data = b'Python\xe7\xbc\x96\xe7\xa8\x8b'
print(data.decode('utf-8'))   # Python编程
# 错误编码导致乱码或异常
corrupted = b'hello\xffworld'
print(corrupted.decode('utf-8', errors='replace')) # hello?world


实际中可编写安全解码函数,尝试多种编码:

def safe_decode(data):
    encodings = ['utf-8','gbk','gb2312','iso-8859-1','windows-1252']
    for enc in encodings:
      try:
            decoded = data.decode(enc)
            if decoded.count('\ufffd') / max(len(decoded),1) < 0.1:
                return enc, decoded
      except:
            continue
    return 'utf-8', data.decode('utf-8', errors='replace')


4.3 BOM(字节顺序标记)

BOM用于标识编码和字节序,UTF-8 BOM为EF BB BF,UTF-16 LE BOM为FF FE,UTF-16 BE BOM为FE FF。处理文件时可检测并移除BOM:

raw = b'\xef\xbb\xbfhello'
if raw.startswith(b'\xef\xbb\xbf'):
    text = raw.decode('utf-8')

Python的utf-8-sig编码可自动处理BOM:

with open('test.txt','w',encoding='utf-8-sig') as f:
    f.write('带BOM的文字')


五、实用场景

5.1 文件I/O——二进制模式

处理非文本文件(图片、音频等)必须使用二进制模式:

with open('image.jpg','rb') as f:
    data = f.read()
# 分块拷贝大文件
def copy_binary(src, dst):
    with open(src,'rb') as fin, open(dst,'wb') as fout:
      while True:
            chunk = fin.read(8192)
            if not chunk: break
            fout.write(chunk)

通过文件头魔数判断文件类型:

def detect_type(filepath):
    magic = {
      b'\x89PNG\r\n\x1a\n':'PNG',
      b'\xff\xd8\xff':'JPEG',
      b'%PDF':'PDF',
      b'PK\x03\x04':'ZIP',
    }
    with open(filepath,'rb') as f:
      head = f.read(8)
      for m,t in magic.items():
            if head.startswith(m):
                return t
    return '未知'


5.2 网络编程

socket收发数据均为bytes:

import socket
sock = socket.socket()
sock.connect(('httpbin.org',80))
request = b'GET /get HTTP/1.1\r\nHost: httpbin.org\r\n\r\n'
sock.sendall(request)
response = b''
while True:
    chunk = sock.recv(4096)
    if not chunk: break
    response += chunk
sock.close()


5.3 使用struct模块打包二进制数据

处理自定义二进制协议:

import struct
# 打包:魔数(2B) + 消息ID(4B) + 类型(2B) + 长度(2B) + 载荷
magic = b'\xAB\xCD'
msg_id, msg_type, payload = 1, 0, 'Hello'
payload_bytes = payload.encode('utf-8')
header = struct.pack('!IHH', msg_id, msg_type, len(payload_bytes))
packet = magic + header + payload_bytes


六、常见错误与调试

常见错误:TypeError: a bytes-like object is required, not 'str'。
通常发生在需要bytes的地方传入了str,比如socket.sendall()传入字符串。解决方法是先encode()。

bytes与十六进制字符串互转:

# bytes -> hex string
print(b'hello'.hex())   # '68656c6c6f'
# hex string -> bytes
print(bytes.fromhex('68656c6c6f'))# b'hello'


七、总结

bytes是不可变的字节序列,bytearray是可变的。str与bytes通过encode/decode转换。处理二进制文件、网络通信、协议解析时,必须使用bytes或bytearray。掌握它们的创建、操作和编码解码细节,能避免开发中常见的类型错误和乱码问题。

热心网友1 发表于 2026-6-12 10:10:00

Re: Python字节操作进阶:bytes与bytearray核心区别与实战用法

感谢楼主的详细教程!平时写脚本时经常遇到bytes和str混用的报错,这篇文章把底层区别和操作方法梳理得很清楚,尤其是bytearray可变性的例子很直观。关于切片赋值时长度不匹配的情况,我试过bytearray(b'hello') = b'ABC'是可以的,但如果源长度大于目标呢?会不会自动调整bytearray的长度?希望楼主能补充一下这方面的边界情况。总之非常实用,已收藏!

热心网友1 发表于 2026-6-19 22:35:00

Re: Python字节操作进阶:bytes与bytearray核心区别与实战用法

非常感谢楼主的详细讲解!之前一直在用 bytes 和 bytearray 做网络协议解析,但对底层细节还是有些模糊。特别是“bytes 元素是整数”这一点,以前调试时总以为自己记错了。楼主把创建方式、操作方法、编解码陷阱都理得很清楚,尤其是 safe_decode 那段代码,在实际项目中非常实用。另外,bytearray 的切片赋值和 append 扩展功能,用来构建网络缓冲比手动拼接 bytes 要方便很多。希望楼主后续能再讲讲 struct 打包解包时的注意事项,或者 bytes 在 socket 收发中的常见坑。再次感谢分享!

热心网友5 发表于 2026-6-20 00:30:00

Re: Python字节操作进阶:bytes与bytearray核心区别与实战用法

感谢分享!非常清晰实用的教程,把 bytes 和 bytearray 的核心区别讲得很透彻。最近正好在做一个二进制日志解析工具,bytearray 的可变性确实省去了很多临时拷贝,配合切片赋值效率很高。另外补充一点:对于大文件读取,可以用 `memoryview` 对 bytes 或 bytearray 进行零拷贝切片,避免重复分配内存,社区里有些性能敏感的场景会这样用。再次感谢,省了我自己翻文档的时间。
页: [1]
查看完整版本: Python字节操作进阶:bytes与bytearray核心区别与实战用法