PythonNote-文件操作

1. 文件基本操作

1.1 文件的概述

  • 文件是数据的抽象和集合,是数据存储的一种形式。一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。
  • 文件是操作系统提供给用户操作硬盘的一种虚拟的概念。用来承接指令和数据。
  • 文件拥有 文本文件二进制文件 两种展现形式。

1.2 文件基本操作

操作文件的套路非常固定,一共包含三个步骤:

  • 第一步:打开文件
  • 第二步:读、写文件
    • 将文件内容读入内存
    • 将内存内容写入文件
  • 第三步:关闭文件
# 1、打开文件
f=open('a.txt',mode='rt',encoding='utf-8')

# 2、调用文件对象下的读写方法
res=f.read()
# f.write()      #写入文件的内容

# 3、关闭文件
f.close() # 回收操作系统资源

补充:

  • open() 函数的语法格式:
# 常用的语法格式:
open(file, mode='r', encoding='utf-8')   # 文本文件常用
open(file, mode='rb')        # 二进制文件常用

# 完整的语法格式:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

# 参数说明:
file: 必填项,文件路径(相对或者绝对路径)。
mode: 文件打开模式,默认是r(只读)
buffering: 设置缓冲
encoding: 一般使用utf-8
errors: 报错级别
newline: 区分换行符
closefd: 传入的file参数类型
opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。
  • 绝对路径和相对路径:
    • 绝对路径:从根路径位置到指定文件的全路径;
    • 相对路径:从当前文件的位置到指定文件的路径;

1.3 with语法 自动关闭文件

with语法 自动关闭文件,相当于帮你执行了fp.close()

# 语法格式:
with open(file,mode='r') as 文件对象
	# 读写操作
    ……

# 方法一.读写二进制文件
with open("集合2.png",mode="rb") as fp:
	res = fp.read()
with open("集合3.png",mode="wb") as fp:
	fp.write(res)

# 方法二.继续简化
with open("集合3.png",mode="rb") as fp1 , open("集合4.png",mode="wb") as fp2 :
	res = fp1.read()
	fp2.write(res)

# 这种简化方式,可以打开多个文件进行读写操作。

1.4 文本文件和二进制文件

文本文件和二进制文件是文件的两种不同的展现形式。

  • t表示文本模式,默认的,可以不体现出来。

    • 读写都是以字符串(unicode)为单位
    • 只能针对文本文件
    • 必须指定字符编码,即必须指定encoding参数
  • b表示二进制模式

    • 读写都是以bytes为单位
    • 可以针对所有文件
    • 一定不能指定字符编码,即一定不能指定encoding参数
  • 这两种模式都不能单独进行使用,必须配合r、w、a这些操作模式使用。

# encoding参数
# 针对文本文件,在读写的时候建议指定encoding参数,没有指定encoding参数操作系统会使用自己默认的编码。
# linux系统默认utf-8
# windows系统默认gbk
with open('c.txt',mode='rt',encoding='utf-8') as f:
    res=f.read()      # t模式会将f.read()读出的结果解码成unicode
    print(res,type(res))

2. 文件操作模式

2.1 操作模式介绍

文件操作模式:

  • w:(只写模式)
    文件不存在则创建文件,存在的话则打开清空内容,并且将文件指针放在文件的开头。

  • r:(只读模式)
    文件不存在则报错,存在的话则打开文件,并且将文件指针放在文件的开头。

  • a:(追加模式)
    文件不存在则创建文件,存在的话则打开文件,并且将文件指针放在文件的末尾。

  • x:(只写模式,较少使用)
    文件已存在则报错,不存在的话则创建文件,将文件指针放在文件的开头。

扩展模式 (配合打开模式的辅助模式,自己单独不能使用)

  • +:增强模式(可以让文件具有读写功能)
  • t:文本模式(文本),默认的模式,例如:rt = rwt = w等。
  • b:bytes bytes模式(二进制字节流)

模式参数

参数 参数+ 参数b 参数b+
w/wt w+/wt+ wb wb+
r/rt r+/rt+ rb rb+
a/at a+/at+ ab ab+
x/xt x+/xt+ xb xb+

2.2 文件操作详解

r模式(只读)

r(默认的操作模式):只读模式,当文件不存在时报错,当文件存在时文件指针跳到开始位置。

with open('c.txt',mode='rt',encoding='utf-8') as f:
	print('第一次读'.center(50,'*'))
	res=f.read() # 把所有内容从硬盘读入内存
	print(res)    # 读取到文件按里的内容。

	print('第二次读'.center(50,'*'))
	res1=f.read()
	print(res1)    # 没有内容

# 第二次读,没有读取到文件里的内容,是因为第一次读的时候,文件的指针已经跳转到了文件的末尾位置,而且文件没有关闭,所以第二次读的时候,就在指针的当前位置往下读取。
# 当重新打开一次时,文件指针会又跳转到开始位置,读取数据。

with open('c.txt',mode='rt',encoding='utf-8') as f:
	print('第一次读'.center(50,'*'))
	res=f.read() # 把所有内容从硬盘读入内存
	print(res)    # 读取到文件按里的内容。

with open('c.txt', mode='rt', encoding='utf-8') as f:
	print('第二次读'.center(50,'*'))
	res1=f.read()
	print(res1)    # 读取到文件按里的内容。

# 案例:匹配登录账号密码
inp_username=input('your name>>: ').strip()
inp_password=input('your password>>: ').strip()
# 验证
with open('user.txt',mode='rt',encoding='utf-8') as f:
	for line in f:
	username,password=line.strip().split(':')
	if inp_username == username and inp_password == password:
		print('login successfull')
		break
	else:
		print('账号或密码错误')

w模式(只写)

w:只写模式,当文件不存在时会创建空文件,当文件存在会清空文件,指针位于开始位置。

  • 在以w模式打开文件没有关闭的情况下,连续写入,新的内容总是跟在旧的之后;
  • 如果重新以w模式打开文件,则会清空文件内容
with open('d.txt',mode='wt',encoding='utf-8') as f:
	# f.read() # 报错,不可读
	f.write('测试abc\n')

# 1、在以w模式打开文件没有关闭的情况下,连续写入,新的内容总是跟在旧的之后
with open('d.txt',mode='wt',encoding='utf-8') as f:
	f.write('123\n')
	f.write('234\n')
	f.write('345\n')

# 2、如果重新以w模式打开文件,则会清空文件内容
with open('d.txt',mode='wt',encoding='utf-8') as f:
	f.write('123\n')
with open('d.txt',mode='wt',encoding='utf-8') as f:
	f.write('234\n')
with open('d.txt',mode='wt',encoding='utf-8') as f:
	f.write('345\n')

w模式更加适用于创建新的文件,比如复制操作,生成新的复制文件之类的。

# 案例:w模式用来创建全新的文件
# 文件文件的copy工具

src_file=input('源文件路径>>: ').strip()
dst_file=input('源文件路径>>: ').strip()
with open(r'{}'.format(src_file),mode='rt',encoding='utf-8') as f1,open(r'{}'.format(dst_file),mode='wt',encoding='utf-8') as f2:
	res=f1.read()
	f2.write(res)

a模式(只追加)

a:只追加写,在文件不存在时会创建空文档,在文件存在时文件指针会直接调到末尾

with open('e.txt',mode='at',encoding='utf-8') as f:
	# f.read() # 报错,不能读
	f.write('123\n')
	f.write('234\n')
	f.write('345\n')

a模式更加适合对原有文件进行追加操作的文件,比如说日志文件等。

# 案例:a模式用来在原有的文件内存的基础之上写入新的内容,比如记录日志、注册
# 注册功能
name=input('your name>>: ')
pwd=input('your name>>: ')
with open('db.txt',mode='at',encoding='utf-8') as f:
	f.write('{}:{}\n'.format(name,pwd))

强调 w 模式与 a 模式的异同:

  • 相同点:在打开的文件不关闭的情况下,连续的写入,新写的内容总会跟在前写的内容之后。

  • 不同点:以 a 模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件末尾,新写的内容永远写在最后。

+模式(增强)

+增强模式:可读可写

+不能单独使用,必须配合r、w、a;同理,其性能特点也是依赖于r/w/a的特点的。

例如:rt+的特点取决于rt,当文件不存在时报错,当文件存在时文件指针跳到开始位置。

with open('g.txt',mode='rt+',encoding='utf-8') as f:
	# print(f.read())     # 可读
	f.write('zhangsan')     # 可写
# rt+ 的写是依赖于rt,文件指针跳到开始的位置,所以,rt+的写是覆盖式的写。
    
with open('g.txt',mode='w+t',encoding='utf-8') as f:
	f.write('111\n')
	f.write('222\n')
	f.write('333\n')
	print('====>',f.read())    # 可读,但读不到数据
# wt+ 每次打开文件都是清空文件,文件指针跳转到文件开头,写入之后,紧接着读的话,读取不到任何数据,因为此时的指针位于文件末尾。

with open('g.txt',mode='a+t',encoding='utf-8') as f:
	print(f.read())       # 可读,但读不到数据
	f.write('444\n')
	f.write('5555\n')
	print(f.read())       # 可读,同样读不到数据
# at+ 每次打开文件文件指针都位于文件末尾,直接读的话,读取不到任何数据,因为此时的指针位于文件末尾。写入之后读,也是同样的清况。

x模式(只写)

x:只写,在文件不存在时会创建空文档,在文件存在时报错。一般不会使用,鸡肋。

with open('c.txt',mode='x',encoding='utf-8') as f:
	f.read()        # 报错

with open('d.txt', mode='x', encoding='utf-8') as f:
    f.write('哈哈哈')

b模式(二进制)

读写都是以bytes为单位;
可以针对所有文件;
一定不能指定字符编码,即一定不能指定encoding参数;

1、在操作纯文本文件方面 t模式帮我们省去了编码解码的环节,b模式则需要手动编码与解码,所以此时t模式更为方便
2、针对非文本文件(如图片、视频、音频等)只能使用b模式。

# 处理非文本文件
with open(r'test.jpg',mode='rb') as f:
	res=f.read()  # 硬盘的二进制读入内存—>b模式下,不做任何转换,直接读入内存
	print(res)    # bytes类型—>当成二进制编码(展现的是16进制编码) 
	print(type(res))   # <class 'bytes'>

# 处理文本文件
with open(r'测试.txt',mode='rb') as f:
	res=f.read() # utf-8的二进制
	print(res,type(res))
	print(res.decode('utf-8'))    # 进行解码,实现用户可读

with open(r'd.txt',mode='rt',encoding='utf-8') as f:
	res=f.read() # utf-8的二进制->unicode  这里直接可读是因rt模式也插入实现了解码的操作。
	print(res)     # 用户可读内容

with open(r'测试.txt',mode='wb') as f:
	f.write('你好hello'.encode('gbk'))     # 写的时候进行编码

# 总结:
# 1、在操作纯文本文件方面 `t`模式帮我们省去了编码与解码的环节,b模式则需要手动编码与解码,所以此时`t`模式更为方便
# 2、针对非文本文件(如图片、视频、音频等)只能使用`b`模式。
  • 案例:文件拷贝工具
src_file=input('源文件路径>>: ').strip()
dst_file=input('源文件路径>>: ').strip()
with open(r'{}'.format(src_file),mode='rb') as f1,open(r'{}'.format(dst_file),mode='wb') as f2:
	# res=f1.read()       # 这样一次读取,内存占用过大
	# f2.write(res)
	# 循环读取文件实现:
	for line in f1:
		f2.write(line) 
  • 循环读取文件实现:
# 方式一:自己控制每次读取的数据的数据量,单位为字节
with open(r'test.jpg',mode='rb') as f:
	while True:
	res=f.read(1024) # 1024
	if len(res) == 0:
		break
	print(len(res))


# 方式二:以行为单位读,但是当一行内容过长时会导致一次性读入内容的数据量过大
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
	for line in f:
		print(len(line),line)

with open(r'g.txt',mode='rb') as f:
	for line in f:
		print(line)

with open(r'test.jpg',mode='rb') as f:
	for line in f:
		print(line)

3. 文件操作的常用方法

3.1 读方法

  • read():读取所有内容,执行完该操作后,文件指针会移动到文件末尾。
    • 也可以读取指定字节数,例如:read(10),读取10字节数据。
  • readline():读取一行内容,光标移动到第二行首部。
  • readlines():读取每一行内容,存放于列表中。
# 1、readline:一次读一行
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
	# res1=f.readline()
	# res2=f.readline()
	# print(res2)
	while True:
		line=f.readline()
		if len(line) == 0:
			break
		print(line)

# 2、readlines:类似于read,一起全都读取
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
	res=f.readlines()
	print(res)

read()readlines()都是将内容一次性读入内容,如果内容过大会导致内存溢出,若还想将内容全读入内存,则必须分多次读入,有两种实现方式:

# 方式一
with open('a.txt',mode='rt',encoding='utf-8') as f:
 for line in f:
     print(line) # 同一时刻只读入一行内容到内存中

# 方式二
with open('1.mp4',mode='rb') as f:
 while True:
     data=f.read(1024) # 同一时刻只读入1024个Bytes到内存中
     if len(data) == 0:
         break
     print(data)

3.2 写方法

  • write():将指定的文本写入文件
  • writelines():将内容是字符串的可迭代性数据写入文件中
    • 参数:内容为字符串类型的可迭代数据
# writelines():
with open('h.txt',mode='wt',encoding='utf-8') as f:
	# f.write('1111\n222\n3333\n')
	# l=['11111\n','2222','3333',4444]
	l=['11111\n','2222','3333']
	# for line in l:
	#     f.write(line)
	f.writelines(l)

with open('h.txt', mode='wb') as f:
	l = [
		'1111aaa1\n'.encode('utf-8'),
		'222bb2'.encode('utf-8'),
		'33eee33'.encode('utf-8')
	]

# 补充1:如果是纯英文字符,可以直接加前缀b得到bytes类型
l = [b'1111aaa1\n',b'222bb2',b'33eee33']

# 补充2:'上'.encode('utf-8') 等同于bytes('上',encoding='utf-8')
l = [bytes('上啊',encoding='utf-8'),bytes('冲呀',encoding='utf-8'),bytes('小垃圾们',encoding='utf-8')]

3.3 其他方法(了解)

readable():判断文件对象是否可读

writable():判断文件对象是否可写

flush():手动刷新缓冲区,直接把内容写入到文件

# readable()
with open('a.txt',mode='rb') as f:
	res = f.readable()
	print(res)    # True
    
# writable()
with open('a.txt',mode='rb') as f:
	res = f.readable()
	print(res)   # False
    
# flush()
"""
注意:在刷新缓冲区之前,读写的数据是保留在缓冲区中,并没有真正的写到文件中。
刷新缓冲区 flush() 的情况:
	1.当文件关闭的时候自动刷新缓冲区
	2.当整个程序运行结束的时候自动刷新缓冲区
	3.当缓冲区写满了  会自动刷新缓冲区
	4.手动刷新缓冲区
"""
with open('h.txt', mode='wt',encoding='utf-8') as f:
	f.write('哈')
	f.flush()

4. 文件扩展操作

4.1 文件指针的移动

  • 指针移动的单位都是以bytes/字节为单位

  • 只有一种情况特殊:t模式下的read(n),n代表的是字符个数

with open('aaa.txt',mode='rt',encoding='utf-8') as f:
	res=f.read(4)
	print(res)  # abcd,只读取了4个字符。由此验证t模式下的read(n),n代表的是字符个数
  • 指针移动方法seek()
# f.seek(n,模式):n指的是移动的字节个数

# 模式0:参照物是文件开头位置
f.seek(9,0)  # 从文件开头右移9个
f.seek(3,0)  # 从文件开头右移3个

# 模式1:参照物是当前指针所在位置
f.seek(9,1) # 当前位置右移9个
f.seek(3,1) # 当前位置右移3个,

# 模式2:参照物是文件末尾位置,应该倒着移动,一般是负数。
f.seek(-9,2) 
f.seek(-3,2) 

注意:只有0模式可以在t下使用,1、2必须在b模式下用。

######## 
0模式详解
########
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)
abc你好
 
# 0模式的使用
with open('a.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)     # 参照文件开头移动了3个字节
    print(f.tell()) # 查看当前文件指针距离文件开头的位置,输出结果为3
    print(f.read()) # 从第3个字节的位置读到文件末尾,输出结果为:你好
    # 注意:由于在t模式下,会将读取的内容自动解码,所以必须保证读取的内容是一个完整中文数据,否则解码失败
 
with open('a.txt',mode='rb') as f:
    f.seek(6,0)
    print(f.read().decode('utf-8')) #输出结果为: 好
    
########
1模式详解
########
# 1模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(3,1) # 从当前位置往后移动3个字节,而此时的当前位置就是文件开头
    print(f.tell()) # 输出结果为:3
    f.seek(4,1)     # 从当前位置往后移动4个字节,而此时的当前位置为3
    print(f.tell()) # 输出结果为:7

########
2模式详解
########
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)
abc你好
 
# 2模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(0,2)     # 参照文件末尾移动0个字节, 即直接跳到文件末尾
    print(f.tell()) # 输出结果为:9
    f.seek(-3,2)     # 参照文件末尾往前移动了3个字节
    print(f.read().decode('utf-8')) # 输出结果为:好
 
# 小练习:实现动态查看最新一条日志的效果
import time
with open('access.log',mode='rb') as f:
    f.seek(0,2)
    while True:
        line=f.readline()
        if len(line) == 0:
            # 没有内容
            time.sleep(0.5)
        else:
            print(line.decode('utf-8'),end='')
  • 获取文件指针的当前位置tell()
# a.txt:测试123456
with open('a.txt',mode='rb') as f:
	f.seek(6,0)  
	print(f.tell())     # 6
	f.seek(7,0)
	res=f.read()
	print(res.decode('utf-8'))   # 23456

4.2 文件内容修改

文件对应的是硬盘空间,硬盘不能修改对应着文件本质也不能修改,那我们看到文件的内容可以修改,是如何实现的呢?
大致的思路是将硬盘中文件内容读入内存,然后在内存中修改完毕后再覆盖回硬盘具体的实现方式分为两种:

方式一:文本编辑采用的就是这种方式

  • 实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
  • 优点: 在文件修改过程中同一份数据只有一份
  • 缺点: 会过多地占用内存
with open('c.txt',mode='rt',encoding='utf-8') as f:
	res=f.read()
	data=res.replace('alex','dsb')
	print(data)

with open('c.txt',mode='wt',encoding='utf-8') as f1:
	f1.write(data)

方式二:

  • 实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名
  • 优点: 不会占用过多的内存
  • 缺点: 在文件修改过程中同一份数据存了两份
import os
with open('c.txt', mode='rt', encoding='utf-8') as f, \
        open('.c.txt.swap', mode='wt', encoding='utf-8') as f1:
    for line in f:
        f1.write(line.replace('alex', 'dsb'))

os.remove('c.txt')
os.rename('.c.txt.swap', 'c.txt')

# 验证
f = open('a.txt')
res = f.read()
print(res)
posted @ 2023-08-01 15:34  两口猪头肉OVO  阅读(16)  评论(0编辑  收藏  举报