Python学习笔记(九)文件输入/输出

文件输入/输出

数据持久化最简单的类型是普通文件,有时也叫平面文件(flat file)。它仅仅是在一个文件 名下的字节流,把数据从一个文件读入内存,然后从内存写入文件。Python 很容易实现这 些文件操作,它模仿熟悉的和流行的 Unix 系统的操作。
读写一个文件之前需要打开它:
fileobj = open(filename,mode)
下面时对该open()调用的简单解释:

  • fileobj时open()返回的文件对象。
  • filename是该文件的字符串名
  • mode是指明文件类型和操作的字符串

mode 的第一个字母表示对其的操作。

  • r 表示读模式
  • w 表示写模式,如果文件不存在则新创建,如果存在则重写新内容
  • x 表示文件不存在的情况下新创建并写入文件
  • a 表示如果文件存在,在文件末尾追加写内容

mode 的第二个字母是文件类型1:

  • t (或者省略)代表文本类型
  • b 代表二进制文件

打开文件之后就可以调用函数来读写数据。
最后需要关闭文件。

接下来我们会在一个程序中用Python字符串创建一个文件,然后返回。


使用write()写文本文件

首先创建我们使用的文本源:
In [4]: poem = '''There was a young lady named Bright, 
   ...: Whose speed was far faster than light; 
   ...: She started one day 
   ...: In a relative way, 
   ...: And returned on the previous night.'''

In [5]: len(poem)
Out[5]: 154
将文本源添加到文件'relativity' 中:
In [6]: fout = open('relativity','wt')
In [7]: fout.write(poem)
In [8]: fout.close()

函数write()返回写入文件的字节数。和print()一样,它没有增加空格或换行符。同样,我们也可以在一个文本文件中使用print()。

In [15]: fout = open
In [16]: fout = open('relativity','wt')
In [17]: print(poem,file=fout)
In [18]: fout.close()

这就产生了一个问题:到底是使用 write() 还是 print() ? print() 默认会在每个参数后 面添加空格,在每行结束处添加换行。 在之前的例子中,它在文件 relativity 中默认添 加了一个换行。为了使 print() 与 write() 有同样的输出,传入下面两个参数。

  • sep分隔符:默认是一个空格 ' '
  • end结束字符:默认是一个换行符'\n'

除非自定义参数,否则print()会使用默认参数。在这里,我们通过空字符串替换print()添加对的所有多余输出:

In [19]: fout = open('relativity','wt',sep=' ',end=' ')
In [20]: print(poem,file=fout)
In [21]: fout.close()

上面的场景,打开文件就清晰很多,在未设置换行时候,最后一行是没有换行的。

如果源字符串非常大,可以将数据分块,直到所有字符被写入:

In [19]: fout = open('relativity','wt')
In [20]: size = len(poem)
In [21]: offset = 0 
In [22]: chunk = 100 

In [23]: while True:
    ...:     if offset > size:
    ...:         break
    ...:     fout.write(poem[offset:offset+chunk])
    ...:     offset += chunk
    ...:      
In [24]: fout.close()

第一次写入100个字符,然后写入剩下的50个字符。
如果文件 'relativity' 已经存在,使用模式 x 可以避免重写文件:

In [25]: fout = open('relativity','xt')
Traceback (most recent call last):   File "<stdin>", line 1, in <module> FileExistsError: [Errno 17] File exists: 'relativity'

可以添加一个异常处理:

In [27]: try:
    ...:     fout = open('relativity', 'xt')
    ...:     fout.write('stomp stomp stomp') 
    ...: except:
    ...:     print('relativity already exists!. That was a close one.') 
    ...:      
relativity already exists!. That was a close one.

使用read(),readline()或者readlines()读文本文件

我们可以使用不带参数的read()函数一次读入文件的所有内容。

In [17]: fin = open('relativity','rt')
In [18]: poem = fin.read()
In [19]: fin.close()

同样也可以设置最大的读入字符数限制 read() 函数一次返回的大小。下面一次读入 100 个 字符,然后把每一块拼接成原来的字符串 poem:

In [40]: fin = open('relativity','rt')
In [41]: while True:
    ...:     fragment = fin.read(chunk)
    ...:     if not fragment:
    ...:         break
    ...:     poem += fragment 
    ...:      
In [42]: fin.close()

读到文件结尾之后,再次调用 read() 会返回空字符串(''),if not fragment 条件被判为 False。此时会跳出 while True 的循环。 当然,我们也能使用 readline() 每次读入文件的一 行。在下面栗子中,通过追加每一行拼接成原来的字符串 poem:

In [5]: poem = '' 
In [6]: fin = open('relativity','rt')
In [7]: while True:
   ...:     line = fin.readline()
   ...:     if not line:
   ...:         break
   ...:     poem += line
   ...:      
In [8]: fin.close()

对于一个文本文件,即使空行也有1字符长度(换行字符 '\n'),自然就会返回True。当 文件读取结束后,readline()(类似 read())同样会返回空字符串,也被 while True 判 为 False。

In [9]: poem = ''
In [10]: fin = open('relativity','rt')
In [11]: for line in fin:
    ...:     poem += line 
    ...:      
In [12]: fin.close()

前面所有的栗子最终都返回单个字符串poem。函数readlines()调用时每次读取一行,并且返回单行字符串的列表:

In [19]: fin = open('relativity','rt')
In [20]: lines = fin.readlines()
In [21]: print(len(lines),'lines read')
5 lines read
In [22]: for line in lines:
    ...:     print(line,end='')
    ...:

使用write()写二进制文件

如果文件模式字符串中包含'b',那么文件会以二进制模式打开。这种情况下,读写的是字节而不是字符串。

我们首先生成一串二进制数据:
In [25]: bdata = bytes(range(0,256))
In [26]: len(bdata)
Out[26]: 256
以二进制模式打开,并且一次写入所有的数据:
In [27]: fout = open('bfile','wb')
In [28]: fout.write(bdata)
Out[28]: 256
write()返回写入的字节数
In [29]: fout.close()

对于文本文件,也可以分块写二进制数据。

In [30]: fout = open('bfile','wb')
In [31]: size = len(bdata)
In [32]: chunk = 100 
In [33]: offset = 0
In [34]: while True: 
    ...:     if offset > size:
    ...:         break
    ...:     fout.write(bdata[offset:offset+chunk])
    ...:     offset += chunk 
    ...:  
In [35]: fout.close()

使用read()读取二进制文件

下面的栗子只需要用'rb'打开文件即可:

In [38]: fin = open('bfile','rb')
In [39]: bdata = fin.read()
In [40]: len(bdata)
Out[40]: 256

In [41]: fin.close()

使用with自动关闭文件

如果你忘记关闭已经打开的一个文件, 在该文件对象不再被引用之后 Python 会关掉此文 件。这也就意味着在一个函数中打开文件,没有及时关闭它,但是在函数结束时会被关 掉。然而你可能会在一直运行中的函数或者程序的主要部分打开一个文件,应该强制剩下 的所有写操作完成后再关闭文件。
Python的上下文管理器(context manager)会清理一些资源,例如打开的文件。它的形式为 with expression as variable:

In [42]: with open('withfile','wt') as fout: 
    ...:     fout.write(poem)
    ...:      

完成上下文管理器的代码后,文件会自动关闭


使用seek()改变位置

无论是读或者写文件,Python都会跟踪文件中的位置。函数tell()返回距离文件开始处的字节偏移量。函数seek()允许跳转到其他字节偏移量的位置。这意味着可以不用从头读取文件的每一个字节,直接跳转到最后位置并只读一个字节也是可以的。
下面就是我们的栗子:

使用之前的二进制文件bfile
In [43]: fin = open('bfile','rb')
In [44]: fin.tell()
Out[44]: 0
使用 seek() 读取文件结束前最后一个字节:
In [45]: fin.seek(255)
Out[45]: 255
一直读到文件结束:
In [46]: bdata = fin.read()
In [47]: len(bdata)
Out[47]: 1

In [48]: bdata[0]
Out[48]: 255

调用seek()函数的时候,可以使用两个参数:seek(offset,origin)。

  • 如果origin等于(默认为0),从开头便宜offset个字节
  • 如果origin等于1,从当前位置偏移offset个字节
  • 如果origin等于2,距离最后结束处偏移offset个字节

这些值也在标准os模块中被定义:

In [1]: import os 
In [2]: os.SEEK_SET
Out[2]: 0

In [3]: os.SEEK_CUR
Out[3]: 1

In [4]: os.SEEK_END
Out[4]: 2

我们可以用不同的方法读取最后一个字节:

In [9]: fin = open('bfile','rb')
结尾前的一个字节:
In [10]: fin.seek(-1,2)
Out[10]: 255

In [11]: fin.tell()
Out[11]: 255
在调用seek()函数时不需要额外调用tell()。上面只是说明两个函数具有相同的偏移量
一直读到结尾:
In [12]: bdata = fin.read()
In [13]: len(bdata)
Out[13]: 1

In [14]: bdata[0]
Out[14]: 255

下面时从文件的当前位置寻找的栗子:

In [15]: fin = open('bfile','rb')
下面的栗子返回最后两个字节:
In [16]: fin.seek(254,0)
Out[16]: 254

In [17]: fin.tell()
Out[17]: 254

在此基础上前进一个字节:

In [18]: fin.seek(1,1)
Out[18]: 255

In [19]: fin.tell()
Out[19]: 255
最后一直读到文件结尾:
In [20]: bdata = fin.read()
In [21]: len(bdata)
Out[21]: 1

In [22]: bdata[0]
Out[22]: 255

这些函数对于二进制文件都是极其重要的。当文件是 ASCII 编码(每个字符一个字节) 时,也可以使用它们,但是计算偏移量会是一件麻烦事。其实,这些都取决于文本的编码 格式,最流行的编码格式(例如 UTF-8)每个字符的字节数都不尽相同。

注:本文内容来自《Python语言及其应用》欢迎购买原书阅读

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容

  • PHP常用函数大全 usleep() 函数延迟代码执行若干微秒。 unpack() 函数从二进制字符串对数据进行解...
    上街买菜丶迷倒老太阅读 1,369评论 0 20
  • php usleep() 函数延迟代码执行若干微秒。 unpack() 函数从二进制字符串对数据进行解包。 uni...
    思梦PHP阅读 1,984评论 1 24
  • 在宿舍呆了一天,感觉甚是无聊。拿上几本书,准备去教室自习,今天出乎意料的冷,小风一阵阵的,此时我突然想起了昨天给家...
    啦啦啦的哥欠阅读 211评论 0 1
  • 这个气候和温度,是南京的秋天吧。口舌干燥,喉咙痒痛,这种感觉已然持续几日了, 干燥的风吹得人脸上有轻微的割痛感,吹...
    軟語華阅读 182评论 0 0
  • 《The end of love》 goodbye my love 再见,我的爱人 you have been t...
    may徊阅读 206评论 0 0