文件操作

什么是文件

文件是操作系统提供给用户/应用程序操作硬盘的一种虚拟的概念/接口。

用户/应用程序想要读写硬盘上保存的数据时,调用文件接口的过程:

  1. 向操作系统发送相关请求;
  2. 操作系统执行请求修改硬盘数据的请求
总结:用户\应用程序  ----->  操作系统  -----> 硬盘

用代码操作文件的基本流程

open方法

缺点,需要手动的去关闭文件

# 1. 打开文件,由应用程序向操作系统发起系统调用open(...)
# 操作系统打开该文件,对应一块硬盘空间,并返回一个文件对象赋值给一个变量file
file = open("02.txt", mode="rt", encoding="utf-8")
# 2. 调用文件对象下的读/写方法,会被操作系统转换为读/写硬盘的操作
content = file.read()

print(content)
# # 3. 向操作系统发起关闭文件的请求,回收系统资源
file.close()
# 文件中的内容
jack-521
eva-113
amigo-445

with open

文件操作完成后一定要及时回收操作系统资源和程序资源。但是这个file.close()del file很容易在编写代码的过程中被忘记掉。

上下文管理器,1、在执行完子代码块后,with 会自动执行file.close()

通过文件句柄file操作读写操作,所有的读写操作写在with下面的自带吗块中。程序退出字代码块的时候会自动回收系统资源。

with open("02.txt", mode="rt", encoding="utf-8") as file:
    content = file.read()
    print(content)
# 文件中的内容
jack-521
eva-113
amigo-445

同时打开多个文件

with open("02.txt", mode="rt", encoding="utf-8") as f1, open("db.txt", mode="wt", encoding="utf-8") as f2:
    content = f1.read()
    f2.write(content)

如果太长,可以用\换行,下面功能和上面一样

with open("02.txt", mode="rt", encoding="utf-8") as f1, \
        open("db.txt", mode="wt", encoding="utf-8") as f2:
    content = f1.read()
    f2.write(content)

mode参数详解

t模式(默认)

控制文件读写内容的模式:t模式、b模式; **指的是读写的内容是字符串还是Bytes

t模式(默认的模式),一般如果是操作文本,t可以简写,当读取文件的时候,也可以直接写成with open("db.txt", encoding=utf-8) as file,省略掉文件模式。

wt = w
rt = r(或者不写)
at = a

注意点:

  1. 读写都是以str(Unicode)为单位的
  2. 文本文件,必须指定encoding="utf8"

b模式(二进制/bytes)

如果文件的mode为b模式,那么不需要指定encoding参数,不然会报错,常见的后缀:图片、音乐、视频、压缩文件等,都是需要使用b模式去读写的,所有文件都可以使用二进制模式去操作。

常见的文件模式

'r':以只读模式打开文件。文件的指针将会放在文件的开头,如果文件不存在则会引发FileNotFoundError错误。

'w':以写入模式打开文件。如果文件存在,则会清空文件内容;如果文件不存在,则会创建一个新文件。文件的指针将会放在文件的开头。

'x':以独占写入模式打开文件。如果文件存在,则会引发FileExistsError错误;如果文件不存在,则会创建一个新文件。文件的指针将会放在文件的开头。

'a':以追加模式打开文件。如果文件存在,则文件指针将会放在文件的末尾;如果文件不存在,则会创建一个新文件。

'b':以二进制模式打开文件。这个模式可以与上述模式组合使用,如'rb'、'wb'等。它用于处理二进制文件,如图片、视频等。

't':以文本模式打开文件(默认模式)。这个模式可以与上述模式组合使用,如'rt'、'wt'等。它用于处理文本文件,如TXT文件等。

'+':以读写模式打开文件,可以与上述模式组合使用,如'r+'、'w+'等。它允许对文件进行读写操作。

总结:

r只读模式

w只写模式

a只追加写模式

+r+w+a+ (了解即可)

一般情况下用到的r w a模式比较多,b模式不需要指定encoding,t和b不能单独使用,必须跟r/w/a模式连用。

通过什么编码写入的文件,那么读取文件就需要采用一样的编码才可以。

读写操作模式 说明 备注
r 只读模式,默认 文件没有关闭时,第二次读将从指针上次停留的位置开始接着往下读
w 只写模式 mode="w" file=read()会报错
a 追加模式 a模式内不可读文件、指针再最后
+ 读写模式,了解 在平时工作中,我们只单纯使用r/w/a,要么只读,要么只写,一般不用可读可写的模式
x 只写模式(不可读,了解) 文件不存在新建;文件存在则报错
b 二进制 不需要指定encoding

r模式演示

with open("02.txt", mode="rt", encoding="utf-8") as file:
    content = file.read()
    print(content)

w模式演示

with open("02.txt", mode="wt", encoding="utf-8") as file:
    content = file.read()
    print(content)

a模式演示

with open("02.txt", mode="at", encoding="utf-8") as file:
    content = file.read()
    print(content)

b模式演示

import requests

url = "https://pic.netbian.com/uploads/allimg/231111/010350-16996358307f29.jpg"
response = requests.get(url)
content = response.content

with open("BeautifulGirl.jpg", "wb") as file:
    file.write(content)
    print("写入完成!")

自制简易的文件拷贝工具

old_path = r"D:\Python\girl.jpg"
new_path = r"D:\Python\img\girl.jpg"

with open(r"{}".format(old_path), "rb") as f1, \
        open(r"{}".format(new_path), "wb") as f2:
    data = f1.read()
    f2.write(data)

编码方式模式

编码方式模式指的是open()中的encoding参数的使用。

encoding参数是用在文本文件中,且仅使用在t模式下。

当读写操作模式是r时,encoding表示解码的方式,即将其他二进制数据解码为Unicode格式二进制数据读到内存;

当读写操作模式是w时,encoding表示编码的方式,即将Unicode格式二进制数据编码为其他二进制数据保存到硬盘。

扩展资料:Python文本文件读写操作时的字符编码问题

操作文件的方法

读操作

read()

读取所有内容,执行完该操作后,文件指针会移动到文件末尾。

喜欢的句子.txt

你懂的越多,你就越像这个世界的孤儿。
with open("喜欢的句子.txt", encoding="utf-8") as file:
    data = file.read()
    print(data)  # 你懂的越多,你就越像这个世界的孤儿。

注意:read(n)在t模式是n指字符数,b模式下n指字节数

with open("喜欢的句子.txt", encoding="utf-8") as file:
    data = file.read(5)  # 读取五个字符
    print(data)  # 你懂的越多

读取一张图片

with open("BeautifulGirl.jpg", "rb") as file:
    content = file.read()
    print(content)
x90C/\x07\xb6\x1dO\xeb\xe9\xe9\xc5{\x8c\xdf\xb2/\xc4\xe89\xb4\xf8\xa1\xa1\xdc \x18\xdby\xe1i\x83\x93\xc6s%\xbe\xa9\n\x9c\x0e\xfb\x17\x04\x8f\xc2\x9c\xdf\xb2\xff\x00\xc6(X\x08<U\xe0\xfb\xb5\x1fx\xcbg\xaaY\xb6\xe0{*=\xe0\xc7\x7f\xbc\xa7\'\x1c\xf4\xa5\xec\xf7\xbbZ\xf4\xbbk\xa5\xae\x9e\x9d\xf6\xed~\xa6\xb1\xc5\xd2N\xf7OO=\xd5\xba;$\xdd\xee\xb6\xd9\xf5\xb1\xf3\xbe\xad\xf0\x9f\xe1\x86\xb8%]W\xc0\x1e\x0e\xbd\xf3\x83nk\x8f\x0f\xe92H\xe4\xf5,\xc6\xdfq-\x93\x93\x9f\xd35\xe3:\xdf\xec\x83\xfb;......
with open("BeautifulGirl.jpg", "rb") as file:
    while True:
        data = file.read(1024)
        if len(data) == 0:
            break
        print(data)
x90C/\x07\xb6\x1dO\xeb\xe9\xe9\xc5{\x8c\xdf\xb2/\xc4\xe89\xb4\xf8\xa1\xa1\xdc \x18\xdby\xe1i\x83\x93\xc6s%\xbe\xa9\n\x9c\x0e\xfb\x17\x04\x8f\xc2\x9c\xdf\xb2\xff\x00\xc6(X\x08<U\xe0\xfb\xb5\x1fx\xcbg\xaaY\xb6\xe0{*=\xe0\xc7\x7f\xbc\xa7\'\x1c\xf4\xa5\xec\xf7\xbbZ\xf4\xbbk\xa5\xae\x9e\x9d\xf6\xed~\xa6\xb1\xc5\xd2N\xf7OO=\xd5\xba;$\xdd\xee\xb6\xd9\xf5\xb1\xf3\xbe\xad\xf0\x9f\xe1\x86\xb8%]W\xc0\x1e\x0e\xbd\xf3\x83nk\x8f\x0f\xe92H\xe4\xf5,\xc6\xdfq-\x93\x93\x9f\xd35\xe3:\xdf\xec\x83\xfb;......

readline

读取一行内容,光标移动到第二行首部

喜欢的句子.txt

你懂的越多,你就越像这个世界的孤儿。
山腰的风景很美
然而
我还是
想去山顶看看
with open("喜欢的句子.txt", encoding="utf-8") as file:
    data = file.readline()
    print(data)
    data = file.readline()
    print(data)
    data = file.readline()
    print(data)
    data = file.readline()
    print(data)
    data = file.readline()
    print(data)
你懂的越多,你就越像这个世界的孤儿。

山腰的风景很美

然而

我还是

想去山顶看看
with open("喜欢的句子.txt", encoding="utf-8") as file:
    while True:
        data = file.readline()
        if len(data) == 0:
            break
        print(data)
你懂的越多,你就越像这个世界的孤儿。

山腰的风景很美

然而

我还是

想去山顶看看

readlines

读取每一行内容,存放于列表中

列表中的每一项后面都会包括一个\n的换行符

喜欢的句子.txt

你懂的越多,你就越像这个世界的孤儿。
山腰的风景很美
然而
我还是
想去山顶看看
with open("喜欢的句子.txt", encoding="utf-8") as file:
    data = file.readlines()
    print(data)
['你懂的越多,你就越像这个世界的孤儿。\n', '山腰的风景很美\n', '然而\n', '我还是\n', '想去山顶看看']

读操作注意事项

注意1:三个读函数都可以用在t模式和b模式下,t模式读出来的是字符,b模式下读出来的是Bytes字节(二进制)

注意2:read(n)在t模式是n指字符数,b模式下n指字节数

写操作

write

记得加上换行符

with open("喜欢的句子.txt", "wt", encoding="utf-8") as file:
    sentence = "你懂的越多,你就越像这个世界的孤儿。\n跟着光,成为光,散发光。\n"
    file.write(sentence)
你懂的越多,你就越像这个世界的孤儿。
跟着光,成为光,散发光。

通过b模式去写入,不需要指定encoding

with open("喜欢的句子.txt", "wb") as file:
    sentence = "你懂的越多,你就越像这个世界的孤儿。\n跟着光,成为光,散发光。\n"
    file.write(sentence.encode("utf-8"))
你懂的越多,你就越像这个世界的孤儿。
跟着光,成为光,散发光。

writelines

记得列表里面的每一项后面加上一个换行符,不然内容会写到一起

dataList = ['你懂的越多,你就越像这个世界的孤儿。\n', '跟着光,成为光,散发光。\n']
with open("喜欢的句子.txt", "wt", encoding="utf-8") as file:
    file.writelines(dataList)
你懂的越多,你就越像这个世界的孤儿。
跟着光,成为光,散发光。
with open("喜欢的句子.txt", "wb") as file:
    file.writelines(["跟着光\n".encode("utf-8"), "成为光\n".encode("utf-8"), "散发光\n".encode("utf-8")])
跟着光
成为光
散发光

如果是纯英文字符,可以直接加前缀b得到bytes类型

sentence = b"talk is cheap, show me the code.\n"
with open("喜欢的句子.txt", "wb") as file:
    file.writelines([sentence])
talk is cheap, show me the code.

写操作注意事项

  1. 如果是纯英文字符,可以直接加前缀b得到bytes类型
  2. '上'.encode('utf-8') 等同于bytes('上',encoding='utf-8')
  3. 强调:read()readlines()都是将内容一次性读入内容,如果内容过大会导致内存溢出,若还想将内容全读入内存,则必须分多次读入.

了解的方法

f.readable()  # 文件是否可读 r模式为True w或者a模式为Fale
f.writable()  # 文件是否可读 r模式为False
f.closed  # 文件是否关闭
f.encoding  # 如果文件打开模式为b,则没有该属性
f.flush()  # 立刻将文件内容从内存刷到硬盘
f.name
f.tell()  # 获取文件指针当前位置

bytes方法

sentence = "talk is cheap, show me the code."
data = sentence.encode("utf-8")

print(data)  # b'talk is cheap, show me the code.'
sentence = "talk is cheap, show me the code."
data = bytes(sentence, "utf-8")

print(data)  # b'talk is cheap, show me the code.'

避免读文件时内存溢出的方式

方式1

with open("喜欢的句子.txt", encoding="utf-8") as file:
    for line in file:
        print(line)  # 同一时刻只读入一行内容到内存中

方式2

with open("喜欢的句子.txt", encoding="utf-8") as file:
    while True:
        data = file.read(1024)  # 同一时刻只读入1024个Bytes到内存中
        if len(data) == 0:
            break
        print(data)

控制文件指针移动seek

# 之前文件内指针的移动都是由读/写操作而被动触发的,若想读取文件某一特定位置的数据,则需要用f.seek方法主动控制文件内指针的移动,详细用法如下:

# f.seek(指针移动的字节数,模式控制): 
# 模式控制:
	# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的(参照物永远是文件开头位置)
	# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的(参照物是指针当前所在位置)
	# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的(参照物是文件末尾的位置,应该倒着数)
    
# 补充:当指针移动的字节数n为整数时,表示往后移动;当n为负数时,表示往前移动;

# 强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用,012三种模式b模式下都可以用.
# 注意:由于在t模式下,会将读取的内容自动解码,所以必须保证读取的内容是一个完整中文数据,否则解码失败报错

content.txt

hero小满
with open("content.txt", encoding="utf-8") as file:
    file.seek(4, 0)
    print(file.read())  # 小满
with open("content.txt", "rb") as file:
    file.seek(4, 0) # 4个指针
    file.seek(3, 1) # 一个中文字占位三个字节
    print(file.read().decode("utf-8"))  # 满
with open("content.txt", "rb") as file:
    file.seek(-6, 2)  # 当seek为2的时候,第一个参数需要为负数
    print(file.read().decode("utf-8"))  # 小满

文件修改实战

1、硬盘空间是无法修改的,硬盘中数据的更新都是用新内容覆盖旧内容
2、内存中的数据是可以修改的

文件对应硬盘空间,硬盘不能修改也就是说文件不能修改。

想要修改文件的思路是将硬盘上的文件内容读入内存,在内存中修改完毕后再覆盖到硬盘上

方案1实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件

优点: 在文件修改过程中同一份数据只有一份

缺点: 会过多地占用内存

content.txt

全体渣男起立,入队宣誓。

我宣示:做个俗人,不谈亏欠,不负遇见。做个俗人,没心没肺。没感情。
安稳自由,谈笑风生。做个俗人,只沾酒沾烟,不染感情。
做个俗人,忘南不归。做个俗人,贪财好色,一身正气。
做个俗人,你我无心,永不用心。从此寻花问柳,闭口不谈一生厮守。从此红灯绿酒,再也不掐牵谁。
with open("content.txt", encoding="utf-8") as file:
    data = file.read()

with open("content.txt", "wt", encoding="utf-8") as file:
    file.write(data.replace("渣男", "海王"))
全体海王起立,入队宣誓。

我宣示:做个俗人,不谈亏欠,不负遇见。做个俗人,没心没肺。没感情。
安稳自由,谈笑风生。做个俗人,只沾酒沾烟,不染感情。
做个俗人,忘南不归。做个俗人,贪财好色,一身正气。
做个俗人,你我无心,永不用心。从此寻花问柳,闭口不谈一生厮守。从此红灯绿酒,再也不掐牵谁。

方案2实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名。

优点: 不会占用过多的内存。

缺点: 在文件修改过程中同一份数据存了两份

import os

with open("content.txt", encoding="utf-8") as f1, \
    open("content.txt.swap", "wt", encoding="utf-8") as f2:
    for line in f1:
        f2.write(line.replace("渣男", "海王"))

os.remove("content.txt")
os.rename("content.txt.swap", "content.txt")
全体海王起立,入队宣誓。

我宣示:做个俗人,不谈亏欠,不负遇见。做个俗人,没心没肺。没感情。
安稳自由,谈笑风生。做个俗人,只沾酒沾烟,不染感情。
做个俗人,忘南不归。做个俗人,贪财好色,一身正气。
做个俗人,你我无心,永不用心。从此寻花问柳,闭口不谈一生厮守。从此红灯绿酒,再也不掐牵谁。

原始文件内容如下:

张一蛋 山东 179 49 12344234523
李二蛋 河北 163 57 13913453521
王全蛋 山西 153 62 18651433422

题目要求:

通过文件操作变成下面的结果

张一蛋(妇女主任) 山东 179 49 12344234523
张二蛋(村长) 河北 163 57 13913453521
王全蛋(书记) 山西 153 62 18651433422

data.txt就是原始文件内容,result.txt就是最终结果

dic = {
    "张一蛋": "张一蛋(妇女主任)",
    "李二蛋": "张二蛋(村长)",
    "王全蛋": "王全蛋(书记)"
}
with open("data.txt", "r+", encoding="utf-8") as f1, open("result.txt", "w", encoding="utf-8") as f2:
    for line in f1.read().splitlines():
        name = line.split()[0]  # 转成列表取出姓名
        new_text = line.replace(name, dic.get(name))
        f2.write(new_text + "\n")
posted @ 2023-12-05 16:39  小满三岁啦  阅读(5)  评论(0编辑  收藏  举报