python中struct模块之字节型数据的处理方法


Posted in Python onAugust 27, 2019

简介

这个模块处理python中常见类型数据和Python bytes之间转换。这可用于处理存储在文件或网络连接中的bytes数据以及其他来源。在python中没有专门处理字节的数据类型,建立字节型数据也比较麻烦,我们知道的bytes()函数也只能对无符号整型做处理,并且数据如下(没错,数字为多少就有多少个\x00,我们要是用这种方式来存储大量数据,结果可想而知):

va = bytes(1) # va: '\x00'
vb = bytes(2) # vb: '\x00\x00'
vc = bytes(5) # vc: '\x00\x00\x00\x00\x00'

但在python中str类型中既可以用字符串表示也可以以字节方式表示,所以你定义一个字节型的字符串常量,python是能处理它的:

va = '\x26' # va: '&'

struct处理

字节顺序

一个数据有多个字节表示的时候,字节的顺序不同也就决定了值,在struct中有以下几种字节顺序:

字符 字节顺序 尺寸 对齐方式
@ 本机 本机 本机
= 本机 标准
小端 标准
> 大端 标准
网络 标准

对于字节顺序,只有大端和小端两种方式,只是比如你用@和=代表你用本机的字节顺序,!代表你使用网络的字节顺序。你不指定字节顺序则默认的是@。

本地字节顺序是大端或小端,取决于主机系统。例如,Intel x86和AMD64(x86-64)是小端的; 摩托罗拉68000和PowerPC G5是大端; ARM和Intel Itanium具有可切换的字节序(双字节序)。使用sys.byteorder来检查你的系统的字节顺序。

数据格式

struct支持的打包解包的数据格式如下,我们需要指定格式才能对应处理,其中对应尺寸已列出(以字节为单位):

字符 C类型 python类型 标准尺寸
x 填充字节 没有意义的值
c char 长度为1的字节 1
b signed char 整型 1
B unsigned char 整型 1
_Bool 布尔 1
h short 整型 2
H unsigned short 整型 2
i int 整型 4
I unsigned int 整型 4
l long 整型 4
L unsigned long 整型 4
q long long 整型 8
Q unsigned long long 整型 8
n ssize_t 整型
N size_t 整型
e 浮动 2
f float 浮动 4
d double 浮动 8
s char[] 字节
p char[] 字节
P void * 整型

打包

通过struct的pack(fmt, *args)来实现对各种数据的打包(转换为对应字节数据),pack的需要传递的参数fmt就是数据的格式,包括了字节顺序、数据类型;后面的*args参数是需要打包的数据。

vaa = struct.pack('>I', 1255) # vaa: '\x00\x00\x04\xe7' 1*4=1个字节 vab = struct.pack('>II', 1255, 23) # vab: '\x00\x00\x04\xe7\x00\x00\x00\x17' 2*4=8个字节 vac = struct.pack('>2I?', 1255, 23, True) # vac: '\x00\x00\x04\xe7\x00\x00\x00\x17\x01' 2*4+1=9个字节

我们看上述三个使用例子(数据与数据之间没有填充,都是连续的,比如对于vac我们不知道 它是由两个4字节无符号整型和一个布尔构成,我们就无法取得正确的值),看fmt参数:

‘>I'代表了以大端的字节顺序打包一个4字节无符号整型数据,所以后面只跟了一个无符号整型参数1255;

‘>II'代表了以大端的字节顺序打包两个4字节无符号整型数据,所以后面跟了两个个无符号整型参数1255和23;

‘>2I?'代表了以大端的字节顺序打包两个4字节无符号整型和一个布尔型数据,所以后面跟了两个个无符号整型参数1255、23和一个布尔值True。

注意'2I'和'II','4I'和'IIII','2?'和'??'是一样的效果。

解包

通过struct的unpack(fmt, string)来实现对字符串的解包,fmt和打包的是完全一样的,如下(返回的结果是一个元组):

vaa = struct.pack('>I', 1255) # vaa: '\x00\x00\x04\xe7'
vab = struct.pack('>II', 1255, 23) # vab: '\x00\x00\x04\xe7\x00\x00\x00\x17'
vaaa = struct.unpack('>I', vaa) # vaaa: <class 'tuple'>: (1255, )
vaba = struct.unpack('>II', vab) # vaba: <class 'tuple'>: (1255, 23)

进阶使用

pack_into(fmt, buffer, offset, *args)

fmt参数和pack是一样的,buffer参数是可写的缓存区,offset是写入位置的偏移量,*args是需要写入的数据。这个有什么用呢,我们想想这样两个情况,我们有两个类型已经打包好,我们想在这两个已经打包好的数据后面再添加一个数据打包;或者我们要打包的数据很多,我们不可能在pack中把所有需要打包的数据都通过参数传递给pack,那你的pack函数可能得写成千上完个参数了。这时候我们就可以用到这个函数了。

要使用它必须要一个可以写入的缓存区,我们可以导入一个字符缓存区包,然后创建一个固定大小的缓存区(以字节为单位):

import struct
from ctypes import create_string_buffer

# 创建一个9字节大小的缓存区,初始化默认全部为\x00 
buf = create_string_buffer(9) # buf.raw: '\x00\x00\x00\x00\x00\x00\x00\x00\x00'

# 冲缓存区buf的第0个字节开始打包两个4字节无符号整型数据1和2
struct.pack_into(">II", buf, 0, 1, 2) # buf.raw: '\x00\x00\x00\x01\x00\x00\x00\x02\x00'
# 然后我们想再打包一个布尔型数据到buf中就可以改变以下偏移量
struct.pack_into(">?", buf, 8, True) # buf.raw: '\x00\x00\x00\x01\x00\x00\x00\x02\x01'

unpack_from(fmt, buffer, offset)和calcsize(fmt)结合解包数据

calcsize用于计算格式字符串所对应的结果的长度,如:struct.calcsize(‘II'),返回8。因为两个无符号整型所占用的长度是8个字节。unpack_from(fmt, buffer, offset)用于从buffer缓存区中使用fmt格式从offset偏移量处开始解包fmt里对应数量的数据。

import struct
from ctypes import create_string_buffer

buf = create_string_buffer(9)
struct.pack_into(">II", buf, 0, 1, 2)
struct.pack_into(">?", buf, 8, True)
# 记录位置
pos = 0
# 从buf缓存区中以大端方式从偏移位置pos处解包两个无符号整型数据返回,注意
#返回值如果只写一个则返回一个元组,否则你解包几个数据就要写几个返回值。
val = struct.unpack_from('>II', buf, pos) # val: <class 'tuple'>: (1, 2)
val_a, val_b = struct.unpack_from('>II', buf, pos) # val_a: 1 val_b: 2

# 重置解包位置
pos += struct.calcsize('>II') # pos: 8
val_c, = struct.unpack_from('>?', buf, pos) # val_c: True

示例

这个示例是基于mnist手写数字识别的,我们刚开始有60000张手写数字的图片(.bmp格式的),我们通过下述代码将60000张图片转换成字节型数据,bytes.py代码如下:

import struct
import os
import numpy as np
from ctypes import create_string_buffer
import cv2

# 创建一个60000 * 784 * 1 + 3 * 4字节大小的缓存区,初始化默认全部为\x00
buffer = create_string_buffer(60000 * 784 * 1 + 3 * 4)
def writeBytesData():
 index = 0
 BMP_NUM = 0
 BMP_WIDTH = 28
 BMP_HEIGHT = 28

 # 先保留三个无符号整型的缓存区
 index += struct.calcsize('>III')
 path = 'data/bmp'
 if not os.path.exists(path):
  print('No this dir!')
  return
 list = os.listdir(path)
 for line_bmp in list:
  bmp_path = os.path.join(path, line_bmp)
  if os.path.isdir(bmp_path):
   print('This is not a .bmp')
  else:
   BMP_NUM += 1
   print(BMP_NUM)
   buf = cv2.imread(bmp_path, cv2.IMREAD_GRAYSCALE)
   buf = np.reshape(buf, [784])
   for pos in range(buf.__len__()):
    struct.pack_into('>B', buffer, index, buf[pos])
    index += struct.calcsize('>B')

 # 将保留缓存区的内容填上
 struct.pack_into('>III', buffer, 0, BMP_NUM, BMP_WIDTH, BMP_HEIGHT)
 with open('data/bytes/bytes.bytes', 'wb') as fp:
  fp.write(buffer)


def readFromBytes():
 index = 0
 images = []
 with open('data/bytes/bytes.bytes', 'rb') as fp:
  buffer = fp.read()
  # 解包前三个无符号整型
  bmp_num, bmp_width, bmp_height = struct.unpack_from('>III', buffer, index)

  # 重定位偏移量
  index += struct.calcsize('>III')
  for pos in range(bmp_num):
   img = struct.unpack_from('>784B', buffer, index)
   index += struct.calcsize('>784B')
   # 修改为原来的图片形状
   img = np.array(img, dtype=np.uint8)
   img = np.reshape(img, [bmp_height, bmp_width])
   # 显示图片
   cv2.imshow('bmp', img)
   # 按任意键继续
   cv2.waitKey(0)
   images.append(img)
 return images


writeBytesData()
readFromBytes()

在写入bytes文件的时候有点慢,由于有60000张图片每张要写28 * 28个字节,其中目录结构如下,需要图片的可以去我的下载区下载mnist图片数据集:

bytes.py
data
 bmp
  1.bmp
  2.bmp
  ...
  60000.bmp
 bytes

以上这篇python中struct模块之字节型数据的处理方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python对小数进行除法运算的正确方法示例
Aug 25 Python
跟老齐学Python之玩转字符串(1)
Sep 14 Python
深入浅析Python中list的复制及深拷贝与浅拷贝
Sep 03 Python
python输入多行字符串的方法总结
Jul 02 Python
Python turtle绘画象棋棋盘
Aug 21 Python
python 安装教程之Pycharm安装及配置字体主题,换行,自动更新
Mar 13 Python
Python 程序报错崩溃后如何倒回到崩溃的位置(推荐)
Jun 23 Python
属性与 @property 方法让你的python更高效
Sep 21 Python
Python和Bash结合在一起的方法
Nov 13 Python
pandas DataFrame.shift()函数的具体使用
May 24 Python
教你怎么用Python实现GIF动图的提取及合成
Jun 15 Python
Python基础之变量的相关知识总结
Jun 23 Python
Python的bit_length函数来二进制的位数方法
Aug 27 #Python
使用python将excel数据导入数据库过程详解
Aug 27 #Python
python读取指定字节长度的文本方法
Aug 27 #Python
Django中的cookie和session
Aug 27 #Python
Python3之字节串bytes与字节数组bytearray的使用详解
Aug 27 #Python
Python如何应用cx_Oracle获取oracle中的clob字段问题
Aug 27 #Python
在Python中获取操作系统的进程信息
Aug 27 #Python
You might like
PHP学习 运算符与运算符优先级
2008/06/15 PHP
简化php模板页面中分页代码的解析
2009/02/06 PHP
php封装单文件上传到数据库(路径)
2017/10/15 PHP
让复选框只能选择一项的方法
2013/10/08 Javascript
js中settimeout方法加参数的使用实例
2014/02/27 Javascript
node.js中的定时器nextTick()和setImmediate()区别分析
2014/11/26 Javascript
基于jquery ui的alert,confirm方案(支持换肤)
2015/04/03 Javascript
jQuery中extend()和fn.extend()方法详解
2015/06/03 Javascript
浅析jQuery Ajax请求参数和返回数据的处理
2016/02/24 Javascript
基于JS实现密码框(password)中显示文字提示功能代码
2016/05/27 Javascript
AngularJS 如何在控制台进行错误调试
2016/06/07 Javascript
全面解析Bootstrap中tooltip、popover的使用方法
2016/06/13 Javascript
阿里云ecs服务器中安装部署node.js的步骤
2016/10/08 Javascript
js图片加载效果实例代码(延迟加载+瀑布流加载)
2017/05/12 Javascript
vue 项目打包时样式及背景图片路径找不到的解决方式
2019/11/12 Javascript
详解javascript脚本何时会被执行
2021/02/05 Javascript
Python中的jquery PyQuery库使用小结
2014/05/13 Python
使用Python的urllib和urllib2模块制作爬虫的实例教程
2016/01/20 Python
python web基础之加载静态文件实例
2018/03/20 Python
tensorflow1.0学习之模型的保存与恢复(Saver)
2018/04/23 Python
对Python subprocess.Popen子进程管道阻塞详解
2018/10/29 Python
python实现五子棋人机对战游戏
2020/03/25 Python
Python3交互式shell ipython3安装及使用详解
2020/07/11 Python
用python写PDF转换器的实现
2020/10/29 Python
维德科技C#面试题笔试题
2015/12/09 面试题
网络维护中文求职信
2014/01/03 职场文书
高中考试作弊检讨书
2014/01/14 职场文书
党员目标管理责任书
2014/07/25 职场文书
运动会加油稿20字
2014/11/15 职场文书
保洁员岗位职责
2015/02/04 职场文书
销售经理工作检讨书
2015/02/19 职场文书
2015年度优秀员工获奖感言
2015/07/31 职场文书
2015年美容师个人工作总结
2015/10/14 职场文书
MySQL为id选择合适的数据类型
2021/06/07 MySQL
python迷宫问题深度优先遍历实例
2021/06/20 Python
MutationObserver在页面水印实现起到的作用详解
2022/07/07 Javascript