文件有两种,文本文件和二进制文件。读写文本文件比较简单,也在这里简单说一下;读写二进制文件用到了struct库,涉及一些大端小端、字节填充等概念,稍微有点复杂。
在读写文件之前,需要打开文件,Python不需要导入其他库,直接可以打开关闭文件
file = open('filename.ext', openmod) #打开文件
file.close() #关闭文件
操作模式可以分为两种,一种是文本文件模式,另一种为二进制文件模式
文件写入就比较简单了
file.write('string')
file.write('string\n') # 换行
file.write('%10s=%10.2f\n' % ('val', 12.3455)) # 格式化字符串
write函数只能输出字符串,不能像C语言那样定义输出格式。 如果像这么做,需要先对要输出的内容转换为字符串。可以像上例那样格式化字符串,格式化方法与C中的是一致的,适合学过C语言的同学。
Python也有一个输出多行的函数,同时输出多行
content = ['I have a dream.\n', 'One day, \n', 'former slaves and slave owners will join together,\n',
'as brotherhood.\n']
file.writelines(content)
writelines()函数不会为你添加换行符,需要自己添加。
读入文件内容
file.read() # 读取文件所有内容
file.read(n) # 读取前n个字符
需要注意的是read(n)。如果文件中有中文,一个中文字表示一个字符,不管其中内部编码是几个字节。读取完之后,光标移动n个字符。
顺便讲一讲如何读取中文。如果要正确读取中文,以下条件要满足
要读的文本文件的编码与打开文件时设置的编码一致。
其实我们在打开文件时,有一个文件编码参数我们没有设置,用的默认的参数,这个参数可以设置。假如我们的文本文件a.txt用的utf-8进行的编码。就可以进行下面设置。
file = open('a.txt', 'r', encoding='utf-8')
如果两者没有设置成一致的,在读取时就会出错。 在Windows中, open方法打开的文件好像默认为GBK编码,如果你的文本文件用的utf-8格式,就会错误。
除了上面的读取函数之外,还有一个
line1 = file.readline()
str2 = file.readline(n) #读取本行的n个字符
注意line1中包含回车符,读取完line1后,读取位置移动到下一行
读取完str2后,读取位置移动n个字符,可能还在本行。
如果n >= 本行的字符数(包含换行符), 最终读取的内容就是本行的字符,读取位置移到下一行;
如果n <本行的字符数(包含换行符), 最终读取的内容长度为n, 读取位置移动n个字符,读取位置还在本行。
感觉说的有点啰嗦,就这样吧。
二进制文件中都是一个一个的字节数据。因此读和写,都是针对字节数据的。
bytes_all = file.read() #读取文件所有的字节数据
bytes_read = file.read(n) #读取n个字节数据
file.write(bytes_read) #将字节数据写到文件
下面就是讲如何把数据转换为字节数据,以及如何把字节数据解析成原始数据。
这里用到了Python提供了一个struct库。
在使用之前先导入struct库
import struct
struct库有两个基本操作
struct.pack(fmt, v1, v2, ...) #将原始数据v1, v2, ...根据fmt格式转换为字节串
val_arr = struct.unpack(fmt, bytes_arr) #将字节数据byts_arr根据fmt格式转换为原始数据元组 val_arr
下面是一个简单的例子
b1 = struct.pack('>hif', 65, 123, 3.14)
print([hex(i) for i in b1])
输出
['0x0', '0x41', '0x0', '0x0', '0x0', '0x7b', '0x40', '0x48', '0xf5', '0xc3']
这里稍作解释,后面会有更详细的。
这里的fmt为’>hif’ 表示数据为大端顺序,h表示一个short类型,也就是2个字节的整数,i表示一个int类型也就是4个字节表示的整数, f表示单精度浮点类型,也是4个字节。 因此这个fmt表示的10个字节的数据。
可以用下面计算fmt代表的字节数
struct.calcsize('>hif')
然后是把字节解析为原始数据
struct.unpack('>hif', b1)
输出
(65, 123, 3.140000104904175)
在fmt的定义中,有3样东西需要注意:字节顺序,数据由几个字节组成,以及字节填充(对齐)。
字节顺序:
是对于多字节的数据,比如一个int类型的数16909060(其十六进制表示为 0x01020304),转换为字节有两种转换方法
大端表示,最高位在前, 0x01, 0x02, 0x03, 0x04
小端表示,最低位在前, 0x04, 0x03, 0x02, 0x01
[hex(i) for i in struct.pack('>i', 16909060)]
Out[69]: ['0x1', '0x2', '0x3', '0x4']
[hex(i) for i in struct.pack(', 16909060)]
Out[70]: ['0x4', '0x3', '0x2', '0x1']
数据长度,这个大家应该都知道。下面是完整的数据长度列表
字节填充(对齐),这是以前C语言中的一个概念。比如有一个100个长度的int类型的数组,为了能够找到第61个数据,我只需要把指针移动60个int长度就行了,因为这100个数都是4字节对齐的。
在Python中,对齐稍微有一点区别。比如h表示的是short,是2字节的。如果启用了对齐设置,那么在任何short类型放置的位置,都需要2字节对齐。如果是i,也就是int类型的数据,就需要4字节对齐。如果是d,也就是double,就需要8字节对齐。放在最前面的数据不需要对齐,始终是从第0个字节开始。当然也可以不用对齐设置,那么数据就是连续排列的。
像刚刚的’>'就定义了这三样东西:大端字节顺序(big-endian),标准的数据字节数,无字节填充(对齐)。 完成的参数见下表:
默认是@,native表示配置方法跟本机环境有关。不同类型的CPU,操作系统,配置不一样。有些CPU可能是采用大端、有些事小端;有些操作系统下int表示4个字节,有些表示8个字节;有些要求对齐,有些不要求对齐。size设置为standard,表示数据类型的长度不随着本地环境而改变。
可以通过下面的方法,查看本地的字节顺序
import sys
print(sys.byteorder)
我的电脑处理器i7, 系统win10。 显示的little, 也就是小端模式
后面还有几个例子,来说明x,c, s类型。
x表示填充字节。如果首字符设置的是不填充,那么一个x代表一个字节。pack中不需要有数据与之对应。
[hex(i) for i in struct.pack(',16909060)]
Out[76]: ['0x0', '0x0', '0x4', '0x3', '0x2', '0x1']
如果首字符设置的是填充,并且在x的位置本来就有填充,那么x就不起作用,除非x的个数超过了本来要填充的数目。
[hex(i) for i in struct.pack('xxi',16909060)] #x不起作用,因为本身要填充3个字节
输出:
['0x2a', '0x0', '0x0', '0x0', '0x4', '0x3', '0x2', '0x1']
[hex(i) for i in struct.pack('cxxxxi',b'*', 16909060)] #x起作用,x数超过了3个字节,int对齐到下下一个4字节了
输出:
['0x2a',
'0x0',
'0x0',
'0x0',
'0x0',
'0x0',
'0x0',
'0x0',
'0x4',
'0x3',
'0x2',
'0x1']
c表示的是一个char,其实只能用字节数据,并且字节长度只能是1。 但是可以在c前面加数字,避免连续写几个c(也适用x)。
[hex(i) for i in struct.pack('<3ci', b'a', b'b', b'c',16909060)]
Out[75]: ['0x61', '0x62', '0x63', '0x4', '0x3', '0x2', '0x1']
s表示的是字符串,其实也是只能用字节数据,不过字节长度可以大于1。在s前面加数字,表示这个串的长度。如果后面字节数目不够,就会填充0;如果后面字节数过长,就会截取。
[hex(i) for i in struct.pack('<1si', b'abc',16909060)]
Out[90]: ['0x61', '0x4', '0x3', '0x2', '0x1']
[hex(i) for i in struct.pack('<10si', b'abc',16909060)]
Out[89]:
['0x61',
'0x62',
'0x63',
'0x0',
'0x0',
'0x0',
'0x0',
'0x0',
'0x0',
'0x0',
'0x4',
'0x3',
'0x2',
'0x1']
哎,写了这么长,累死了。