➊ 纯文本文件
.txt、.py、.md、.html等。➋ 二进制文件
.mp3、.mp4、.doc、.ppt、.jpg、png等。➊ 绝对路径
从文件系统的『根目录』开始,完整描述文件或目录所在的位置。
举例:D:/demo/test/a.txt
➋ 相对路径
以当前『工作目录』为参照,描述目标文件或目录相对于它的位置。
举例:../../a.txt(其中 .. 表示上一级目录)
标准流程如下:
⑴ 创建『文件对象』;
⑵ 操作文件(读取、写入等);
⑶ 关闭文件。
文件操作的核心 —— **open()**函数,它最常用的三个参数如下:
file:需要操作的文件路径。
mode:文件的打开模式,可选值:
r:读取(默认值)w:写入,并先截断文件x:排它性创建,如果文件已存在,则创建失败a:打开文件用于写入,如果文件存在,则在文件末尾追加内容b:二进制模式t:文本模式(默认值)+:打开用于更新(读取与写入)encoding:字符编码
功能:用于改变文件对象指针的位置。
语法:seek(offset, whence)
参数:
offset偏移量,要移动多少距离。
whence参考点,从哪里开始计算偏移,有三种取值:
注意:在文本模式下,不要随意去定位中文字符的位置,否则可能破坏文件编码。
代码实例:常用场景是将当前指针移动到开始,便于重新开始操作。
file = open('./test/a.txt', 'rt', encoding='utf-8')
# 第一次读取文件
content = file.read()
print('下面是第一次读取到的内容:')
print(content)
# 第二次读取文件
content = file.read()
print('下面是第二次读取到的内容:')
print(content)
print(repr(content)) # 查看读取到的原始数据
file.close()
上面的代码运行结果,发现第二次读取到的内容为空,原因是针对同一个文件对象,第一次读取完针会移动到末尾,接着第二次读取就会读取不到内容。
如果需要第二次也能读取到内容,需要使用seek方法将指针移动开始,实现代码如下:
file = open('./test/a.txt', 'rt', encoding='utf-8')
# 第一次读取文件
content = file.read()
print('下面是第一次读取到的内容:')
print(content)
# 第二次读取文件
file.seek(0)
content = file.read()
print('下面是第二次读取到的内容:')
print(content)
file.close()
概述:使用『文件对象』的read()方法,读取文件中的内容。
方法说明:
read(size)中的size是可选参数:size参数,表示:读取文件中所有的内容(注意内存占用!)。size参数,表示:读取文件中指定个数的字符(文本模式),或指定大小的字节(二进制模式)。read会从上一次read的位置继续读取,若到达文件末尾后继续取,文本模式将会返回空字符串'',二进制模式将会返回空字节b''。示例代码:
# 1. 创建文件对象
# 读取文本文件
file = open('./test/a.txt', 'rt', encoding='utf-8')
# 读取二进制文件,下面以图片文件为例(注意:第二个参数mode的值为rb,且不能设置encoding参数)
file = open('./test/b.jpg', 'rb')
# 2. 读取文件
# 已知内容长度,逐步读取文件
r1 = file.read(2)
r2 = file.read(3)
r3 = file.read(4)
r4 = file.read() # 读取文件所有内容,如果文件很大会撑爆内存
print(r1, end='')
print(r2, end='')
print(r3, end='')
print(r4, end='')
# 重置上下文文件对象指针到开头
file.seek(0)
print('\n' + '--' * 10)
# 通过循环多次read(对内存友好)
while True:
result = file.read(10)
if result == '':
break
print(result, end='')
# 3. 关闭文件对象
file.close()
概述:使用『文件对象』的readline()方法,读取文件中的一行。
方法说明:
readline(size)中的size是可选参数:size参数,表示:读取当前这一行所有的内容。size参数,表示:表示读取当前行时,最多能读取的字符数,或字节数。size不是行数。readline方法,也是从上一次位置继续读取,若到达文件末尾后继续取,文本模式将会返回空字符串'',二进制模式将会返回空字节b''。示例代码:
# 1. 创建文件对象
file = open('./test/a.txt', 'rt', encoding='utf-8')
# 2. 逐行读取文件
r1 =file.readline()
r2 =file.readline()
r3 =file.readline()
r4 =file.readline()
print(r1.strip())
print(r2.strip())
print(r3.strip())
print(r4.strip())
file.seek(0)
print('\n' + '--' * 10 + '\n')
while True:
line = file.readline()
if line == '':
break
print(line.strip())
# 3. 关闭文件对象
file.close()
概述:可以使用 for 循环直接遍历『文件对象』(逐行遍历)。
示例代码:
file = open('./test/a.txt', 'rt', encoding='utf-8')
for line in file:
print(line.strip())
file.close()
概述:使用『文件对象』的readlines()方法,一次性按“行”读完,返回一个列表。
方法说明:
readlines(hint)中的hint是可选参数:hint参数,表示:读取当前文件的所有行。hint参数,表示:期望读取的【字符个数 或 字节数】(hint不是行数)。**readlines**是一次性读取文件的所有内容,所以不适合读取体积较大的文件。示例代码:
file = open('./test/a.txt', 'rt', encoding='utf-8')
result = file.readlines()
print(result)
file.close()
概述:更推荐使用with上下文管理器,结合for循环遍历,逐行读取文件。
示例代码:
with open('./test/a.txt', 'rt', encoding='utf-8') as file:
for line in file:
print(line.strip())
概述:Python 中的with主要用于管理程序中“需要成对出现的操作”,例如:
使用with的目标:
编码者只管做具体的事,“进入”和“离开”的事,让 Python 自动处理。
语法格式:
with 能得到一个上下文管理器的表达式 as 变量:
具体的事1
具体的事2
具体的事3
上下文管理协议:
__enter__方法:with中的代码执行【之前】调用,其返回值会赋值给as后的变量。__exit__方法:with中的代码执行【结束后】调用(无论with中是否会出现异常都会调用)。with 中的代码发生异常时,__exit__方法的返回值来控制是否抛出异常:测试代码:定义一个Person类,让其实例对象遵循上下文管理器协议
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print(f'我叫{self.name},今年{self.age}岁了。')
def __enter__(self):
print('---- 我是进入的逻辑 ----')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('---- 我是离开的逻辑 ----')
if exc_type:
print(f'异常类型:{exc_type}')
print(f'异常对象:{exc_val}')
print(f'异常追踪信息:{exc_tb}')
return True # return True 则不抛出异常,False 则抛出异常
with Person('远方', 41) as person:
person.speak()
person.study() # 此行运行时会抛出异常
print('6666....') # 由于上一行排除异常,该行不会运行
上面的代码运行逻辑:
1、计算
with后面的表达式,得到一个『上下文管理器』;2、调用『上下文管理器』的
__enter__()方法,并将其返回值赋值为as后面的变量;3、执行
with所管理的代码块;4、无论
with中的代码,是正常结束还是发生异常,都会自动调用『上下文管理器』的__exit__()方法。
运行结果:
---- 我是进入的逻辑 ----
我叫远方,今年41岁了。
---- 我是离开的逻辑 ----
异常类型:<class 'AttributeError'>
异常对象:'Person' object has no attribute 'study'
异常追踪信息:<traceback object at 0x000001B9A8A6BA80>
该模式是写入模式,写入前会先截断文件(清空文件),覆盖性写入。
with open('./test/b.txt', 'wt', encoding='utf-8') as f:
f.write('hello, Python')
该模式是排它性创建,如果文件已创建,则创建失败。换言之,x模式等于新建文件然后写入新建的文件。
# 如果 .text/b.txt 文件已经存在,则会报错:FileExistsError: [Errno 17] File exists: './test/b.txt'
with open('./test/b.txt', 'xt', encoding='utf-8') as f:
f.write('hello, Python')
该模式是追加模式,打开文件用于写入,如果文件存在,则在文件末尾追加内容。
import time
with open('./test/b.txt', 'at', encoding='utf-8') as f:
f.write('\nhello, Python ' + time.strftime("%Y-%m-%d %H:%M:%S"))
Python 写入文件时,并不是每写一次就立刻落盘,而是先写到『缓冲区』里。
flush方法的作用把缓冲区中的数据立刻写入到文件中。
测试代码:
with open('./test/b.txt', 'at', encoding='utf-8') as f:
f.write('\nnew data 1')
f.write('\nnew data 2')
f.flush()
time.sleep(10000)
f.write('\nnew data 3')
f.write('\nnew data 4')
代码解析:
time.sleep(1000)会阻塞with代码块执行,在阻塞结束之前,如果不使用f.flush,通过f.write写入的内容都在『缓冲区』,需要等到阻塞结束,且后续代码执行完,触发了with模块的__exit__方法,才能把『缓冲区』中的数据立刻写入到文件中。f.flush方法,则会将『缓冲区』中的数据立刻写入到文件中。『混合模式』就是在单独的 r、w、a 定义的『基础模式』后面,增加一个+号“修饰符”,用来开启“读写”能力,形成的rt+、wt+、at+等就是『混合模式』。
只用基础模式,无法在一个已打开的文件对象上同时进行读写:
# 错误示范:读取模式创建的文件对象,想要写入就会报错
f = open('test.txt', 'r', encoding='utf-8') # 只有读能力
# f.write('hello') # 会报错!io.UnsupportedOperation: not writable
f.close()
# 错误示范:写入模式创建的文件对象,想要读取就会报错
f = open('test.txt', 'w', encoding='utf-8') # 只有写能力,且文件被清空了
# content = f.read() # 会报错!io.UnsupportedOperation: not readable
f.close()
用混合模式就能解决上面的问题:
# 读取混合模式
file_r = open('./test/d.txt', 'rt+', encoding='utf-8')
file_r.write('abc')
file_r.seek(0)
content = file_r.read()
print(content)
file_r.close()
# 写入混合模式
file_w = open('./test/d.txt', 'wt+', encoding='utf-8')
file_w.write('test')
file_w.seek(0)
content = file_w.read()
print(content)
file_w.close()
r模式可以读取,+模式可以更新(读取或写入),所以rt+模式可读可写。
注意:r模式打开文件后,文件指针在起始位置。
with open('./test/c.txt', 'rt+', encoding='utf-8') as f:
f.write('hello world')
f.seek(0) # 上一行代码执行完,文件对象的指针在末尾,要想读取内容,需要将指针移动到开始
content = f.read()
print(content)
注意:针对同一个文件对象,每次执行read、write方法都会移动文件对象的指针,可能会造成不同的读取和写入的结果。
w模式可以写入,+模式可以用于更新(读取或写入),所以wt+模式可读可写。
w模式打开文件后,文件指针在起始位置,但write方法执行完成后,指针在文件结束位置。
with open('./test/c.txt', 'wt+', encoding='utf-8') as f:
f.write('hello world')
f.seek(0) # 上一行代码执行完,文件对象的指针在末尾,要想读取内容,需要将指针移动到开始
content = f.read()
print(content)
注意:针对同一个文件对象,每次执行read、write方法都会移动文件对象的指针,可能会造成不同的读取和写入的结果。
x模式可以写入,+模式可以用于更新(读取或写入),所以xt+模式可读可写。
x模式打开文件后,文件指针在起始位置。
with open('./test/d.txt', 'xt+', encoding='utf-8') as f:
f.write('hello world')
f.seek(0) # 上一行代码执行完,文件对象的指针在末尾,要想读取内容,需要将指针移动到开始
content = f.read()
print(content)
注意:针对同一个文件对象,每次执行read、write方法都会移动文件对象的指针,可能会造成不同的读取和写入的结果。
a模式可以追加内容,+模式可以用于更新(读取或写入),所以at+模式可读可写。
a模式打开文件后,文件指针在结束位置。
with open('./test/d.txt', 'at+', encoding='utf-8') as f:
f.write('hello world')
f.seek(0) # 上一行代码执行完,文件对象的指针在末尾,要想读取内容,需要将指针移动到开始
content = f.read()
print(content)
注意:针对同一个文件对象,每次执行read、write方法都会移动文件对象的指针,可能会造成不同的读取和写入的结果。
Python 中操作目录需要导入标准库模块os:
import os
下面介绍常见目录操作。
1、os.mkdir(path)创建“单级”目录
注意:如果目录已经存在,会抛出异常。
os.mkdir('demo')
2、os.makedirs(path)创建“多级”目录
注意:如果路径中的所有目录都已经存在,则会抛出异常。
os.makedirs("demo/a/b/c")
3、os.rmdir(path)删除空目录
如果目录不存在,或目录非空,都会抛出异常。
os.rmdir('demo/a/b/c')
4、os.removedirs(path)递归删除空目录
递归删除多级空目录,在成功删除末尾一级目录后,会“向上”尝试把父级目录也删除,直到父目录不是空目录。
os.removedirs('./demo/a/b')
5、os.path.exists(path)判断路径是否存在(文件/目录都算)
print(os.path.exists("./demo/a")) # True
print(os.path.exists("./demo/a.txt")) # True
6、os.path.isdir(path)用于判断路径
具体判断规则:
print(os.path.isdir('./demo/1/a')) # False
print(os.path.isdir('./demo/a')) # True
7、os.path.isfile(path)判断是否为文件
print(os.path.isfile('./demo/1/a')) # True
print(os.path.isfile('./demo/a')) # False
8、os.scandir(path)扫描指定目录(只扫描一级)
result = os.scandir('./demo')
for item in result:
print('目录' if item.is_dir() else '文件', item.name)
# 运行结果
# 目录 1
# 目录 a
# 文件 a.txt
9、os.walk(path)递归遍历指定目录(扫描多级目录)
result = os.walk('./demo')
for item in result:
print(item)
# 运行结果
# ('./demo', ['1', 'a'], ['a.txt'])
# ('./demo\\1', [], ['a'])
# ('./demo\\a', ['b'], [])
# ('./demo\\a\\b', ['c'], [])
# ('./demo\\a\\b\\c', [], [])
10、hutil.rmtree(path)删除有内容的目录(危险操作,谨慎使用)
import shutil
shutil.rmtree('demo')
import os
# 源文件
source = './test/eason-chen-backpack.mp3'
# 目标目录
target = './media'
# 如果目标目录不存在,那就去创建
if not os.path.isdir(target):
os.mkdir(target)
with open(source, 'rb') as f1, open(target + '/test_media.mp3', 'wb') as f2:
while True:
# 每次只读取1KB
data = f1.read(1024)
# 如果文件读取完毕了,就跳出循环
if not data:
break
# 向目标文件中写入数据
f2.write(data)
print('复制完毕!')
详细需求如下:
代码实现如下:
import time
# 用户数据
users = {
'张三': '123456',
'李四': '888888',
'王五': 'abc123'
}
# 提示输入信息
username = input('请输入用户名:')
password = input('请输入密码:')
# 获取当前时间
now = time.strftime('%Y-%m-%d %H:%M:%S')
# 如果用户名不在users中
if username not in users:
print('用户名未注册')
with open('./test/logs.txt', 'at', encoding='utf-8') as f:
f.write(f'{now} 登录失败(未注册的用户名{username})\n')
# 如果密码不正确
elif users[username] != password:
print('密码不正确')
with open('./test/logs.txt', 'at', encoding='utf-8') as f:
f.write(f'{now} {username} 登录失败(密码输入错误)\n')
# 登录成功
else:
print('登录成功!')
with open('./test/logs.txt', 'at', encoding='utf-8') as f:
f.write(f'{now} {username} 登录成功 \n')