如何读写文本文件


python2中,默认编码是ascii编码,这种编码不能直接存储物理硬件(磁盘的扇区、网络的socket)中,需要转换成string(由连续的字节组成)


从错误提示可以看出,字符串s不是unicode编码是而是ascii编码,不能再进行编码。

创建unicode编码字符串 在字符串前加’u’


在python中只使用内部的unicode表示字符,编解码要使用统一格式 ,否则会乱码


Py2中写入文件时要把unicode编码格式编码,从文件读出后,也要把读出的按统一格式解码。

Py2文件读写


在py3中的str 变成了unicode(真正意义上的连续字符串),在py3 表示byte需要在字符串前加个b

在py2中需要加个u''表示unicode字符串,而py3中默认就是unicode(也就是str),不需要添加。

在py3中open函数功能更强大,可以使用encoding指定编码格式

info = '你好'#open通过encoding参数自动编码成utf8
with open('H3','wt',encoding='utf8') as f: # t可以不写 python3 默认文本模式打开
f.write(info)
#读取文件的时候,自动解码成utf8,不要和上面存入时的编码搞混,文件里的数据显示的是连续的字节
with open('H3','rt',encoding='utf8') as f2:
print(f2.read())

如何处理二进制文件

实际案例

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操作的次数, 文件通常使用缓冲区(有足够多的数据才进行系统调用)。 文件的缓冲行为,分为

'全缓冲', '行缓冲', '无缓冲'全缓冲

缓冲区满了 才将内容写入文件

全缓冲的大小和设备有关,一般一个块为4096个字节,当超过4096时才会输出到文件上。

# 全缓冲
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()

如何访问文件的状态

在某些项目中, 我们需要获得文件的状态, 列如:

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

TemporaryFile

由TemporaryFile创建的文件,不能由绝对路径找到,只能通过文件对象找到访问

from tempfile import TemporaryFile, NamedTemporaryFile
#创建一个临时文件可以指定临时文件 放在哪个盘/目录 tf = TemporaryFile( dir='/tmp/')
f = TemporaryFile()
#将数据写入这个临时文件,
f.write(b'abcdef'*10000)
f.seek(0)
print( f.read(100)) #读数据时一部分一部分读取
# 删除临时文件
f.close()


NamedTemporaryFile

由NamedTemporaryFile创建的文件,可以由绝对路径找到。临时文件关闭后也会自动删除

from tempfile import TemporaryFile, NamedTemporaryFile
# 如若使临时文件关闭后不自动删除 可以修改delete参数指定 为false
# ntf = NamedTemporaryFile(delete = False)
ntf = NamedTemporaryFile()
# 此属性描述了在文件系统下的路径
print()