赞
踩
在 Linux 系统中万物皆文件,所以我们不可避免的要和文件打交道,我们会常常对文件进行读和写的操作。例如:
- cat /etc/password # 读文件
- vim /etc/password # 读写文件
- echo test > /tmp/abc.txt # 覆盖写文件
- echo text >> /tmp/abc.txt # 追加写文件
而以上内容我们都是对文本文件进行读写,计算机中也存在对二进制文件的读写操作,那用 Python 如何实现呢?
不管是读文件还是写文件,我们第一步都是要将文件打开。
作为打开文件之门的“钥匙”,内建函数 open() 提供了初始化输入/输出(I/O)操作的通用接口,成功打开文件后时候会返回一个文件对象,否则引发一个错误。
要以任何方式使用文件——哪怕仅仅是打印其内容,都得先打开打文件,这样才能访问它。
file_name:表示我们要打开文件的路径
mode:以怎样的方式打开文件
文件模式 | 操 作 |
---|---|
r | 以读方式打开(文件不存在则报错) |
w | 以写方式打开(文件存在则清空,不存在则创建) |
a | 以追加模式打开 |
b | 以二进制模式打开 |
file_object:文件操作对象,我们后续对文件的所有读写操作都需要通过这个对象,而不是直接操作文件中的数据。
要使用文本文件中的信息,首先需要将信息读取到内存中。为此,我们可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取。
open
函数的第一个参数是要打开的文件名(文件名区分大小写)
如果文件 存在,返回 文件操作对象
如果文件 不存在,会 抛出异常
read
方法可以一次性 读入 并 返回 文件的 所有内容
close
方法负责 关闭文件
如果 忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问
注意:read
方法执行后,会把 文件指针 移动到 文件的末尾
- # 1.打开文件
- fr = open("/etc/hosts", mode="r") # r: read
-
- # 2.读取文件
- print(fr.read(5)) # 读取前5个字符
- print(fr.read(3)) # 接着向后读3个字符
- print(fr.read()) # 读取文件剩余的所有内容
- print(fr.read()) # 什么都读取不到,因为指针已经在末尾了
-
- # 3.关闭资源
- fr.close()
文件指针 标记 从哪个位置开始读取数据
第一次打开 文件时,通常 文件指针会指向文件的开始位置
当执行了 read
方法后,文件指针 会移动到 读取内容的末尾
默认情况下会移动到 文件末尾
重新打开文件时,文件指针 重新指向文件的最 开始位置
图例
思考
如果执行了一次 read
方法,读取了所有内容,那么再次调用 read
方法,还能够获得到内容吗?
答案
不能,因为第一次读取之后,文件指针移动到了文件末尾,再次调用不会读取到任何的内容
read
方法默认会把文件的 所有内容 一次性读取到内存
如果文件太大,对内存的占用会非常严重
readline
方法可以一次读取一行内容
方法执行后,会把 文件指针 移动到下一行,准备再次读取
- # 1.打开文件
- fr1 = open("/etc/passwd", mode="r") # r:read
-
- # 2.读取文件
- print(fr1.readline(), end="") # 读取第一行数据
- print(fr1.readline(), end="") # 读取第二行数据
-
- # 3.关闭资源
- fr1.close()
- # 1.打开文件
- fr = open("/etc/passwd", mode="r")
-
- # 2.读取文件
- while True: # 死循环
- data = fr.readline() # 某一行数据
- if len(data) == 0: # 文件读取完毕,终止循环
- break
- print("data:", data, end="")
-
- # 3.关闭资源
- fr.close()
readlines()
方法读取所有(剩余的)行然后把它们作为一个 字符串列表 返回
图例
如果需要逐行处理文件,可以结合 for 循环迭代文件
迭代文件的方法与处理其他序列类型的数据类似
- # 1.打开文件
- fr2 = open("/etc/hosts", mode="r")
-
- # 2.读取文件
- # 和for连用
- # data = fr2.readlines()
- # print("data:", data)
- # print(data[0]) # 第一行
- # print(data[1]) # 第二行
- for item in fr2: # 相当于:for item in fr2.readlines():
- print(item, end="")
-
- # 3.关闭资源
- fr2.close()
图列:
write() 内建方法功能与 read()
和 readline()
相反
它把含有 文本数据 或 二进制数据 的内容写入到文件中去
和 readlines()
一样,writelines()
方法是针对 列表 的操作
它接受一个 字符串列表 作为参数,将他们写入文件
行结束符并不会被自动加入,所以如果需要的话,必须再调用 writelines() 前给每行结尾加上行结束符
- # 1.打开文件
- fw = open("/opt/dc.txt", mode="w") # w: write
-
- # 2.写文件
- fw.write("hello world\n")
- fw.write("you are my baby girl~\n")
-
- # 3.关闭资源
- fw.close()
- list01 = ["hello\n", "world\n", "nfx\n"]
-
- # 1.打开文件
- fw1 = open("/opt/tc.txt", mode="w")
-
- # 2.写文件
- fw1.writelines(list01)
-
- # 3.关闭资源
- fw1.close()
- # hello
- # world
- # nfx
图例:
with语句 是用来简化代码的
在将打开文件的操作放在 with 语句中,代码块结束后,文件将自动关闭
读写文件的逻辑没有变化,变得只是 写法
- with open('/tmp/passwd', mode="r") as f:
- # 文件的读写操作
- # ......
- # 模拟 cp 操作
- # 1. 创建 cp.py 文件
- # 2. 将 /usr/bin/ls "拷贝" 到/tmp 目录下
- # 3. 不要修改原始文件
-
- fr = open("/usr/bin/ls", mode="rb") # rb:读取二进制
- fw = open("/tmp/myls",mode="wb") # wb:写二进制
- while True:
- data = fr.read(4096) # 4096字节 = 4k
- if len(data) == 0: # 文件读取完毕,终止循环
- break
- fw.write(data) # 将读到的数据写入文件
- fr.close()
- fw.close()
所谓函数,就是把 具有独立功能的代码块 组织为一个小模块,在需要的时候 调用
函数的使用包含两个步骤:
定义函数 —— 封装 独立的功能
调用函数 —— 享受 封装 的成果
- # 早上洗衣服
- print("打水")
- print("洗衣服")
- print("甩干")
- # 中午洗衣服
- print("打水")
- print("洗衣服")
- print("甩干")
- # 晚上洗衣服
- print("打水")
- print("洗衣服")
- print("甩干")
发现了问题:我们将有独立功能的代码封装成一个函数
- def washing_machine(): # 洗衣机可以帮我们完成
- print("打水")
- print("洗衣服")
- print("甩干")
- # 优化后代码
- # 早上洗衣服
- washing_machine() # 打开洗衣机开关
- # 中午洗衣服
- washing_machine() # 打开洗衣机开关
- # 晚上洗衣服
- washing_machine() # 打开洗衣机开关
函数用 def
语句创建,语法如下:
- def 函数名(参数列表): # 具体情况具体对待,参数可有可无
- """函数说明文档字符串"""
- 函数封装的代码
- ……
标题行由 def
关键字,函数的名字,以及参数的集合(如果有的话)组成
def
子句的剩余部分包括了一个可选的文档字串,和必需的函数体
函数名称 的命名应该 符合 标识符的命名规则
可以由 字母、下划线 和 数字 组成
不能以数字开头
不能与关键字重名
- def washing_machine(): # 洗衣机可以帮我们完成
- print("打水")
- print("洗衣服")
- print("甩干")
使用一对圆括号 () 调用函数,如果没有圆括号,只是对函数的引用
任何输入的参数都必须放置在括号中
图例:
- def washing_machine(): # 洗衣机可以帮我们完成
- print("打水")
- print("加洗衣粉!!!")
- print("洗衣服")
- print("甩干")
- # 早上洗衣服
- washing_machine()
- # 中午洗衣服
- washing_machine()
- # 晚上洗衣服
- washing_machine()
定义好函数之后,只表示这个函数封装了一段代码而已
如果不主动调用函数,函数是不会主动执行的
能否将 函数调用 放在 函数定义 的上方?
不能!
因为在 使用函数名 调用函数之前,必须要保证 Python
已经知道函数的存在
否则控制台会提示 NameError: name '函数名' is not defined
(名称错误:这个名字没有被定义)
形参:定义 函数时,小括号中的参数,是用来接收参数用的,在函数内部 作为变量使用
实参:调用 函数时,小括号中的参数,是用来把数据传递到 函数内部 用的
当我们想洗其他的东西,要手动改方法内部的代码:
- def washing_machine(): # 洗衣机可以帮我们完成
- print("打水")
- print("加洗衣粉!!!")
- print("洗床单") # 洗被套
- print("甩干")
在函数内部有一定的变化的值:
- def washing_machine(): # 洗衣机可以帮我们完成
- print("打水")
- print("加洗衣粉!!!")
- print("洗衣服")
- print("甩干")
- washing_machine()
- def washing_machine(): # 洗衣机可以帮我们完成
- print("打水")
- print("加洗衣粉!!!")
- print("洗床单")
- print("甩干")
- washing_machine()
- ......
思考一下存在什么问题
函数只能处理 固定 的数据
如何解决?
如果能够把需要处理的数据,在调用函数时,传递到函数内部就好了!
在函数名的后面的小括号内部填写 参数
多个参数之间使用 ,
分隔
调用函数时,实参的个数需要与形参个数一致,实参将依次传递给形参
- def washing_machine(something): # 洗衣机可以帮我们完成
- print("打水")
- print("加洗衣粉!!!")
- print("洗" + something)
- print("甩干")
- # 洗衣服
- washing_machine("衣服")
- # 洗床单
- washing_machine("床单")
图例
函数,把 具有独立功能的代码块 组织为一个小模块,在需要的时候 调用
函数的参数,增加函数的 通用性,针对 相同的数据处理逻辑,能够 适应更多的数据
在函数 内部,把参数当做 变量 使用,进行需要的数据处理
函数调用时,按照函数定义的参数顺序,把 希望在函数内部处理的数据,通过参数 传递
与 shell
脚本类似,程序名以及参数都以位置参数的方式传递给 python 程序,使用 sys
模块的 argv
列表接收
图例:
默认参数就是声明了 默认值 的参数,因为给参数赋予了默认值,所以在函数调用时,不向该参数传入值也是允许的
1.如果没有手动给形参赋值,那么就用默认值
2.传递的实际参数优先级比默认参数要高
3.定义函数时,有默认值的参数一定放在没有默认值参数的后面
- def get_sum(num01, num02=100):
- print(num01+num02)
- get_sum(50) # num01 = 50 num02 = 100
- get_sum(10, 20) # num01 = 10 num02 = 20
- get_sum(1, 2) # num01 = 1 num02 = 2
-
- def get_sum2(num01=30, num02=40):
- print(num01+num02)
- get_sum2() # num01=30 num02=40
-
- # def get_sum1(num01=100, num02): 报错
- # print(num01+num02)
在程序开发中,有时候,会希望 一个函数执行结束后,告诉调用者一个结果,以便调用者针对具体的结果做后续的处理
返回值 是函数 完成工作后,最后 给调用者的 一个结果
在函数中使用 return
关键字可以返回结果
调用函数一方,可以 使用变量 来 接收 函数的返回结果
注意:
return
表示返回,表示方法执行结束,后续的代码都不会被执行
- # 查余额: 看,不带走,不能买东西
- # 如果内部没有return语句,那么会默认返回None: return None
- def chaxun():
- print(100)
- # return None
- money1 = chaxun()
- print("我取了:", money1) # 我取了: None
-
- # 取钱: 将钱放自己兜里带走,能买东西
- # return后跟的数据,叫函数的返回值
- def quqian():
- return 100 # 将100放自己兜里带走
- money = quqian() # 调用quqian, 将100赋值给变量money
- print("我取了:", money) # 我取了: 100
- money -= 5
- print("我买了1个冰淇凌,花了5元,还剩", money)
图例说明
如果方法内部没有 return
语句,那么会默认返回 None,即 return None
斐波那契数列函数
将斐波那契数列代码改为函数
数列长度由用户指定
要求把结果用 return 返回
版本一:方法内部直接打印
- def gen_fib():
- fib = [0, 1] # 定义列表,指定斐波那契数列的初始两个值
- n = int(input('长度: ')) # 定义变量n, 此变量为用户要看到的列表fib中的元素个数
- for i in range(n - 2):
- fib.append(fib[-1] + fib[-2])
- print(fib) # 打印列表fib
- # 调用两次函数gen_fib()
- gen_fib()
- gen_fib()
版本二:带返回值
- def gen_fib():
- fib = [0, 1]
- n = int(input('长度: '))
- for i in range(n - 2):
- fib.append(fib[-1] + fib[-2])
- return fib # 返回最后生成的列表fib
- # 调用函数gen_fib()
- print(gen_fib())
版本三:带参数
- def gen_fib(n):
- fib = [0, 1]
- for i in range(n - 2):
- fib.append(fib[-1] + fib[-2])
- return fib # 返回最后生成的列表fib
- # 定义列表nlist, 将要产生的斐波那契数列的长度,作为列表nlist的元素
- nlist = [10, 8, 6]
- # 使用for循环,传递实参(nlist中的元素)给函数gen_fib(n),得到三组斐波那契数列
- for i in nlist:
- print(gen_fib(i))
修改文件练习中的拷贝程序
将程序改为函数的形式
源文件和目标文件要求通过参数进行传递
实参要求来自于命令行
版本1:
- # 定义函数copy(),实现指定单个文件拷贝的功能
- def copy():
- src_name = '/usr/bin/ls'
- dst_name = '/tmp/list3'
- # 以只读字节的方式打开源文件,fr
- # 以写入字节的方式打开源文件,fw
- fr = open(src_name, mode='rb')
- fw = open(dst_name, mode='wb')
- while True:
- data = fr.read(4096)
- if len(data) == 0:
- break
- fw.write(data)
-
- fr.close()
- fw.close()
- copy() # 调用拷贝文件的函数copy()
版本2:
- def copy(src_name, dst_name): # 定义函数copy(),实现任意文件的拷贝操作
- # 以只读字节的方式打开源文件,赋值给变量fr
- # 以写入字节的方式打开源文件,赋值给变量fw
- fr = open(src_name, mode='rb')
- fw = open(dst_name, mode='wb')
- while True:
- data = fr.read(4096)
- if len(data) == 0:
- break
- fw.write(data)
- fr.close()
- fw.close()
- copy(源文件路径, 目标文件路径)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。