您当前的位置:首页 > 计算机 > 编程开发 > Python

还在用open读文件?那你就out了!这个库比open好用100倍

时间:05-25来源:作者:点击数:

使用 open 函数去读取文件,似乎是所有 Python 工程师的共识。

今天我给大家推荐一个比 open 更好用、更优雅地读取文件方法 -- 使用 fileinput

fileinput 是 Python 的内置模块,但我相信,不少人对它都是陌生的。今天我把 fileinput 的所有的用法、功能进行详细的讲解,并列举了一些非常实用的案例,对于理解和使用它可以说完全没有问题。

1. 从标准输入中读取

当你的 Python 脚本没有传入任何参数时,fileinput 默认会以 stdin 作为输入源

效果如下,不管你输入什么,程序会自动读取并再打印一次,像个复读机似的。

2. 单独打开一个文件

脚本的内容如下

其中 a.txt 的内容如下

helloworld

执行后就会输出如下

需要说明的一点是,fileinput.input() 默认使用 mode='r' 的模式读取文件,如果你的文件是二进制的,可以使用mode='rb' 模式。fileinput 有且仅有这两种读取模式。

3. 批量打开多个文件

从上面的例子也可以看到,我在 fileinput.input 函数中传入了 files 参数,它接收一个包含多个文件名的列表或元组,传入一个就是读取一个文件,传入多件就是读取多个文件。

i

a.txt 和 b.txt 的内容分别是

运行后输出结果如下,由于 a.txt 和 b.txt 的内容被整合成一个文件对象 file ,因此 fileinput.lineno() 只有在读取一个文件时,才是原文件中真实的行号。

如果想要在读取多个文件的时候,也能读取原文件的真实行号,可以使用 fileinput.filelineno() 方法

运行后,输出如下

这个用法和 glob 模块简直是绝配

运行效果如下

4. 读取的同时备份文件

fileinput.input 有一个 backup 参数,你可以指定备份的后缀名,比如 .bak

运行的结果如下,会多出一个 a.txt.bak 文件

5. 标准输出重定向替换

fileinput.input 有一个 inplace 参数,表示是否将标准输出的结果写回文件,默认不取代

请看如下一段测试代码

运行后,会发现在 for 循环体内的 print 内容会写回到原文件中了。而在 for 循环体外的 print 则没有变化。

利用这个机制,可以很容易地实现文本替换。

附:如何实现 DOS 和 UNIX 格式互换以供程序测试,使用 vim 输入如下指令即可

DOS转UNIX::setfileformat=unixUNIX转DOS::setfileformat=dos

6. 不得不介绍的方法

如果只是想要 fileinput 当做是替代 open 读取文件的工具,那么以上的内容足以满足你的要求。

  • fileinput.filenam() 返回当前被读取的文件名。 在第一行被读取之前,返回 None。
  • fileinput.fileno() 返回以整数表示的当前文件“文件描述符”。 当未打开文件时(处在第一行和文件之间),返回 -1。
  • fileinput.lineno() 返回已被读取的累计行号。 在第一行被读取之前,返回 0。 在最后一个文件的最后一行被读取之后,返回该行的行号。
  • fileinput.filelineno() 返回当前文件中的行号。 在第一行被读取之前,返回 0。 在最后一个文件的最后一行被读取之后,返回此文件中该行的行号。

但若要想基于 fileinput 来做一些更加复杂的逻辑,也许你会需要用到如下这几个方法

  • fileinput.isfirstline() 如果刚读取的行是其所在文件的第一行则返回 True,否则返回 False。
  • fileinput.isstdin() 如果最后读取的行来自 sys.stdin 则返回 True,否则返回 False。
  • fileinput.nextfile() 关闭当前文件以使下次迭代将从下一个文件(如果存在)读取第一行;不是从该文件读取的行将不会被计入累计行数。 直到下一个文件的第一行被读取之后文件名才会改变。 在第一行被读取之前,此函数将不会生效;它不能被用来跳过第一个文件。 在最后一个文件的最后一行被读取之后,此函数将不再生效。
  • fileinput.close() 关闭序列。

7. 进阶一点的玩法

在 fileinput.input() 中有一个 openhook 的参数,它支持用户传入自定义的对象读取方法。

若你没有传入任何的勾子,fileinput 默认使用的是 open 函数。

fileinput 为我们内置了两种勾子供你使用

  1. fileinput.hook_compressed(*filename*, *mode*) 使用 gzip 和 bz2 模块透明地打开 gzip 和 bzip2 压缩的文件(通过扩展名 '.gz' 和 '.bz2' 来识别)。 如果文件扩展名不是 '.gz' 或 '.bz2',文件会以正常方式打开(即使用 open() 并且不带任何解压操作)。使用示例: fi = fileinput.FileInput(openhook=fileinput.hook_compressed)
  2. fileinput.hook_encoded(*encoding*, *errors=None*)

返回一个通过 open() 打开每个文件的钩子,使用给定的 encoding 和 errors 来读取文件。使用示例: fi = fileinput.FileInput(openhook=fileinput.hook_encoded("utf-8", "surrogateescape"))

如果你自己的场景比较特殊,以上的三种勾子都不能满足你的要求,你也可以自定义。

这边我举个例子来抛砖引玉下

假如我想要使用 fileinput 来读取网络上的文件,可以这样定义勾子。

  1. 先使用 requests 下载文件到本地
  2. 再使用 open 去读取它

直接将这个函数传给 openhoos 即可

运行后按预期一样将 CSDN 的 robots 的文件打印了出来

8. 列举一些实用案例

案例一:读取一个文件所有行

案例二:读取多个文件所有行

案例三:利用fileinput将CRLF文件转为LF

案例四:配合 re 做日志分析:取所有含日期的行

案例五:利用fileinput实现类似于grep的功能

9. 写在最后

fileinput 是对 open 函数的再次封装,在仅需读取数据的场景中, fileinput 显然比 open 做得更专业、更人性,当然在其他有写操作的复杂场景中,fileinput 就无能为力啦,本身从 fileinput 的命名上就知道这个模块只专注于输入(读)而不是输出(写)。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门