文件读写操作
在 Linux 系统中万物皆文件,所以我们不可避免的要和文件打交道,我们会常常对文件进行读和写的操作。例如:
1 | cat /etc/password # 读文件 |
而以上内容我们都是对文本文件进行读写,计算机中也存在对二进制文件的读写操作,那用 Python 如何实现呢?
文件的打开方法—open 内建函数
不管是读文件还是写文件,我们第一步都是要将文件打开。
作为打开文件之门的“钥匙”,内建函数 open() 提供了初始化输入/输出(I/O)操作的通用接口,成功打开文件后时候会返回一个文件对象,否则引发一个错误。
基本语法
要以任何方式使用文件——哪怕仅仅是打印其内容,都得先打开打文件,这样才能访问它。
参数介绍
file_name:表示我们要打开文件的路径
mode:以怎样的方式打开文件
文件模式 | 操 作 |
---|---|
r | 以读方式打开(文件不存在则报错) |
w | 以写方式打开(文件存在则清空,不存在则创建) |
a | 以追加模式打开 |
b | 以二进制模式打开 |
file_object:文件操作对象,我们后续对文件的所有读写操作都需要通过这个对象,而不是直接操作文件中的数据。
文件读操作
要使用文本文件中的信息,首先需要将信息读取到内存中。为此,我们可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取。
read 方法 —— 读取文件
open
函数的第一个参数是要打开的文件名(文件名区分大小写)- 如果文件 存在,返回 文件操作对象
- 如果文件 不存在,会 抛出异常
read
方法可以一次性 读入 并 返回 文件的 所有内容close
方法负责 关闭文件- 如果 忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问
- 注意:
read
方法执行后,会把 文件指针 移动到 文件的末尾
1 | # 1.打开文件 |
文件指针
- 文件指针 标记 从哪个位置开始读取数据
- 第一次打开 文件时,通常 文件指针会指向文件的开始位置
- 当执行了
read
方法后,文件指针 会移动到 读取内容的末尾- 默认情况下会移动到 文件末尾
- 重新打开文件时,文件指针 重新指向文件的最 开始位置
图例
思考
- 如果执行了一次
read
方法,读取了所有内容,那么再次调用read
方法,还能够获得到内容吗?
答案
- 不能,因为第一次读取之后,文件指针移动到了文件末尾,再次调用不会读取到任何的内容
readline 方法 —— 按行读取
read
方法默认会把文件的 所有内容 一次性读取到内存- 如果文件太大,对内存的占用会非常严重
readline
方法可以一次读取一行内容- 方法执行后,会把 文件指针 移动到下一行,准备再次读取
1 | # 1.打开文件 |
案例:读取大文件的正确姿势
1 | # 1.打开文件 |
readlines 方法
readlines()
方法读取所有(剩余的)行然后把它们作为一个 字符串列表 返回
图例
- 如果需要逐行处理文件,可以结合 for 循环迭代文件
- 迭代文件的方法与处理其他序列类型的数据类似
1 | # 1.打开文件 |
文件写操作
write 方法 —— 写文件
- write() 内建方法功能与
read()
和readline()
相反- 它把含有 文本数据 或 二进制数据 的内容写入到文件中去
writelines 方法
- 和
readlines()
一样,writelines()
方法是针对 列表 的操作 - 它接受一个 字符串列表 作为参数,将他们写入文件
- 行结束符并不会被自动加入,所以如果需要的话,必须再调用 writelines() 前给每行结尾加上行结束符
1 | # write 方法 |
案例 3:writelines
1 | # writelines 方法 |
图例:
with 子句
with语句 是用来简化代码的
在将打开文件的操作放在 with 语句中,代码块结束后,文件将自动关闭
读写文件的逻辑没有变化,变得只是 写法
案例 4:with
1 | with open('/tmp/passwd', mode="r") as f: |
练习
1 | # 模拟 cp 操作 |
方案:
1 | fr = open("/usr/bin/ls", mode="rb") # rb:读取二进制 |
函数
快速体验
- 所谓函数,就是把 具有独立功能的代码块 组织为一个小模块,在需要的时候 调用
- 函数的使用包含两个步骤:
- 定义函数 —— 封装 独立的功能
- 调用函数 —— 享受 封装 的成果
案例:洗衣服
1 | # 早上洗衣服 |
发现了问题:我们将有独立功能的代码封装成一个函数
1 | def washing_machine(): # 洗衣机可以帮我们完成 |
函数的创建与调用
创建函数
函数用 def
语句创建,语法如下:
1 | def 函数名(参数列表): # 具体情况具体对待,参数可有可无 |
标题行由 def
关键字,函数的名字,以及参数的集合(如果有的话)组成
def
子句的剩余部分包括了一个可选的文档字串,和必需的函数体
函数名称 的命名应该 符合 标识符的命名规则
- 可以由 字母、下划线 和 数字 组成
- 不能以数字开头
- 不能与关键字重名
1 | def washing_machine(): # 洗衣机可以帮我们完成 |
调用函数
使用一对圆括号 () 调用函数,如果没有圆括号,只是对函数的引用
任何输入的参数都必须放置在括号中
图例:
案例:加洗衣粉
1 | def washing_machine(): # 洗衣机可以帮我们完成 |
总结
- 定义好函数之后,只表示这个函数封装了一段代码而已
- 如果不主动调用函数,函数是不会主动执行的
思考
能否将 函数调用 放在 函数定义 的上方?
- 不能!
- 因为在 使用函数名 调用函数之前,必须要保证
Python
已经知道函数的存在 - 否则控制台会提示
NameError: name '函数名' is not defined
(名称错误:这个名字没有被定义)
函数的参数
形参和实参
- 形参:定义 函数时,小括号中的参数,是用来接收参数用的,在函数内部 作为变量使用
- 实参:调用 函数时,小括号中的参数,是用来把数据传递到 函数内部 用的
问题
当我们想洗其他的东西,要手动改方法内部的代码:
1 | def washing_machine(): # 洗衣机可以帮我们完成 |
在函数内部有一定的变化的值:
1 | def washing_machine(): # 洗衣机可以帮我们完成 |
思考一下存在什么问题
函数只能处理 固定 的数据
如何解决?
- 如果能够把需要处理的数据,在调用函数时,传递到函数内部就好了!
传递参数
- 在函数名的后面的小括号内部填写 参数
- 多个参数之间使用
,
分隔 - 调用函数时,实参的个数需要与形参个数一致,实参将依次传递给形参
1 | def washing_machine(something): # 洗衣机可以帮我们完成 |
图例
作用
- 函数,把 具有独立功能的代码块 组织为一个小模块,在需要的时候 调用
- 函数的参数,增加函数的 通用性,针对 相同的数据处理逻辑,能够 适应更多的数据
- 在函数 内部,把参数当做 变量 使用,进行需要的数据处理
- 函数调用时,按照函数定义的参数顺序,把 希望在函数内部处理的数据,通过参数 传递
位置参数
与 shell
脚本类似,程序名以及参数都以位置参数的方式传递给 python 程序,使用 sys
模块的 argv
列表接收
图例
默认参数
默认参数就是声明了 默认值 的参数,因为给参数赋予了默认值,所以在函数调用时,不向该参数传入值也是允许的
1 | # 默认参数 |
函数的返回值
- 在程序开发中,有时候,会希望 一个函数执行结束后,告诉调用者一个结果,以便调用者针对具体的结果做后续的处理
- 返回值 是函数 完成工作后,最后 给调用者的 一个结果
- 在函数中使用
return
关键字可以返回结果 - 调用函数一方,可以 使用变量 来 接收 函数的返回结果
注意:
return
表示返回,表示方法执行结束,后续的代码都不会被执行
案例:取钱和查余额
1 | # 查余额: 看,不带走,不能买东西 |
图例说明
没有 return
如果方法内部没有 return
语句,那么会默认返回 None,即 return None
练习 3:斐波那契数列函数
斐波那契数列函数
- 将斐波那契数列代码改为函数
- 数列长度由用户指定
- 要求把结果用 return 返回
版本一:方法内部直接打印
1 | def gen_fib(): |
版本二:带返回值
1 | def gen_fib(): |
版本三:带参数
1 | def gen_fib(n): |
练习4:复制文件函数
- 修改文件练习中的拷贝程序
- 将程序改为函数的形式
- 源文件和目标文件要求通过参数进行传递
- 实参要求来自于命令行
版本1:
1 | # 定义函数copy(),实现指定单个文件拷贝的功能 |
版本2:
1 | def copy(src_name, dst_name): # 定义函数copy(),实现任意文件的拷贝操作 |