一. 如何读写文本文件
实际案列
某文本文件编码格式(如UTF-8, GBK, BIG5),
在python2和python3 中分别如何读取该文件?
原理
字符串的语义变化
python2 python3
--------------------------
str -> bytes
unicode -> str
- python2: 写入文件前对 unicode 编码, 读入文件后对 字节 进行解码
- python3: open函数指定't'的文本模式, encoding指定编码格式
读写代码
# python2 先encode编码,再写入, 读取后还要解码
s = u'我是python2'
type(s)
# 结果<type 'unicode>
f = open('a.txt', 'w')
f.write(s.encode('utf8'))
f.flush() # 从内存中放入磁盘中 这里可以用f.close()
f = open('a.txt')
txt = f.read() # '\xe6\x88....... 此时是字节
txt.decode('utf8') # u'\xe6\x88..... 此时是unicode 可以print到 我是python2
# python3
s = '我是python3'
f = open('b.txt', 'wt', encoding='gbk') # t可以不写 python3 默认文本模式打开
f.write(s)
f.flush()
f = open('b.txt', encoding='gbk')
txt = f.read() # 直接读取
print(txt) # 我是python3
print(type(txt)) # <class 'str'> 在python3 中即 unicode
二. 如何处理二进制文件
实际案例
wav 是一种音频文件的格式, 音频文件为二进制文件。
wav 文件由头部信息和音频采样数据构成。 前面为头部信息, 包括声道数, 采样频率, 编码位宽等等, 后面是音频采样数据
使用python, 分析一个 wav 文件头部信息, 处理音频数据。
解决方案
- open函数以二进制文件打开, 指定mode 参数为b
- 二进制数据可以用readinto, 读入到提前分配好的buffer中
- 解析二进制数据可以使用标准库中的struct模块的unpack方法
代码
import struct
def find_subchunk(f, chunk_name):
f.seek(12) # 从头 跳过12个字节
while True:
name = f.read(4) # 读取4个字节
chunk_size, = struct.unpack('i', f.read(4)) # i 即int 解析4个字节 h 解析两个字节
print(name)
if name == chunk_name:
return f.tell(), chunk_size # f.tell当前文件的指针位置
f.seek(chunk_size, 1) # 1 表示当前 0 表示从头 为默认
f = open('demo.wav', 'rb') # 二进制文件 带b
offset, size = find_subchunk(f, b'data')
import numpy as np
buf = np.zeros(size // 2, dtype=np.short) # array([0,0,0,0,...0], dtype=int16)
f.readinto(buf)
buf //= 8 # 除8 获得整数结果 获取声音小的声音
f2 = open('out.wav', 'wb')
f.seek(0)
info = f.read(offset)
f2.write(info)
buf.tofile(f2)
f2.clode()
三. 如何设置文件的缓冲
实际案列
将文件内容写入到硬件设备时, 使用系统调用, 这类IO操作的时间很长。 为例减少IO
操作的次数, 文件通常使用缓冲区(有足够多的数据才进行系统调用)。 文件的缓冲行为,分为
'全缓冲', '行缓冲', '无缓冲'
如何设置python 中文件对象的 缓冲行为?
全缓冲
- 缓冲区满了 才将内容写入文件
# 全缓冲
f = open('a.bin', wb)
f.write(b'abc') # 此时 打开a.bin文件 发现什么都没有, 因为现在数据 在缓冲区中
f.write(b'efg') # 此时 缓冲区没有填满, 数据依然在缓冲区中
f.write(b'1'*(4096-6)) # 假设 磁盘的缓冲区大小为 4096 此时打开 a.bin 发现有内容了
# 文本模式下
f2 = open('a.txt', 'w')
f2.write('a'*4095) # 此时a.txt没有数据
f2.write('bc') # 超过4096 字节了 a.txt依然没有内容 为什么呢?
# 字节流wb 方式 打开 时, 数据 -> B(4096字节缓冲区 encode和decode) -> Raw(无缓冲 可以直接写入文件)
# 文本模式 打开 , 数据 -> TextIO(8192字节缓冲区) -> B -> Raw
f2.write('2'*10000) # 此时 a.txt 有内容了
指定缓冲区大小
f = open('a.bin', 'wb', buffering=8192)
f.write('........')
行缓冲
- 遇到 换行符/n 就将缓冲区的内容写入 文件, 遇不到换行符 则和全缓冲一样。
- linux 的shell终端 就是 行缓冲的
- 只能在 文本模式中 使用
# 设置为行缓冲
f = open('a.bin', 'wb', buffering=1)
f.write('huanghuanchong')
无缓冲
# 无缓冲方法
#方法一
f.raw.write('aaaaaaaaa')
# 方法二
f = open('a.bin', 'wb', buffering=0)
f.write('wuhuanchong')
四. 如何将文件映射到内存
实际案列
1. 在访问某些二进制文件时, 希望能把文件映射到内存中, 可以实现随机访问('framebuffer设备文件')
2. 某些嵌入式设备, 寄存器被编址到内存地址空间, 我们可以映射 '/dev/mem' 某范围, 去访问这些寄存器
3. 如果多个进程映射同一个文件, 还能实现进程通讯的目的
解决方案
使用标准库 'mmap.mmap()函数', 将文件映射到 进程的内存地址空间
设置屏幕颜色
import mmap
# 屏幕设备文件
f = open('/dev/fb0', 'r+b')
size = 8294400
m = mmap.mmap(f.fileno(), size)
# f.fileno 得到文件描述符 , os.open获取的文件描述符一样
# size如果为0 表示 文件的全部放入内存, 这里是特殊设备文件需要计算大小用0可能会出错
# m 有 如 m.write(b'abc) 类似文件的 一些操作
m[:size//2] = b'\xff\xff\xff\x00' * (size // 4 // 2)
# 把一般的屏幕变为白色 和透明 一个字节是8位 \xff 正好一个字节
m.close()
f.close()
# linux 下 ctrl +alt +F1 切换到终端模式, 运行该程序 屏幕一半才会变为白色
五. 如何访问文件的状态
实际案列
在某些项目中, 我们需要获得文件的状态, 列如:
1. 文件的类型('普通文件、目录、符号链接、设备文件...')
2. 文件的访问权限
3. 文件的最后的访问/ 修改/ 节点 状态更改时间
4. 普通文件的大小
解决方案
-
系统调用: 标准库 'os模块'中的系统调用 'stat' 获取文件状态
import os
# fd = os.open('b.py', os.O_RDONLY) # 只读方式打开, 得到文件描述符 实际上就是一个数字 如23
# os.read(fd, 10) # 文件描述符是给 系统调用的 方法用的, 读10个字节
s = os.stat('5_2.py') # 返回状态结果对象 可以传入文件, 也可以传入 文件夹
s = os.lstat('5_2.py') # 不会跟随 符号链接 比如a 是b 的符号链接(软链接) 不加l 参数a会自动变为b
'''
os.stat_result(st_mode=33261, st_ino=13242808, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20,
st_size=349, st_atime=1539776238, st_mtime=1531978808, st_ctime=1534324393)
st_mode 文件类型 和权限 如 lrwxrwxrwx
st_ino 文件系统inode 节点的索引
st_dev 当前文件所在设备 当前磁盘的分区
st_nlink 硬链接数1 表示文件本身 shell命令 :ln a.txt b.txt 创建a的硬连接b, a的硬连接会由1变为2
st_uid 文件所有者id
st_gid 文件所有者所在组id
st_size 文件大小 字节
st_atime 最近访问时间
st_mtime 最近修改时间
st_ctime 创建时间
'''
# 目前的 状态结果对象 不容易观察, 需要与特定的掩码 做与操作, python给我们定义好了
# ----------------处理st_mode---------------------
import stat
stat.S_IFDIR & s.st_mode # 0 不是目录
stat.S_IFCHR & s.st_mode # 0 不是char设备
stat.S_IFREG & s.st_mode # 32768得到数字 是普通文件
# 或者
stat.S_ISREG(s.st_mode) # true 是普通文件
stat.S_ISLNK(s.st_mode) # false 不是符号链接文件
stat.S_IRUSR & s.st_mode # 236 是否用户刻度
stat.S_IXOTH & s.st_mode # 是否用户可执行
# ----------------处理st_atime 等时间对象---------------------
import time
time.localtime(s.st_atime)
-
快捷函数: 标准库 'os.path' 下的一些函数, 使用起来更加简洁
os.path.isfile('5_3.py') # 是否是文件
os.path.isdir('5_3.py') # 是否是目录
os.path.getatime('5_3.py') # 获取访问时间
os.path.getsize('5_3.py') # 获取文件大小
#...........
六. 如何使用临时文件
实际案例
某项目中, 我们从传感器采集数据, 没收集到1G 数据后, 做数据分析, 最终只保存分析结果。
这样很大的临时数据如果常驻内存, 将消耗大量内存资源, 我们可以使用临时文件存储这些临时数据('外部存储')
临时文件不用命名, 且关闭后会自动被删除
解决方案
-
使用标准库中的 TemporaryFile 以及 NamedTemporaryFile
-
一般使用TemporaryFile即可
-
当多进程 要访问同一个临时文件时, 可以使用命名的临时文件NamedTemporaryFile
from tempfile import TemporaryFile, NamedTemporaryFile
# 创建临时文件, 操作系统级别的 , 可以用os.open等命令完成
tf = TemporaryFile()
# 可以指定临时文件 放在哪个盘/目录 tf = TemporaryFile( dir='/tmp/')
tf.write(b'*' * 1024 * 1024) # 写入磁盘
tf.write(b'*' * 1024 * 1024)
tf.write(b'*' * 1024 * 1024)
tf.write(b'*' * 1024 * 1024)
tf.seek(0)
tf.read(512)
# 删除临时文件
tf.close()
# 带名字的临时文件, 非操作系统级别的
ntf = NamedTemporaryFile()
ntf.name # '/tmp/tempae7x06w7'
# 可以使用python库中的函数查看文件名和前缀
import tempfile
temfile.gettempdir()
tempfile.gettempprefix()
# 删除临时文件
ntf.close()
# 在NamedTemporaryFile() 传入 delete=False, close时就不会自动删除