查看: 267|回复: 0

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

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在 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 下无法编码。
  1. char = '中'
  2. print(len(char.encode('utf-8')))
  3. print(len(char.encode('gbk')))
复制代码

str 的元素是长度为 1 的 str,bytes 的元素是 int。下面代码中 len(text) 是字符数 8,len(data) 是字节数 12,data[0] 是 80,而不是字符 'P'。
  1. text = 'Python编程'
  2. data = text.encode('utf-8')
  3. print(type(text), len(text), text[0])
  4. print(type(data), len(data), data[0])
复制代码

二、bytes 创建与操作

bytes 是不可变字节序列。常见创建方式包括 b 前缀字面量、bytes() 整数序列、指定长度生成全零字节,以及从 str 调用 encode()。
  1. data1 = b'hello'
  2. data2 = bytes([72, 101, 108, 108, 111])
  3. data3 = bytes(10)
  4. data4 = 'Python编程'.encode('utf-8')
  5. print(data1)
  6. print(data2)
  7. print(len(data3))
  8. print(data4)
复制代码

bytes 索引访问返回整数,切片返回 bytes;成员检查可以用整数,也可以用单字节 bytes。常用方法包括 find、rfind、index、count、startswith、endswith、split、partition、replace、strip、isalpha、isdigit、join 等。
  1. data = b'hello world hello python'
  2. print(data[0])
  3. print(data[:5])
  4. print(data.find(b'hello'))
  5. print(data.rfind(b'hello'))
  6. print(data.count(b'hello'))
  7. print(data.split())
  8. print(data.replace(b'hello', b'hi', 1))
  9. print(b' '.join([b'hello', b'world']))
复制代码

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

三、bytearray 可变字节序列

bytearray 是 bytes 的可变版本,支持原地修改,适合网络缓冲区累积、图像数据处理等频繁修改字节的场景。它的元素仍然是 0-255 整数。
  1. ba = bytearray(b'hello world')
  2. ba[0] = ord('H')
  3. print(ba)
  4. ba[6:11] = b'python'
  5. print(ba)
  6. ba.append(33)
  7. ba.extend(b' world')
  8. ba.insert(5, ord(','))
  9. print(ba)
  10. last = ba.pop()
  11. print(last)
  12. ba.reverse()
  13. 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 编码时替换不可编码字符。
  1. text = 'Python编程'
  2. utf8_data = text.encode('utf-8')
  3. print(utf8_data.decode('utf-8'))
  4. gbk_data = text.encode('gbk')
  5. print(gbk_data.decode('gbk'))
  6. bad = b'hello' + bytes([255]) + b'world'
  7. print(bad.decode('utf-8', errors='ignore'))
  8. print(bad.decode('utf-8', errors='replace'))
复制代码

处理未知编码时,可以按常见编码依次尝试解码。下面函数尝试 utf-8、gbk、gb2312、iso-8859-1、windows-1252,并用替换字符比例做简单判断。
  1. def safe_decode(data):
  2.     encodings = ['utf-8', 'gbk', 'gb2312', 'iso-8859-1', 'windows-1252']
  3.     for enc in encodings:
  4.         try:
  5.             decoded = data.decode(enc)
  6.             if decoded.count('�') / max(len(decoded), 1) < 0.1:
  7.                 return enc, decoded
  8.         except (UnicodeDecodeError, UnicodeError):
  9.             continue
  10.     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,适合图片、音频、视频、压缩包等非文本文件。大文件复制应分块读取,避免一次性占满内存。
  1. def copy_file_binary(src, dst):
  2.     with open(src, 'rb') as f_in:
  3.         with open(dst, 'wb') as f_out:
  4.             while True:
  5.                 chunk = f_in.read(8192)
  6.                 if not chunk:
  7.                     break
  8.                 f_out.write(chunk)
复制代码

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

六、网络编程与 struct

socket 发送和接收的都是 bytes。构建自定义二进制协议时,可以用 struct.pack 打包整数。例如协议格式为:魔数 2 字节、消息 ID 4 字节、消息类型 2 字节、载荷长度 2 字节、载荷 N 字节。
  1. import struct
  2. magic = bytes([0xAB, 0xCD])
  3. payload_bytes = 'hello'.encode('utf-8')
  4. header = struct.pack('!IHH', 1001, 2, len(payload_bytes))
  5. packet = magic + header + payload_bytes
  6. 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 不要混用,转换必须显式指定编码,才能在文件、网络和二进制协议处理中稳定落地。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-20 12:07 , Processed in 0.021388 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部