读取文件
假设你已经在某个文件夹下创建了 "test.txt" 文件, 且里面有一些内容, 那你在当前位置输入 Python3, 进入到交互模式, 然后执行下面的操作:
- >>> f = open('test.txt')
- >>> for line in f:
- ... print(line)
- ...
- My name is Rocky
- I love Python
这里提醒大家注意一下, 如果是在该文件所在的位置启动的 Python 交互模式, 那么按照上面的方法 open('test.txt') 打开文件, 这意味着 test.txt 是在当前文件夹下的, 如果要打开其它文件夹下的文件, 要使用相对路径或者绝对路径来表示, 从而让 Python 能找到那个文件.
看上面的例子, open() 一个文件, 即生成了一个对象, 把这个对象赋值给变量 f, 从而让变量 f 和文件对象之间建立了引用关系, 接下来用 for 循环读取文件中的内容, 把读到的文件中的每行赋值给变量 line (这里的每行可以看作是对象), 从打印的结果看, 每一行与你读取的文件的每一行是相同的.
如果你做完了上述操作, 那么请看下面的操作:
- >>> for line1 in f:
- ... print(line1)
- ...
- >>>
你会奇怪的发现, 竟然什么也没有, 是不是出错了? 其实并没有, 因为之前已经读取过一次文件的内容了, 并且到了文件的末尾, 再重复操作, 就要从文章的末尾开始读了, 当然就没有什么东西了, 在 Python 中并不会认为这是错误. 如果你想再次读取的话, 请重新 open() 一下文件.
创建文件
读文件只是针对文件的操作之一, 还有创建文件.
在上面读文件的时候, 我们打开的是一个已经存在的文件, 那么如何创建一个新文件呢? 请看下面的操作:
- >>> new_file = open('new.txt','w')
- >>> new_file.write('this is a new file')
- 18
- >>> new_file.close()
new_file = open('new.txt','w') 意味着在当前的目录下创建了一个名为 new_file 的文件, 该文件为 "w" 打开模式.
new_file.write('this is a new file') 则是向已建立的新文件中写入 "this is a new file", 并且返回的是写入字符串的长度.
new_file.close() 则是关闭当前文件, 然后将写入的话保存到文件中.
由上面的例子我们可以看出, 创建文件我们同样用的是 open() , 但是多了个 "w" , 这是告诉 Python 用什么模式打开文件. 在 Python 中, 可以用很多不同的模式打开文件, 请看下图(截图来自菜鸟教程):
从上图中可以看出, 不同的模式下打开文件可以进行不同的读写操作, 如果什么都不写的话, 默认为以只读 (r) 的方式打开文件.
使用 with 自动关闭文件
在前面的操作中我们可以看到, 在对文件进行写操作之后, 要执行关闭文件的操作, 执行关闭文件的操作是为了将写入的内容保存到文件中, 如果不进行 close() 操作的话, 那么新写入的内容将不会被保存.
Python 早就知道会有很多马大哈们会忘记 close()的操作, 所以它创建了一种简单的方法, 这就是 with, 它会实现自动关闭文件, 看! Python 是多么的人性化! 请看下面的操作:
- >>> new_file.close()
- >>> with open('new.txt','a') as f:
- ... f.write('\nwith is good good good.')
- ...
- 23
在这里 with 其实是要发起一个语句的, 这里兼具了后面我会讲的 try/finally, 即可以在遇到异常的时候发出提醒, 此处暂时先不讲, 等以后我再细说, 我们先学会用 with.
- >>> with open('new.txt') as f:
- ... print(f.read())
- ...
- this is a new file
- with is good good good.
看吧, 在 with 中我们可以放轻松的扔掉 close 了, 上面例子中用到的 read() 方法, 在明天的文章中我会讲到.
文件的属性
很多时候, 我们需要获取一个文件的有关属性, 比如文件的创建日期, 修改日期等等, 在 Python 中有一个专门针对时间设计的模块 -- time. 请看下面的操作:
- >>> import time
- >>> time.localtime()
- time.struct_time(tm_year=2018, tm_mon=7, tm_mday=25, tm_hour=21, tm_min=49, tm_sec=32, tm_wday=2, tm_yday=206, tm_isdst=0)
其实还有一种办法可以查看文件的这些属性, 就是 os 模块里的 stat, 在这里我就是提一嘴, 之后讲到 os 模块的时候再仔细说.
读取文件的内容
因为文件的对象是可迭代的, 所以能够用 open() 打开文件, 所以用 for 循环可以将文件的内容读出来. 我在前面的文章说过, 可以用 dir() 查看文件对象的属性和方法, 当你看了以后你会发现有 3 个方法 read / readline / readlines, 单单从名称上看, 它们应该和读有关系, 事实上确实是这样的, 但是它们 3 个又有些微的差别.
1.read()
文件对象的 read() 方法, 其实完整的写出来其实是 read( size ), 只不过里面的参数可以省略, 如果不省略, 则读取文件中的 size 个字符并返回一个字符串; 如果省略的话, 则读取文件对象的字符知道 EOF,EOF == End - of - file.
- >>> f = open('new.txt')
- >>> f.read(10)
- 'this is a'
- >>> f.read()
- 'new filewith is good good good.'
如果你是按照上述的例子依次进行操作的, 就会在 f.read() 后出现上述的结果, 这主要是因为在前面已经 read(12) 了, 指针已经移动到了第 12 个字符后面.
2.readline() & readlines()
readline() 就是它表面的意思, 逐行读取文件的内容.
- >>> f = open('new.txt')
- >>> f.readline()
- 'this is a new filewith is good good good.'
- >>> f.readline()
- ''
每次执行 readline() 的时候它只读一行, 直到最后一行, 如果还执行 readline() 的话, 它不会报错, 返回的是空字符串.
同样也是有 readline(size) 的, 如果给 readline(size) 参数, 则读取相应行的 size 个字符, 有兴趣的可以自己试一下.
还有一个是 readlines(), 它的作用是将文件中各行读出来, 放到一个列表中返回.
- >>> f = open('test.txt')
- >>> f.readlines()
- ['My name is Rocky\n', 'I love Python']
既然返回的是一个列表, 那么就能用 for 循环读取列表元素, 再观察一下可以发现, 列表中的每个元素都是文件的一行, 并且是字符串.
- >>> f = open('test.txt')
- >>> for line in f.readlines():
- ... print(line)
- ...
- My name is Rocky
- I love Python
这个是不是让你觉得和上面文件的 for 循环很类似?
- >>> f = open('test.txt')
- >>> for line in f:
- ... print(line)
- ...
- My name is Rocky
- I love Python
乍一看两种方式好像没有什么区别, 其实这两种方式是不同的. 在 for line in f 中, 并没有将文件中所以的行都读入内存, 而 for line in f.readlines() 中先执行了 f.readlines(), 在内存中有一个列表, 列表中包含了所有文件的行, 这就是两种方式的区别.
大文件的读取
上面的三个读取文件内容的方法 read 和 readlines 都是一次性将全部的内容读入内存, 如果文件不是很大的话, 这种做法能够保证读取的速度, 但是如果文件内容很大, 大到差不多内存那么大或者更大的时候, 就不能这么做了. 但是 Python 早就替你考虑到了, Python 中有一个 fileinput 模块, 可以使用它来操作.
- >>> import fileinput
- >>> for line in fileinput.input('test.txt'):
- ... print(line,end = '')
- ...
- My name is Rocky
- I love Python
因为我没有大的文件, 只是为了演示一下这个模块的用法, 对于这个模块更多的内容, 可以在交互模式下用 dir() 去查看.
seek
不知道你有没有奇怪过在之前的演示中, 每次都要做 f = open('***') 类似的操作, 否则就会出现下面的情况:
- >>> f = open('test.txt')
- >>> for line in f:
- ... print(line)
- ...
- My name is Rocky
- I love Python
- >>> for line in f:
- ... print(line)
- ...
- >>>
是不是发现, 当我们第二次循环文件的时候, 既没有报错, 也没有显示文件的内容, 类似的现象在前面的 readline 中也出现过, 这是因为读取文件的时候, 有指针随着运动, 当读取结束时, 指针就到了相应的位置.
当指针结束运动时, 可以使用 tell() 告诉我们当前指针的位置.
- >>> f = open('test.txt')
- >>> f.readline()
- 'My name is Rocky\n'
- >>> f.tell()
- 17
现在我们来看 seek() 的能力, 它能够根据偏移量来移动指针.
>>> f.seek(0)
0
上面的意思是将指针移动到文件的开始, 如果用 f.readline() 读取的话, 现在输出的应该是文件的第一行:
- >>> f.readline()
- 'My name is Rocky\n'
其实还可以操纵指针到任何一个位置, 请看下面的操作:
- >>> f.seek(10)
- 10
- >>> f.tell()
- 10
f.seek(10) 就是将位置定位到从开头算起到第 10 个字符后面, 这时候如果使用 readline 的话, 读取的是从当前位置到行末的字母.
写在之后
更多内容, 欢迎关注公众号「Python 空间」, 期待和你的交流.
来源: https://juejin.im/post/5c2eed126fb9a049cd545514