脚本专家 发表于 2026-9-20 10:00:00

Python bytes与bytearray二进制处理与编码解

在 Python 3 中,str 是处理文本的主力,但网络数据包、图片文件、二进制协议、加密解密等场景,必须使用 bytes 或 bytearray。核心关系是:str 是 Unicode 字符序列,bytes 和 bytearray 是 0-255 的整数序列,两者通过 encode() 和 decode() 相互转换。

一、字符、编码与字节

计算机只能存储 bit,8 个 bit 组成一个 byte,一个字节可表示 0-255。字符要存储和传输,必须先编码成字节序列。同一个字符在不同编码下字节数不同,例如“中”在 UTF-8 下占 3 字节,在 GBK 下占 2 字节,在 ASCII 下无法编码。


char = '中'
print(len(char.encode('utf-8')))
print(len(char.encode('gbk')))


str 的元素是长度为 1 的 str,bytes 的元素是 int。下面代码中 len(text) 是字符数 8,len(data) 是字节数 12,data 是 80,而不是字符 'P'。


text = 'Python编程'
data = text.encode('utf-8')
print(type(text), len(text), text)
print(type(data), len(data), data)


二、bytes 创建与操作

bytes 是不可变字节序列。常见创建方式包括 b 前缀字面量、bytes() 整数序列、指定长度生成全零字节,以及从 str 调用 encode()。


data1 = b'hello'
data2 = bytes()
data3 = bytes(10)
data4 = 'Python编程'.encode('utf-8')
print(data1)
print(data2)
print(len(data3))
print(data4)


bytes 索引访问返回整数,切片返回 bytes;成员检查可以用整数,也可以用单字节 bytes。常用方法包括 find、rfind、index、count、startswith、endswith、split、partition、replace、strip、isalpha、isdigit、join 等。


data = b'hello world hello python'
print(data)
print(data[:5])
print(data.find(b'hello'))
print(data.rfind(b'hello'))
print(data.count(b'hello'))
print(data.split())
print(data.replace(b'hello', b'hi', 1))
print(b' '.join())


因为 bytes 不可变,不能执行 data = 106,否则会抛 TypeError。

三、bytearray 可变字节序列

bytearray 是 bytes 的可变版本,支持原地修改,适合网络缓冲区累积、图像数据处理等频繁修改字节的场景。它的元素仍然是 0-255 整数。


ba = bytearray(b'hello world')
ba = ord('H')
print(ba)
ba = b'python'
print(ba)
ba.append(33)
ba.extend(b' world')
ba.insert(5, ord(','))
print(ba)
last = ba.pop()
print(last)
ba.reverse()
print(ba)


bytearray 提供类似 list 的 append、extend、insert、pop、remove、reverse、clear,也有 copy() 方法。bytes(ba) 可以得到不可变拷贝。查找、替换、大小写等方法和 bytes 类似。

四、encode/decode 与错误处理

encode 从 str 到 bytes,decode 从 bytes 到 str。用错编码会乱码或抛 UnicodeDecodeError。errors 参数控制异常字符处理:strict 默认抛异常;ignore 忽略;replace 替换为 ? 或 U+FFFD;xmlcharrefreplace、backslashreplace、namereplace 用于 ASCII 编码时替换不可编码字符。


text = 'Python编程'
utf8_data = text.encode('utf-8')
print(utf8_data.decode('utf-8'))
gbk_data = text.encode('gbk')
print(gbk_data.decode('gbk'))
bad = b'hello' + bytes() + b'world'
print(bad.decode('utf-8', errors='ignore'))
print(bad.decode('utf-8', errors='replace'))


处理未知编码时,可以按常见编码依次尝试解码。下面函数尝试 utf-8、gbk、gb2312、iso-8859-1、windows-1252,并用替换字符比例做简单判断。


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('�') / max(len(decoded), 1) < 0.1:
                return enc, decoded
      except (UnicodeDecodeError, UnicodeError):
            continue
    return 'utf-8', data.decode('utf-8', errors='replace')


BOM 是字节顺序标记。UTF-8 BOM 为 EF BB BF,utf-8-sig 编码会写入或跳过 BOM;UTF-16 BOM 为 FE FF 或 FF FE,用于标识大端或小端。处理带 BOM 文件时,可以检测开头并跳过,更简单的方式是使用 encoding='utf-8-sig'。

五、文件 I/O 与二进制数据

文本模式读写自动编码解码,返回 str;二进制模式读写返回 bytes,适合图片、音频、视频、压缩包等非文本文件。大文件复制应分块读取,避免一次性占满内存。


def copy_file_binary(src, dst):
    with open(src, 'rb') as f_in:
      with open(dst, 'wb') as f_out:
            while True:
                chunk = f_in.read(8192)
                if not chunk:
                  break
                f_out.write(chunk)


文件类型可以通过文件头魔数判断。例如 PNG、JPEG、GIF、PDF、ZIP、DOCX/XLSX 都有固定开头的字节序列。读取前几个字节,再用 startswith 比较,即可做简单类型检测。

六、网络编程与 struct

socket 发送和接收的都是 bytes。构建自定义二进制协议时,可以用 struct.pack 打包整数。例如协议格式为:魔数 2 字节、消息 ID 4 字节、消息类型 2 字节、载荷长度 2 字节、载荷 N 字节。


import struct
magic = bytes()
payload_bytes = 'hello'.encode('utf-8')
header = struct.pack('!IHH', 1001, 2, len(payload_bytes))
packet = magic + header + payload_bytes
print(packet)


这里的 ! 表示网络字节序(大端),I 是 4 字节无符号整数,H 是 2 字节无符号整数。接收端解析时,要按同样格式 unpack,并校验魔数和载荷长度,否则容易读到错误偏移或截断数据。

七、常见错误与选择建议

经典错误是把 str 直接当 bytes 用,例如 socket.send 传 str,或对 str 调用 decode,会报 TypeError: a bytes-like object is required。排查时先确认数据来源是文本还是二进制:文本转字节用 encode,字节转文本用 decode;检查编码是否匹配;未知编码先尝试 utf-8、gbk 等,再用 errors='replace' 兜底。bytes 与十六进制字符串互转可借助 bytes.fromhex() 和 bytes.hex(),便于打印和比对二进制数据。

性能与适用场景方面,bytes 不可变,传递和哈希更安全,适合只读数据;bytearray 可变,频繁修改时能避免反复创建新对象,适合缓冲区。str 与 bytes 不要混用,转换必须显式指定编码,才能在文件、网络和二进制协议处理中稳定落地。

热心网友1 发表于 2026-9-20 19:30:00

Re: Python bytes与bytearray二进制处理与编码解

楼主这篇整理得挺系统,把 str、bytes、bytearray 的关系和常见坑都串起来了。特别是“str 元素是字符,bytes 元素是 int”这个点,很多人第一次碰二进制处理时容易搞混;还有 bytes 不可变、bytearray 适合频繁原地修改,也讲得很清楚。 编码解码部分,errors 参数和 BOM 处理很实用,safe_decode 的思路对处理来源不明的数据也有参考价值。不过这里有个小地方可以留意:普通 decode 成功时一般不会产生 U+FFFD,只有在 errors='replace' 或原数据本身包含替换字符时才会出现,所以用“替换字符比例”来判断编码可能不太灵敏。实际用的时候可以结合异常捕获和编码特征再判断。 文件 I/O 二进制模式这块如果后面继续展开,建议把 open 的 'rb'、'wb'、'ab' 和 bytes、bytearray 的直接读写再补充一下,配上前面的例子会更完整。整体很适合当速查笔记。

热心网友1 发表于 2026-9-20 19:30:00

Re: Python bytes与bytearray二进制处理与编码解

楼主这篇整理得很实用,str 和 bytes 的元素类型差异、bytes 不可变而 bytearray 可原地修改,以及 encode/decode 的 errors 参数都讲得比较清楚。safe_decode 的思路也很适合处理来源不明的字节流,不过要注意 iso-8859-1 和 windows-1252 几乎能解码所有字节,容易让编码判断提前返回,实际用的时候最好把这两个放到最后,或者结合 BOM、统计特征再确认。utf-8-sig 跳过 BOM 这点很省事,平时读带 BOM 的文件确实好用。文件 I/O 那部分好像还没写完,期待后续把文本模式和二进制模式的区别、换行处理和缓冲区刷新也补上。

热心网友3 发表于 2026-9-20 19:40:00

Re: Python bytes与bytearray二进制处理与编码解

这篇整理得很系统,把 str、bytes、bytearray 的关系讲得很清楚。特别是 bytes 元素是 int、切片还是 bytes,以及 bytearray 可以原地修改这部分,对刚接触二进制处理的人很友好。encode/decode 和 errors 参数那几个例子也很实用,safe_decode 按常见编码依次尝试的思路挺有参考价值。BOM 和 utf-8-sig 的提醒也很及时。最后文件 I/O 部分好像还没展开,期待后续内容。
页: [1]
查看完整版本: Python bytes与bytearray二进制处理与编码解