第28~29讲:文件(因为懂你、所以永恒)

一 文件相关内容

1 什么是文件

是指保存数据的文本,有不同的格式:".exe"--可执行文件、".txt--文本文件"、".ppt--PPT演示文件"、".jpg--图片"、".mp4--音频文件"、".avi--视频文件"

2 python中文件操作的流程:

  • 1. 打开文件,得到一个文件句柄并赋值给一个变量  f = open("test.txt","r",encoding="utf-8") 
  • 2. 通过句柄对文件进行操作  content = f.read()
  • 3. 关闭文件 f.close()

需要注意的点:

  • 1. 尽管在这里我们说通过python操作(读写)文件,但实际的过程是python不能直接度读写文件,只能通过向操作系统提供的相应接口发出请求,要求打开一个文件对象,然后由操作系统的接口来完成对文件的具体操作。这里的文件句柄即文件描述符,唯一标识了一个文件对象。
  • 2. 完整的文件操作一定包含了最后一步关闭处理,否则会造成系统资源的严重浪费,每个程序员都应明确践行这点。
  • 3. 对文件进行读写操作时,系统维护了一个指针,指向当前处理完毕后所处文件的位置。
    • 可通过f.seek(n) 将文件指针移动到n处,n为0则表示移动到文件开头位置
    • 可通过f.tell()来获取当前文件指针的位置

3 文件的基本操作

(1)打开文件:open()方法

(2)读取文件:read()方法

(3)写入、创建:write方法

(4)关闭文件:close()方法

(5)文件重命名:rename()方法——os.rename(current_file_name, new_file_name)

(6)文件删除:remove()方法——os.remove(file_name)

具体内容可参考以下几个网站,因为之前已经了解过,这里就当总结了:

4 文件的打开模式

文件的操作需遵循以下规范:句柄变量 = open(文件路径,打开模式,文件编码)

其中文件路径最好定义绝对路径,除非相对路径非常确定,打开模式默认的是r模式,文件编码默认为utf-8。

(1)普通打开模式:

  • r模式:只读模式,不可对文件进行写处理。
  • w模式:写模式,如果文件不存在则先创建空文件然后写入指定内容,否则则直接覆盖写原文件。注意该模式下不可对文件进行读处理
    • 覆盖写处理:指的是测试文件仅仅保留了write进去的内容
    • open文件之后第一次写是覆盖写。close前的第二次写却是追加写。
  • a模式:追加模式,写入的内容总是在当前文件的末尾追加进去,无论怎么移动指针。注意该模式下仍然不可对文件进行读处理。

(2)同时读写模式

  • r+模式:读写模式,同时具备读和写权限,但写入时默认是把内容写入到文件末尾进行追加写,而不是覆盖写,除非在写入时先把指针移动到文件头。
  • w+模式:写读模式,同时具备写和读权限,先创建新的空文件,然后写入内容。该模式实际不常用。
  • a+模式:追加内容的同时可读,注意新内容一定是在源文件末尾追加,同时在读取文件内容时文件指针默认就在文件末尾因此不移动文件指针到文件头部是不能读取到文件内容的。

(3)二进制打开模式

  • 二进制文件要以二进制模式进行读取,该模式下打开文件时不能传递编码参数
  • 常见的二进制格式文件有音频、视频、网络传输的文件(ftp二进制传输模式),因此处理这些文件上时需要用到二进制打开模式。
  • 常见的二进制打开模式下的操作有rb(读二进制),wb(写二进制)和ab(追加二进制)。
  • 需要注意的是
    • wb写入时一定要在write后面调用encode()方法将字符串转换为二进制(字节码)写入,同理rb时如果要输出字符串,则需要在read后面调用decode()将二进制字节码转换为字符串输出,原因是python3中对字节码和字符串进行了严格的区分

5 文件对象属性

二 一个任务(第29讲)代码讲解

  • 题目:
  • record.txt文件
  • 小客服:小甲鱼,今天有客户问你有没有女朋友?
    小甲鱼:咦??
    小客服:我跟她说你有女朋友了!
    小甲鱼:。。。。。。
    小客服:她让你分手后考虑下她!然后我说:"您要买个优盘,我就帮您留意下~"
    小甲鱼:然后呢?
    小客服:她买了两个,说发一个货就好~
    小甲鱼:呃。。。。。。你真牛!
    小客服:那是,谁让我是鱼C最可爱小客服嘛~
    小甲鱼:下次有人想调戏你我不阻止~
    小客服:滚!!!
    ===============================
    小客服:小甲鱼,有个好评很好笑哈。
    小甲鱼:哦?
    小客服:"有了小甲鱼,以后妈妈再也不用担心我的学习了~"
    小甲鱼:哈哈哈,我看到丫,我还发微博了呢~
    小客服:嗯嗯,我看了你的微博丫~
    小甲鱼:哟西~
    小客服:那个有条回复“左手拿著小甲魚,右手拿著打火機,哪裡不會點哪裡,so 
    
    easy ^_^”
    小甲鱼:T_T
    ============================
    小客服:小甲鱼,今天一个会员想找你
    小甲鱼:哦?什么事?
    小客服:他说你一个学生月薪已经超过12k了!!
    小甲鱼:哪里的?
    小客服:上海的
    小甲鱼:那正常,哪家公司?
    小客服:他没说呀。
    小甲鱼:哦
    小客服:老大,为什么我工资那么低啊??是时候涨涨工资了!!
    小甲鱼:啊,你说什么?我在外边呢,这里好吵吖。。。。。。
    小客服:滚!!!
    View Code
  • 最终代码及注释:
  •  1 def save_file(boy,gril,count):                       # 定义save_file函数,用来将分片后的内容保存到新的文件中
     2     file_name_boy = 'boy_' + str(count) + '.txt'     # 利用str(count)的值形成不同的文件名:boy1.txt  boy2.txt  boy3.txt
     3     file_name_gril = 'gril_' + str(count) + '.txt'
     4     
     5     boy_file = open(file_name_boy,'w')               # 打开文件boy1.txt  boy2.txt  boy3.txt
     6     gril_file = open(file_name_gril,'w')
     7     
     8     boy_file.writelines(boy)                         # 将列表boy中的值分行写入文件中
     9     gril_file.writelines(gril)
    10     
    11     boy_file.close()                                 # 关闭文件
    12     gril_file.close()
    13 
    14 def split_file(filename):                            # 定义split_file函数用来将文件中的内容切片
    15     f = open(filename)                               # 打开文件
    16     boy = []                                         # 用来存储切片后得到的小甲鱼的对话内容
    17     gril = []                                        # 用来存储切片后得到的小客服的对话内容
    18     count = 1                                        # 用来统计文件的个数,即构建不同的文件名
    19     for each_line in f:                              # 依次遍历f文件中的每行内容
    20         # print(each_line)                             # 依次打印每行内容,可省略,这里我是为了看清程序的运行过程加的
    21         if each_line[:6] != "======":                # 对每行的内容进行切片操作,即保留每行的前6个字符,判断它是否等于6个等号
    22             (role,line_spoken) = each_line.split(':',1) # 对每行内容进行切片,利用split方法
    23             if role == '小甲鱼':                     # 根据role的不同的值,将切片后得到的内容分别添加到boy和gril列表中
    24                 boy.append(line_spoken)
    25             else:
    26                 gril.append(line_spoken)
    27         else:
    28             print(f"boy = {boy}\n")                  # 用来查看boy的值,观察运行过程,可不加
    29             print(f"gril = {gril}\n")
    30             save_file(boy,gril,count)                # 如果每行切片后的值为6个等号,则调用save_file函数将boy和gril列表的值分别保存在一个文件中
    31             
    32             boy = []                                 # 清空boy的值,用来存储下一个文件的内容
    33             gril = []                                # 清空boy的值,用来存储下一个文件的内容
    34             count += 1                               # 更新count值
    35     print(f"boy = {boy}\n")                          # 用来查看boy的值,观察运行过程,可不加
    36     print(f"gril = {gril}\n")
    37     save_file(boy,gril,count)                        # 调用save_file函数保存最后一个文件的内容
    38     f.close()                                        # 关闭文件
    39 
    40 split_file('record.txt')                             # 调用split_file函数执行程序
    View Code
  • 我遇到的一个错误讲解(看视频看好多人都遇到了):
    • 报错内容:
      ValueError: not enough values to unpack (expected 2, got 1)
    • 原因分析:
    • (1)if判断语句:“if each_line[:6] != "======":”出错
      • 写代码过程中,等号的个数一定是6个,即跟each_line[:6]切片结果相同,如果将它改成each_line[:9],相应的判断条件里面的等号也要改成9个
    • (2)字符串切片语句:“(role,line_spoken) = each_line.split(':',1)”出错
      • 语句中的切片字符是冒号,如果自己动手创建record.txt文件中的冒号是中文,在程序代码中是英文,就会出现中英文不对应的情况,程序就会报错
      • 原理是:实际并没有对文件中的每行完成切片操作,所以等号右边仍旧只有一行字符串,即一个值,而等号左边有两个变量名,所以会出现不对应的情况

三 课后作业

第28讲:

测试题部分

0. 下边只有一种方式不能打开文件,请问是哪一种,为什么?

1 >>> f = open('E:/test.txt', 'w')   # A
2 >>> f = open('E:\test.txt', 'w')   # B
3 >>> f = open('E://test.txt', 'w')  # C
4 >>> f = open('E:\\test.txt', 'w')  # D

答:B不能打开文件。
Windows在路径名中既可以接受斜线(/)也可以接受反斜线(\),不过如果使用反斜线作为路径名的分隔符的话,要注意使用双反斜线(\\)进行转义,否则Python会将反斜线进行转义,例如(\n)看成一个换行符,(\t)看作一个制表符等。

1. 打开一个文件我们使用open()函数,通过设置文件的打开模式,决定打开的文件具有那些性质,请问默认的打开模式是什么呢?
答:open()函数默认的打开模式是'rt',即可读、文本的模式打开。

2. 请问 >>> open('E:\\Test.bin', 'xb') 是以什么样的模式打开文件的?
答:以“可写入以及二进制模式”打开文件“E:\\Test.bin”。
这里要注意的是'x'和'w'均是以“可写入”的模式打开文件,但以'x'模式打开的时候,如果路径下已经存在相同的文件名,会抛出异常,而'w'模式的话会直接覆盖同名文件。
因此,'w'模式打开文件会比较危险,容易导致此前的内容遗失,因此使用'w'模式打开文件前先检查该文件名是否已经存在显得非常重要!下节课小甲鱼会教你如何安全的打开一个文件^_^

3. 尽管Python有所谓的“垃圾回收机制”,但对于打开了的文件,在不需要用到的时候我们仍然需要使用f.close()将文件对象“关闭”,这是为什么呢?
答:Python拥有垃圾收集机制,会在文件对象的引用计数降至零的时候自动关闭文件,所以在Python编程里,如果忘记关闭文件并不会造成内存泄漏那么危险。
但并不是说就可以不要关闭文件,如果你对文件进行了写入操作,那么你应该在完成写入之后进行关闭文件。因为Python可能会缓存你写入的数据,如果这中间断电了神马的,那些缓存的数据根本就不会写入到文件中。所以,为了安全起见,要养成使用完文件后立刻关闭的优雅习惯。

4. 如何将一个文件对象(f)中的数据存放进列表中?
答:list(f),是不是非常的方便!


5. 如何迭代打印出文件对象(f)中的每一行数据?
答:直接使用for语句把文件对象迭代出来即可:

1 for each_line in f:
2         print(each_line)


6. 文件对象的内置方法f.read([size=-1])作用是读取文件对象内容,size参数是可选的,那如果设置了size=10,例如f.read(10),将返回什么内容呢?
答:将返回从文件指针开始(注意这里并不是文件头哦)的连续10个字符。

7. 如何获得文件对象(f)当前文件指针的位置?
答:f.tell()会告诉你^_^

8. 还是视频中的那个演示文件(record.txt),请问为何f.seek(45, 0)不会出错,但f.seek(46)就出错了呢?

1 >>> f.seek(46)
2 46
3 >>> f.readline()
4 Traceback (most recent call last):
5   File "<pyshell#18>", line 1, in <module>
6     f.readline()
7 UnicodeDecodeError: 'gbk' codec can't decode byte 0xe3 in position 4: illegal multibyte sequence

答:因为使用f.seek()定位的文件指针是按字节为单位进行计算的,演示文件(record.txt)是以GBK进行编码的,按照规则,一个汉字需要占用两个字节,f.seek(45)的位置位于字符“小”的开始位置,因此可以正常打印,而f.seek(46)的位置刚好位于字符“小”的中间位置,因此按照GBK编码的形式无法将其解码!

  

动动手

第28讲:

0. 尝试将文件(  OpenMe.mp3 )打印到屏幕上

1 f = open('OpenMe.mp3')
2 for each_line in f:
3         print(each_line, end='')
4 f.close()

1. 编写代码,将上一题中的文件(OpenMe.mp3)保存为新文件(OpenMe.txt)

1  f1 = open('OpenMe.mp3')
2  f2 = open('OpenMe.txt', 'x')        # 使用”x”打开更安全
3  f2.write(f1.read())
4  f2.close()
5  f1.close()

第29讲:

0. 编写一个程序,接受用户的输入并保存为新的文件,程序实现如图:

 1 # -*- encoding:utf-8 -*-
 2 def file_write(file_name):
 3     f = open(file_name,"w+",encoding = "utf-8")
 4     print("请输入内容【单独输入\':w\'保存退出】:")
 5     
 6     while True:
 7         write_some = input()
 8         if write_some != ':w':
 9             f.write(f"{write_some}")
10         else:
11             break
12     f.close()
13 
14 file_name = input("请输入文件名:")
15 file_write(file_name)
View Code

 

1. 编写一个程序,比较用户输入的两个文件,如果不同,显示出所有不同处的行号与第一个不同字符的位置,程序实现如图:

 我的代码(功能是不完整的,只有两个文件内容完全不同的时候才能正常运行):

 1 # -*- encoding:utf-8 -*-
 2 def file_compare(file1,file2):
 3     i = 1
 4     j = 0
 5     f1 = open(file1)
 6     f2 = open(file2)
 7     while True:
 8         string1 = f1.readline()
 9         string2 = f2.readline()
10         print(string1)
11         print(string2)
12         if string1 == string2:
13             break
14         else:
15             print(f"第{i}行不一样")
16             j += 1
17         i += 1
18     print(f"两个文件共有【{j}】处不同:")
19     f1.close()
20     f2.close()
21 
22 file1 = input("请输入需要比较的头一个文件名:")
23 file2 = input("请输入需要比较的另一个文件名:")
24 file_compare(file1,file2)
我的代码

小甲鱼的代码:

 1 def file_compare(file1, file2):
 2     f1 = open(file1)
 3     f2 = open(file2)
 4     count = 0 # 统计行数
 5     differ = [] # 统计不一样的数量
 6 
 7     for line1 in f1:
 8         line2 = f2.readline()
 9         count += 1
10         if line1 != line2:
11             differ.append(count)
12 
13     f1.close()
14     f2.close()
15     return differ
16 
17 file1 = input('请输入需要比较的头一个文件名:')
18 file2 = input('请输入需要比较的另一个文件名:')
19 
20 differ = file_compare(file1, file2)
21 
22 if len(differ) == 0:
23     print('两个文件完全一样!')
24 else:
25     print('两个文件共有【%d】处不同:' % len(differ))
26     for each in differ:
27         print('第 %d 行不一样' % each)
小甲鱼

 

2  编写一个程序,当用户输入文件名和行数(N)后,将该文件的前N行内容打印到屏幕上,程序实现如图:

 

 我的代码(在小甲鱼的代码基础上,加了一点with语句和easygui编程的内容):

 1 import easygui as g
 2 
 3 # 在控制台打印
 4 def file_view(file_name,line_num):
 5     print(f'\n文件{file_name}的前{line_num}内容如下:\n')
 6     with open(file_name) as f:
 7         for i in range(int(line_num)):
 8             print(f.readline(),end=' ')
 9 
10 # 利用easygui编程
11 def file_view_gui(file_name,line_num):
12     text = ''
13     with open(file_name) as f:
14         title = '显示文件内容'
15         msg = f'\n文件{file_name}的前{line_num}内容如下:\n'
16         for i in range(int(line_num)):
17             TEXT = f.readline()
18             text += f'{TEXT}\n'
19         g.textbox(msg,title,text)
20 
21 
22 file_name = input(r'请输入要打开的文件(C:\\test.txt):')
23 line_num = input('请输入需要显示该文件前几行:')
24 file_view(file_name,line_num)
25 file_view_gui(file_name,line_num)
ex29_2.py

 

3. 呃,不得不说我们的用户变得越来越***钻了。要求在上一题的基础上扩展,用户可以随意输入需要显示的行数。(如输入13:21打印第13行到第21行,输入:21打印前21行,输入21:则打印从第21行开始到文件结尾所有内容)

 

 小甲鱼的代码:

 1 def file_view(file_name,line_num):
 2     if line_num.strip() == ':':
 3         begin = '1'
 4         end = '-1'
 5     
 6     (begin,end) = line_num.split(':')
 7     
 8     if begin == '':
 9         begin = '1'
10     if end == '':
11         end = '-1'
12     
13     if begin == '1' and end == '-1':
14         prompt = '的全文'
15     elif begin == '1':
16         prompt = f'从开始到{end}'
17     elif end == '-1':
18         prompt = f'从{begin}到结束'
19     else:
20         prompt = f'从第{begin}行到第{end}行'
21     
22     print(f'\n{file_name}文件{prompt}的内容如下:')
23     
24     begin = int(begin) - 1
25     end = int(end)
26     lines = end - begin
27     
28     with open(file_name) as f:
29         for i in range(begin):      # 用于将文件指针移动到第begin行处
30             f.readline()
31         
32         if lines < 0:
33             print(f.read())
34         else:
35             for j in range(lines):
36                 print(f.readline(),end='')
37 
38 file_name = input(r'请输入要打开的文件(C:\\test.txt):')
39 line_num = input('请输入需要显示的行数【格式如 13:21 或 :21 或 21: 或 : 】:')
40 file_view(file_name, line_num)
ex29_4.py

 

4. 编写一个程序,实现“全部替换”功能。

小甲鱼代码:

 1 def file_replace(file_name,rep_word,new_word):
 2     with open(file_name) as f_read:
 3         
 4         content = []
 5         count = 0
 6         
 7         for eachline in f_read:
 8             if rep_word in eachline:
 9                 count = eachline.count(rep_word)  # 感觉应该用这个
10                 eachline = eachline.replace(rep_word,new_word)
11             content.append(eachline)
12         
13         decide = input(f'\n文件{file_name}中共有{count}个【{rep_word}】\n您确定要把所有的【{rep_word}】替换为【{new_word}】吗?\n【YES/NO】:')
14         
15         if decide in ['YES','Yes','yes']:
16             with open(file_name,'w') as f_write:
17                 f_write.writelines(content)
18 
19 file_name = input('请输入文件名:')
20 rep_word = input('请输入需要替换的单词或字符:')
21 new_word = input('请输入新的单词或字符:')
22 file_replace(file_name,rep_word,new_word)
小甲鱼
posted @ 2020-07-24 20:40  洛兰123  阅读(252)  评论(0编辑  收藏  举报