Python之文件操作


文件操作基本介绍

python open()函数用于打开一个文件,创建一个file对象,相关的方法才可以调用它进行读写。

  • 控制文件读写内容的模式:t和b (t和b不能单独使用,必须跟r/w/a连用)
    • t 文本 (默认的模式)
      • 读写都以str(unicode)为单位的
      • 文本文件
      • 必须指定encoding=’utf-8’ 一种编码格式进行编码
    • b 二进制/bytes binary模式
      • 读写都是以bytes为单位
      • 可以针对所有文件
      • 一定不能指定字符编码encoding参数
  • 控制文件读写操作的模式
    • r 只读模式
    • w 只写模式
    • 只追加写模式
    • +:r+、w+、a+
模式 r r+ w w+ a a+
+ + + +
+ + + + +
创建 + + + +
清除数据 + +
指针在开始 + + + +
指针在结尾 + +

总结:

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

文件操作的流程

  1. 打开文件
  2. 操作文件 读/写
  3. 关闭文件
f = open('test.txt')
res = f.read()
print(res)
f.close()

只指定了文件,其他均按参数的默认值。即mode=’rt’

read()是指读取文本的全部内容

关闭文件,回收资源

with上下文管理

with open()不仅可以处理单个文件,还可以同时处理多个文件,并且在程序执行完成后,自动关闭open的文件

with open('test.txt',mode='rt') as f:
    res = f.read()
    print(res)
with open('test.txt',mode='rt') as f1,open('test.ini',mode='rt') as f2:
    res1 = f1.read()
    res2 = f2.read()
    print(res1)
    print(res2)

指定字符编码

with open('test.txt',mode='rt') as f:
    res = f.read() # t模式,会将f.read()读出的内容解码成unicode
    print(res)

a. test.txt存在硬盘是utf-8格式的二进制

b. read() 执行时,是将test.txt的内容以utf-8格式二进制读入内存。上面的介绍中提到t模式无论读写都是以str(unicode)进行的。所以read()同时将utf-8格式的二进制解码为unicode

c. 这里open()没有指定encoding参数,操作系统会使用系统默认的编码

​ linux系统和mac系统,系统默认的编码是utf-8

​ windows系统,系统默认的编码是gbk

with open('test.txt',mode='rt',encoding='utf-8') as f1:
    res1 = f1.read()
    print(res1)

执行结果:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。

另,可以通过chardet.detect()函数查看当前操作文件的编码格式

chardet.detect(res)

文件操作模式

r模式

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

with open('test.txt',mode='rt',encoding='utf-8') as f1:
    print('第一次读'.center(50,'*'))
    res1 = f1.read()
    print(res1)

    print('第二次读'.center(50, '*'))
    res2 = f1.read()
    print(res2)

执行结果:

***********************第一次读***********************
达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”
***********************第二次读***********************

上面的代码第二次读取的内容为空,因为第一次读完,文件指针已经移到到文件内容的末尾,第二次读取时是从文件的末尾开始读取的,即为空

实例:

在user.txt中保存账号密码:laobai|123

通过程序读取user.txt中的账号信息,与用户输入的账号密码进行比对,验证账号信息是否正确。

input_username = input('请输入账号名称:').strip()
input_password = input('请输入密码:').strip()

with open('user.txt',mode='rt',encoding='utf-8') as f:
    res = f.read()
    username,password = res.split('|')

if input_username == username and input_password == password:
    print('登录成功')
else:
    print('账号或密码错误!')

执行结果:

请输入账号名称:laobai
请输入密码:123
登录成功

在user.txt存放多个账号时的处理方法

laobai|123
plscript|321
nichengwe|123456
input_username = input('请输入账号名称:').strip()
input_password = input('请输入密码:').strip()

with open('user.txt',mode='rt',encoding='utf-8') as f:
    for line in f:
        username,password = line.strip().split('|')
        if input_username == username and input_password == password:
            print('登录成功')
            break
    else:
        print('账号或密码错误!')

执行结果:

请输入账号名称:nichengwe
请输入密码:123456
登录成功

w模式

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

test.txt的内容如下:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”
with open('test.txt',mode='wt',encoding='utf-8') as f:
    f.write("laobai")

执行上面的代码,再次查看test.txt的内容

laobai

注意:

  1. 在以w模式打开文件没有关闭的情况下,连续写入,新的内容总是跟在旧的内容之后;
  2. 如果重新以w模式打开文件,则会清空文件内容;

实例:

test1.txt的内容如下:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
不知道昏迷了多久,朦朦胧胧中,一只手舀给他清凉的泉水。那是个年轻的僧侣,风尘仆仆,似乎远道而来。僧侣向他问路,他绝望的回答:“那个地方已经变成地狱,为什么还要前往。”
僧侣平静的问:“你似乎被心魔所困扰。”
达摩说:“是啊,因为这不幸的结果是我亲手造成的。”他大笑着,自己生来的使命本该是降服邪恶,最终自己反倒变成邪恶。
僧侣却摇了摇头:“你并非邪恶,只是太过懦弱。毁灭容易,建造太难。你一直徘徊在这里:不敢回头,因为不相信自己造就了黑暗;没有勇气离去,因为不敢跨出这片黑暗。”
达摩反问他:“那你呢?明知已降下黑暗,还为何来到这里。”
僧侣“阿弥陀佛”了一声:“贫僧自东方而来,要前往西天取经之地。长安,很快也将黑夜降临。贫僧跋涉千山万水,或许可以找到重建的方法,带回一丝光明。”
达摩握紧了拳头。
他不相信自己生来为魔。
他本应肩挑凡世,拳握初心。
他在黑夜中迷惘,忘记了走不通的路,应该用拳头来打开。
他的本性如此,并不重要。然而见性,方能成佛。
人生是场穷游,偶尔也需要暴走。
道路很远,脚步更长。
告别僧侣,他毅然迈向相反的方向。已回不去故土,就只能继续前进。他要去看看僧侣出发地方——长安,内心的声音告诉他,那里有他寻找的东西。
最深的黑夜,往往是光明所在。
“肩挑凡世,拳握初心。”

test2.txt为空文件

实现test1.txt文件的内容复制到test2.txt中

with open('test1.txt',mode='rt',encoding='utf-8') as f1,\
    open('test2.txt',mode='wt',encoding='utf-8') as f2:
    res = f1.read()
    f2.write(res)

执行完毕后,test1.txt中的内容被拷贝到test2.txt中。再次执行,内容只有一份,与test1.txt的内容一致。

a模式

a模式是只追加写模式,在文件不存在时会创建空文档,在文件存在时文件指针会直接跳到末尾。

test.txt的内容如下:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”
with open('test.txt',mode='at',encoding='utf-8') as f:
    f.write("laobai")

执行上面的代码,再次查看test.txt的内容

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”laobai

注意:w模式与a模式的异同

  1. 相同点:在打开的文件不关闭的情况下,连续的写入,新写的内容总会跟着之前写的内容之后;
  2. 不同点:以a模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件末尾,新写入的内容永远在文件末尾

实例:

在user.txt文件中保存账号密码,文件内容如下:

laobai|123
sch|321
nichengwe|123456

通过程序输入新的账号密码存入user.txt中

input_name = input("请输入注册名:").strip()
input_password = input("请输入密码:").strip()

with open('user.txt',mode='at',encoding='utf-8') as f:
    f.write('{}|{}\n'.format(input_name,input_password))

执行程序后,再次查看user.txt的内容

laobai|123
plscript|321
nichengwe|123456
laobai1|123

+t模式

+不能单独使用,必须配合r、w、a进行使用。而除了均有了读写功能外,其他特性与r、w、a保持一致。

注意:read()和write()都是从光标的位置开始读或写。

a. r+

例子1:

test.txt内容如下:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”

通过r+模式向test.txt读取并写入内容

with open('test.txt',mode='r+',encoding='utf-8') as f:
    print(f.read())
    f.write('nichengwe')
    print(f.read())

执行程序

test.txt新内容如下:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”nichengwe

程序运行结果如下:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”

分析程序运行结果:

  1. f.read() 读取test.txt中的全部内容,并将文件指针指向文件内容的末尾
  2. write(‘nichengwe’) 在文件末尾写入nichengwe,并将文件指针指向文件内容的末尾
  3. 再次执行f.read() 从文件末尾开始读取,读取到的是空

例子2:

test.txt内容如下:

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”nichengwe

通过r+模式向test.txt写入内容

with open('test.txt',mode='r+',encoding='utf-8') as f:
    f.write('nichengwe')

r模式打开文件,文件指针在文件内容的开头

write()是在文件开头开始写入内容

执行程序后,test.txt的内容如下:

nichengwe为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”nichengwe

test.txt文件内容的前几个字被新写入的内容覆盖了

b. w+

例子1:

test.txt的内容如下:

nichengwe为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
“肩挑凡世,拳握初心。”nichengwe

通过w+模式读取文件内容:

with open('test.txt',mode='w+',encoding='utf-8') as f:
    print(f.read())

w模式打开文件,首先清空文件所有内容,文件指针在文件内容的开头

程序执行后,没有读取到任何内容,因为w模式已经将文件的所有内容清空了。

例子2:

test.txt的内容为空白

通过w+模式写入并读取文件内容:

with open('test.txt',mode='w+',encoding='utf-8') as f:
    f.write('nichengwe')
    f.write('laobai')
    f.write('baby2016的天空')
    print("------->",f.read())

执行结果为:

------->

执行程序后,test.txt的内容为:

nichengwelaobaibaby2016的天空

分析程序运行结果:

  1. w模式打开文件,会清空文件的所有内容,并将文件指针指向文件的开头
  2. write() 从文件指针的位置写入内容,写入了3次,文件指针指向文件的末尾
  3. read() 从文件指针的位置开始读取,读取的内容为空

c. a+

例子:

test.txt的内容如下:

nichengwelaobaibaby2016的天空

通过a+模式读写文件:

with open('test.txt',mode='a+',encoding='utf-8') as f:
    print("=======》", f.read())
    f.write('\nnichengwe\n')
    f.write('laobai\n')
    f.write('baby2016的天空\n')
    print("------->",f.read())

执行结果:

=======-------> 

执行程序后test.txt的内容如下:

nichengwelaobaibaby2016的天空
nichengwe
laobai
baby2016的天空

分析程序运行结果:

  1. a模式打开文件,文件指针会在文件的末尾
  2. read() 从文件指针的位置开始读取,内容为空,文件指针依旧在文件末尾
  3. write() 从文件指针的位置开始写入内容,多次写入后,文件指针依旧在文件末尾
  4. 再次read() 从文件指针的位置开始读取,内容为空,文件指针依旧在文件末尾

b模式

处理图片:

with open('test.jpg',mode='rb') as f:
    res = f.read() # 硬盘的二进制读入内容 ——> b模式下,不做任何转换
    print(res)  # bytes类型——> 当成二进制
    print(type(res))

执行结果:

b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x90\x00\x90\x00\x00\xff\xe1\x00tExif\x00\x00MM\x00*\x00\x00\x00\x08\x00\x04\x01\x1a\x00\x05\x00\x00\x00\x01\x00\x00\x00>\x01\x1b\x00\x05\x00\x00\x00\x01\x00\x00\x00F\x01(\x00\x03\x00\x00\x00\x01\x00\x02\x00\x00\x87i\x00\x04\x00\x00\x00\x01\x00\x00\x00N\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x01\x00\x00\x00\x90\x00\x00\x00\x01\x00\x02\xa0\x02\x00\x04\x00\x00\x00\x01\x00\x00\x02\x92\xa0\x03\x00\x04\x00\x00\x00\x01\x00\x00\x01
<class 'bytes'>

处理文本:

with open('user.txt',mode='wb') as f:
    f.write('你好hello'.encode('gbk'))

由于本机使用的是macos,默认编码为utf-8,而这里程序使用了gbk,所以写入的你好会显示为乱码。

例子:

# 文件拷贝工具
file1 = input('源文件路径:').strip()
file2 = input('目标文件路径:').strip()

with open(r'{}'.format(file1),mode='rb') as f1,\
    open(r'{}'.format(file2),mode='wb') as f2:
    #res = f1.read()  # 这种方式内存占用过大
    for line in f1:
        f2.write(line)

readline()和readlines()

readline()

按行读取,每次读一行

例子1:

with open('test1.txt',mode='rt',encoding='utf-8') as f:
    res1 = f.readline()
    res2 = f.readline()
    print(res1)
    print(res2)

执行结果:

达摩是为降伏邪恶而生的。曾经有人这样预言。

他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。

例子2:

with open('test1.txt',mode='rt',encoding='utf-8') as f:
    while True:
        line = f.readline()
        if len(line) == 0:
            break
        print(line)

执行结果:

达摩是为降伏邪恶而生的。曾经有人这样预言。

他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。

告别僧侣,他毅然迈向相反的方向。已回不去故土,就只能继续前进。他要去看看僧侣出发地方——长安,内心的声音告诉他,那里有他寻找的东西。

最深的黑夜,往往是光明所在。

“肩挑凡世,拳握初心。”

readlines()

按行读取,将所有行都读取出来,每一个作为一个元素,组成一个大列表。

例子1:

with open('test1.txt',mode='rt',encoding='utf-8') as f:
    res = f.readlines()
    print(res)

执行结果:

['达摩是为降伏邪恶而生的。曾经有人这样预言。\n', '他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。\n', '但国破家亡就在转瞬之间。父王被叛徒毒死,黑色风暴席卷了大地。人们哀嚎着被杀害。最后,他被逼入绝境。自己的拳头可以面对最强大的敌人,却无法对同胞出手。尽管血脉相连的亲人们在权力的诱惑下陷入疯魔,变得面目可憎。\n', '道路很远,脚步更长。\n', '告别僧侣,他毅然迈向相反的方向。已回不去故土,就只能继续前进。他要去看看僧侣出发地方——长安,内心的声音告诉他,那里有他寻找的东西。\n', '最深的黑夜,往往是光明所在。\n', '“肩挑凡世,拳握初心。”']

f.read()和f.readlines()都是将内容一次性读入内存,如果内容过大会导致内存溢出。

例子2:

可以将列表里的内容,通过writelines()形式按行写入

l = ['达摩是为降伏邪恶而生的。曾经有人这样预言。\n', '他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。\n', '但国破家亡就在转瞬之间。父王被叛徒毒死,黑色风暴席卷了大地。人们哀嚎着被杀害。最后,他被逼入绝境。自己的拳头可以面对最强大的敌人,却无法对同胞出手。尽管血脉相连的亲人们在权力的诱惑下陷入疯魔,变得面目可憎。\n', '道路很远,脚步更长。\n', '告别僧侣,他毅然迈向相反的方向。已回不去故土,就只能继续前进。他要去看看僧侣出发地方——长安,内心的声音告诉他,那里有他寻找的东西。\n', '最深的黑夜,往往是光明所在。\n', '“肩挑凡世,拳握初心。”']

with open('test.txt',mode='wt',encoding='utf-8') as f:
    f.writelines(l)

执行后,test.txt的内容为

达摩是为降伏邪恶而生的。曾经有人这样预言。
他是王者之子,生在一棵优美的菩提树下,从小由德高望重的大师教导经书和拳法,那时他日日刻苦修行,救助身边不幸的人们,并对自己的使命坚信不疑。
但国破家亡就在转瞬之间。父王被叛徒毒死,黑色风暴席卷了大地。人们哀嚎着被杀害。最后,他被逼入绝境。自己的拳头可以面对最强大的敌人,却无法对同胞出手。尽管血脉相连的亲人们在权力的诱惑下陷入疯魔,变得面目可憎。
道路很远,脚步更长。
告别僧侣,他毅然迈向相反的方向。已回不去故土,就只能继续前进。他要去看看僧侣出发地方——长安,内心的声音告诉他,那里有他寻找的东西。
最深的黑夜,往往是光明所在。
“肩挑凡世,拳握初心。”

文件指针移动

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

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

with open('test.txt',mode='rt',encoding='utf-8') as f:
    res = f.read(4)
    print(res)

seek()

f.seek(n,模式):

  • n指的是移动的字节个数
  • 模式参照物文件指针的位置
    • 0:参照物是文件开头位置
    • 1:参照物是当前指针所在位置
    • 2:参照物是文件末尾位置(这个模式是反向向前移动)

只有模式0可以在t模式下使用,1和2必须在b模式下用

f.teel() 获取当前文件指针的位置

例子:0模式

with open('test.txt',mode='rb') as f:
    f.seek(9,0)
    f.seek(3,0)
    print(f.tell())

执行结果:

3

例子:1模式

with open('test.txt',mode='rb') as f:
    f.seek(9,1)
    f.seek(3,1)
    print(f.tell())

执行结果:

12

例子:2模式

with open('test.txt',mode='rb') as f:
    f.seek(-9,2)
    print(f.tell())
    f.seek(-3,2)
    print(f.tell())

执行结果:

882
888

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
 上一篇
Python之函数 Python之函数
Python函数是指组织好的、可重复使用的、用来实现单一或相关联功能的代码段。Python函数包含系统中自带的一些函数、第三方函数、以及用户自定义的函数。
2022-10-07
下一篇 
Linux基础介绍 Linux基础介绍
Linux 是一种自由和开放源码的类 UNIX 操作系统。目前主流的的Linux发行版本有两大系列Debian(包括Ubuntu)和Red Hat(包括Fedora、Centos)。此篇以CentOS7为基础介绍Linux的一些使用知识。
2022-10-03
  目录