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

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

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

1. 从标准输入中读取

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

# demo.py
import fileinputfor line in fileinput.input():print(line)

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

$ python demo.py
hello
hellopython
python

2. 单独打开一个文件

脚本的内容如下

import fileinputwith fileinput.input(files=('a.txt',)) as file:for line in file:print(f'{fileinput.filename()} 第{fileinput.lineno()}行: {line}', end='')

其中 a.txt 的内容如下

hello
world

执行后就会输出如下

$ python demo.py
a.txt 第1行: hello
a.txt 第2行: world

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

3. 批量打开多个文件

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

import fileinputwith fileinput.input(files=('a.txt', 'b.txt')) as file:for line in file:print(f'{fileinput.filename()} 第{fileinput.lineno()}行: {line}', end='')

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

$ cat a.txt
hello
world
$ cat b.txt
hello
python

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

$ python demo.py
a.txt 第1行: hello
a.txt 第2行: world
b.txt 第3行: hello
b.txt 第4行: python

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

import fileinputwith fileinput.input(files=('a.txt', 'b.txt')) as file:for line in file:print(f'{fileinput.filename()} 第{fileinput.filelineno()}行: {line}', end='')

运行后,输出如下

$ python demo.py
a.txt 第1行: hello
a.txt 第2行: world
b.txt 第1行: hello
b.txt 第2行: python

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

import fileinput
import globfor line in fileinput.input(glob.glob("*.txt")):if fileinput.isfirstline():print('-'*20, f'Reading {fileinput.filename()}...', '-'*20)print(str(fileinput.lineno()) + ': ' + line.upper(), end="")

运行效果如下

$ python demo.py
-------------------- Reading b.txt... --------------------
1: HELLO
2: PYTHON
-------------------- Reading a.txt... --------------------
3: HELLO
4: WORLD

4. 读取的同时备份文件

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

import fileinputwith fileinput.input(files=("a.txt",), backup=".bak") as file:for line in file:print(f'{fileinput.filename()} 第{fileinput.lineno()}行: {line}', end='')

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

$ ls -l a.txt*
-rw-r--r--  1 MING  staff  12  2 27 10:43 a.txt$ python demo.py
a.txt 第1行: hello
a.txt 第2行: world$ ls -l a.txt*
-rw-r--r--  1 MING  staff  12  2 27 10:43 a.txt
-rw-r--r--  1 MING  staff  42  2 27 10:39 a.txt.bak

5. 标准输出重定向替换

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

请看如下一段测试代码

import fileinputwith fileinput.input(files=("a.txt",), inplace=True) as file:print("[INFO] task is started...") for line in file:print(f'{fileinput.filename()} 第{fileinput.lineno()}行: {line}', end='') print("[INFO] task is closed...")

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

$ cat a.txt
hello
world$ python demo.py
[INFO] task is started...
[INFO] task is closed...$ cat a.txt
a.txt 第1行: hello
a.txt 第2行: world

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

import sys
import fileinputfor line in fileinput.input(files=('a.txt', ), inplace=True):#将Windows/DOS格式下的文本文件转为Linux的文件if line[-2:] == "\r\n":  line = line + "\n"sys.stdout.write(line)

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

DOS转UNIX::setfileformat=unix
UNIX转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 为我们内置了两种勾子供你使用

  • fileinput.hook_compressed(*filename*, *mode*)

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

  • fileinput.hook_encoded(encoding, errors=None)

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

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

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

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

  • 先使用 requests 下载文件到本地
  • 再使用 open 去读取它
def online_open(url, mode):import requestsr = requests.get(url) filename = url.split("/")[-1]with open(filename,'w') as f1:f1.write(r.content.decode("utf-8"))f2 = open(filename,'r')return f2

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

import fileinputfile_url = 'https://www.csdn.net/robots.txt'
with fileinput.input(files=(file_url,), openhook=online_open) as file:for line in file:print(line, end="")

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

User-agent: *
Disallow: /scripts
Disallow: /public
Disallow: /css/
Disallow: /images/
Disallow: /content/
Disallow: /ui/
Disallow: /js/
Disallow: /scripts/
Disallow: /article_preview.html*
Disallow: /tag/
Disallow: /*?*
Disallow: /link/Sitemap: https://www.csdn.net/sitemap-aggpage-index.xml
Sitemap: https://www.csdn.net/article/sitemap.txt

8. 列举一些实用案例

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

import fileinput
for line in fileinput.input('data.txt'):print(line, end="")

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

import fileinput
import globfor line in fileinput.input(glob.glob("*.txt")):if fileinput.isfirstline():print('-'*20, f'Reading {fileinput.filename()}...', '-'*20)print(str(fileinput.lineno()) + ': ' + line.upper(), end="")

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

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import sys
import fileinputfor line in fileinput.input(files=('a.txt', ), inplace=True):#将Windows/DOS格式下的文本文件转为Linux的文件if line[-2:] == "\r\n":  line = line + "\n"sys.stdout.write(line)

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

#--样本文件--:error.log
aaa
1970-01-01 13:45:30  Error: **** Due to System Disk spacke not enough...
bbb
1970-01-02 10:20:30  Error: **** Due to System Out of Memory...
ccc#---测试脚本---
import re
import fileinput
import syspattern = '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'for line in fileinput.input('error.log',backup='.bak',inplace=1):if re.search(pattern,line):sys.stdout.write("=> ")sys.stdout.write(line)#---测试结果---
=> 1970-01-01 13:45:30  Error: **** Due to System Disk spacke not enough...
=> 1970-01-02 10:20:30  Error: **** Due to System Out of Memory...

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

import sys
import re
import fileinputpattern= re.compile(sys.argv[1])
for line in fileinput.input(sys.argv[2]):if pattern.match(line):print(fileinput.filename(), fileinput.filelineno(), line)
$ ./test.py import.*re *.py
#查找所有py文件中,含import re字样的
addressBook.py  2   import re
addressBook1.py 10  import re
addressBook2.py 18  import re
test.py         238 import re

9. 写在最后

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

Python教程:推荐一个比 open 读文件还好用、方便的库相关推荐

  1. 推荐一个实用的 .gitignore 文件

    转载自    推荐一个实用的 .gitignore 文件 为什么要忽略文件? 常用的版本控制工具,不管是使用 git 还是 svn,我们都需要排除一些与程序代码无关的文件,如像 eclipse/ in ...

  2. python教程推荐-入门python有什么好的书籍推荐?

    Python 越来越火爆,最近自己也在学习.整理下一些 Python 资料,和练手的项目.希望对你学习 Python 有所帮助. 如果大家想要文中 10G Python 编程视频,点击下面的卡片即可免 ...

  3. 还在用 open 读文件?out 了,这个库比 open 好用 100 倍

    使用 open 函数去读取文件,似乎是所有 Python 工程师的共识. 今天明哥要给大家推荐一个比 open 更好用.更优雅的读取文件方法 – 使用 fileinput fileinput 是 Py ...

  4. 有没有好的python教程推荐_Python Pandas教程推荐,全网最佳,没有之一

    https://zhuanlan.zhihu.com/p/29576732​zhuanlan.zhihu.com 简介 下面内容为github作者的Pandas学习笔记,目前是我看到最好的资料,没有之 ...

  5. github上星星1万多的python教程推荐收藏

    简单的说,Python是一个"优雅"."明确"."简单"的编程语言. 学习曲线低,非专业人士也能上手 开源系统,拥有强大的生态圈 解释型语言 ...

  6. python教程推荐-关于推荐系统的详细介绍

    推荐系统中经常需要处理类似user_id, item_id, rating这样的数据,其实就是数学里面的稀疏矩阵,scipy中提供了sparse模块来解决这个问题,但scipy.sparse有很多问题 ...

  7. 有没有好的python教程推荐_学习python中的pandas有没有好的教程推荐?

    Pandas是入门Python做数据分析所必须要掌握的一个库,这里精选了十套练习题,可帮助你快速上手Python代码,完成数据集探索. [小提示:本文所使用的数据集下载地址:DATA | TRAIN ...

  8. 推荐一个在线查看.cer文件的网站

    网址:https://www.sslshopper.com/certificate-decoder.html 我们从IE浏览器里导出Certificate成.cer文件后, 用记事本打开,内容是这样的 ...

  9. 我要自学网python教程推荐_我作文-这就是我作文、十年后的我作文大全

    在平平淡淡的日常中,大家都尝试过写作文吧,借助作文人们可以实现文化交流的目的.相信很多朋友都对写作文感到非常苦恼吧,下面是小编为大家收集的我的烦恼小学作文500字7篇,仅供参考,大家一起来看看吧.我的 ...

最新文章

  1. 常用[js,css,jquery,html]
  2. django mysql orm教程_带你了解Django ORM操作(基础篇)
  3. Hibernate映射解析——七种映射关系
  4. 编写配置文件不能出现帮助信息
  5. [docker]docker压力测试
  6. “智云大咖秀”:大咖摄影师谈惊艳亮相的“大咖级”设备
  7. 算法---计算平方根(牛顿迭代法)
  8. 在公司网络中如何手动为apt-get设置代理
  9. SpringMVC【一 简单入门例子】
  10. 计算机控制系统第二章答案,计算机控制技术(第2版)部分课后题答案
  11. ART模式下dex2oat出错导致系统无法正常启动
  12. 让你心静的七十五条经典修心格言(转)
  13. HDU-ACM程序设计——BFS(宽度优先搜索)
  14. 通读《C++ primer plus》— C++中的5种数据类型转换方式
  15. 图像配准方面的算法总结
  16. 讲解Excel的16种图表类型的“含义”,知道该怎么画图了!
  17. Satori指纹识别原理及dhcp分析
  18. joomlaQQ登录,微信登录
  19. Heroes 综合能力TOP-TEN
  20. Vector3 学习与应用

热门文章

  1. Request爬取网站(seo.chinaz.com)百度权重的查询结果
  2. getDimension等区别
  3. IPSec ××× 在企业网中的应用
  4. 哇,union的优先级很高嘛
  5. rails3使用ActionMail发送邮件
  6. ie浏览器里面无法输入文字:
  7. ASUS WL-500W企业级无线路由器试用
  8. pe估值 python_Python编程学习笔记(8)
  9. oracle sequrnce_OracleSql语句学习(五)
  10. 【ABAP】供应商进项税额查询报表开发