当前位置: 首页 > news >正文

python文件处理

文件处理之open()功能的使用

文章目录

  • 文件处理之open()功能的使用
    • 一、引入
      • 什么是文件?
      • 为什么要有文件?
    • 二、文件操作的基本流程
      • 基本流程
      • 资源回收与with上下文管理
      • 指定操作文本文件的字符编码
    • 三、文件的操作模式
    • 1.控制文件读写的操作模式
      • `r`模式的使用:只读模式
      • `w`模式的使用:只写模式
      • `a`模式的使用:只追加写模式
      • + 模式的使用(了解):r+、w+、a+模式
    • 2.控制文件内容内容的模式
        • 区分b模式与t模式在读写单位、争对文件类型、是否指定字符编码
    • 四、操作文件的方法
      • 重点
        • 文件的读操作
        • 文件的读操作的其他方法
      • 文件的写操作
        • 文件写的其他方法
      • 了解
        • 其他了解操作:`f.readable()`、`f.writeable()`、`f.encoding`、`f.name`、`f.colsed`
    • 五、控制文件指针操作
        • `f.seek`的应用
        • 案例:0模式详解
        • 案例二:1模式详解
        • 案例三:2模式详解
      • 什么是往文件末尾追加一行内容?是用编辑器打开文件,还是a操作?

一、引入

什么是文件?

文件是操作系统提供给用户或者是应用程序用于操作硬盘的虚拟概念或者说接口。

为什么要有文件?

用户通过应用程序可以通过文件,将数据永久保存到硬盘中。

详细的说:用户和应用程序操作的是文件,对文件的所有操作,都是向操作系统发送系统调用,然后再由操作系统将其转换成具体的硬盘的操作。

二、文件操作的基本流程

基本流程

伪代码实例:

    f = open(r'指定你要打开文件的文件路径', mode='rt,)
    r:
        raw_str,指的是原生得字符串,里面得反斜杠\,都会被视为普通字符。
    windows中路径分隔符解决方案:
        方案一(推荐):在文件路径前加r
        方案二:windows中会把左斜杠/,默认转换成右斜杠\。使用 'C:/a.txt/nb/c/d.txt',默认会转成 'C:\a.txt\nb\c\d.txt'。
    绝对路径与相对路径:
        相对路径:会以你当前执行文件所在的文件夹下为基准去找。
            优点:简洁
            缺点:当你执行文件移动,被操作文件将找不到。
        绝对路径:从盘符的根目录下开始直接定位到你需要操作的文件地址。
            优点:执行文件移动到任意目录中都可以找到所需要操作的文件地址。
            缺点:臃肿
    rt:
        默认控制文件读写操作的模式是r,只读模式。
        默认控制文件读写内容的模式是t,t文本模式。
    
原理解析:
    f = open 占用了2部分的内存空间:
        第一部分:open占用的是操作系统的的内存空间。由操作系统的映射到了应用程序的内存空间中。
        第二部分:我们知道f的值是一种变量,而变量都是归属于python的内存空间,也就是说。f占用的就是应用程序的内存空间。

操作文件的基本流程:

# 1、打开文件
f = open(r'aaa/a.txt', mode='rt')   # open是向操作系统发起系统调用。

# 2、操作文件:读写/文件
res = f.read()  # f.read()向操作系统发送请求,让操作系统把文件从硬盘读入内存。
print(res)

# 3、关闭文件
f.close()  # 告诉操作系统,回收刚刚读入文件内存占用的内存空间。(需要考虑的操作:操作系统打开文件,如果没有及时关闭,就会无意义的占用操作系统的内存空间,并且还会无意义的占用操作系统的操作文件数。如果没有指定关闭,操作系统不会立即清除该内存空间的占用,只有过了一定的时间,操作系统才会把没有意义的占用清除,而这种清除时间间隔对于我们来说感觉不到,但是对于计算机来说,是非常大的损耗。补充:操作系统打开文件的文件是有限的,如果开打文件过多,就会影响你的电脑性能。)

f.read()  # 注意:f.close()之后,不能在对文件进行操作了。这个时候操作系统已经把调用的文件关闭回收了,如果继续调用就会报错(ValueError: I/O operation on closed file.)。

del f  # 注意:del f 的顺序要放在f.close()后面。(不需要考虑的操作:有python的GC机制管理)

image-20201125210725625

资源回收与with上下文管理

引入:

with称之为:上下文管理,上文帮你打开文件,下文帮你关闭文件。

强调:

open对应的赋值对象f,称之为文件对象、又有人称之为‘文件句柄’(柄:游戏手柄干嘛的?就是用来操作游戏的。那么我也也可以推理出,其实‘文件句柄’,就是用来远程控制文件的。)

打开一个文件包含两部分资源:应用程序的变量f和操作系统打开的文件。在操作完毕一个文件时,必须把与该文件的这两部分资源全部回收,回收方法为:

1、f.close() #回收操作系统打开的文件资源
2del f #回收应用程序级的变量

其中del f一定要在f.closs()之后,否则就会导致系统文件打开无法关闭,白白占用了资源,而python自动的垃圾回收机制决定了我们无需考虑del f,这就要求我们,在操作完毕文件后,一定要记住f.close(),虽然我们如此强调,但是大多数读者还是会不由自主地忘记f.close(),考虑到这一点,python提供了with关键字来帮我们管理上下文

# 1、with打开单个文件
with open('a.txt', mode='rt') as f:     
    res = f.read()
    print(res)
# 上面没有指定f.close(), with会等其子代码块群不运行完毕以后,会帮你自动调用f.close()。


# 2、with同时打开多个文件,用逗号分隔开即可
# with open('a.txt', mode='rt') as f1, open('b.txt', mode='rt') as f2:  # 当你的一行代码过长,可以使用右斜杠\(注意:右斜杠\并不是换行,而是转义且分隔。)
with open('a.txt', mode='rt') as f1, \
        open('b.txt', mode='rt') as f2:
    res1 = f1.read()
    res2 = f2.read()
    print(res1)
    print(res2)

指定操作文本文件的字符编码

  • 上面没有指定操作文本的字符编码,接下来我们需要指定:

    f = open(...)是由操作系统打开文件,如果打开的是文本文件,会涉及到字符编码问题,如果没有为open指定编码,那么打开文本文件的默认编码很明显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是utf-8。
    这就用到了字符编码的问题:若要保证不乱码,文件以什么方式存的,就要以什么方式打开。
    
    f = open('a.txt','r',encoding='utf-8')
    
    

三、文件的操作模式

1.控制文件读写的操作模式

  • r(默认的):只读
  • w:只写
  • a:之追加写

r模式的使用:只读模式

特点:当文件不存在时,r模式会报错。当文件存在时,文件指针跳到开始位置,也就是说从文件开头位置开始。只能读,不能写。

""""
先手动创建c.txt文件文件,文件内容如下:
哈哈哈哈
""""
# 1、以t模式为基础进行内存操作
with open('c.txt', mode='rt', encoding='utf-8') as f:
    print('res'.center(50, '-'))
    res = f.read()  # 执行f.read(),文件指针从开头一下子跳到文件末尾,同时也把文件内容一次性由硬盘读入内存。问题❓ 当文件过大时,会把内存干懵!!!
    print(res)

    print('res1'.center(50, '-'))
    res1 = f.read()  # 注意:上面执行了f.read()操作,文件指针在末尾,这个时候怎么读。都读不出来了。
    print(res1)

    # 注意:上面读出文件的每行内容,在每行内容末尾都有个换行符\n。
'''
-----------------------res------------------------
哈哈哈哈
-----------------------res1-----------------------
'''
    
# 2、案例:优化之前的用户登录操作,之前的用户站账户信息不能永久保存,这里我们通过存入文件将用户账户信息永久保存。网址连接(第9题:编写用户登录接口写法一):https://www.cnblogs.com/yang1333/p/12357167.html
# 经验总结:for+else,用于用户登录功能中时,else中的子代码块放用户登录失败以后的打印信息。要知道用户登录成功,一定是break退出循环,这个else错误信息也就不执行,也没有执行的必要。但是反过来说,用户登录失败,break语句也就一定不会执行到,那么else中的子代码块的执行,也就不会被break打段。
'''
先手动创建user.txt文件文件,文件内容如下:
egon:123
lili:456
jack:777
tom:666
'''
inp_username = input("please input your username>>:").strip()
inp_password = input("please input your password>>:").strip()

with open('user.txt', mode='rt', encoding='utf-8') as f:
    for line in f:  # 遍历文件对象,line取出的是文件中的每1行的内容。(注意:取出的内容都是str类型,且每一行的末尾都有换行符\n,直接打印print(line)看不到,所以下面使用print([line])放入列表中显示)
        # print([line])
        # line.split()  # 注意:不能直接使用split(),因为文件每行内容末尾(除了最后1行文件内容没有换行符)都有换行符\n,我们要像下面?一样先使用strip()去除\n。
        res = line.strip()  # 使用strip()默认不加参数,去除空白,需要注意的是 \n这个换行符也叫空白符,也可以去除。
        # print([res])

        # 使用split(':')切分字符串,转换成列表。第一个值作为用户账号,第二个值作为用户密码。
        username, password = res.split(':')
        if username == inp_username and password == inp_password:
            print('登录成功!')
            break
    else:
        print('登陆失败!账号或密码错误!')

w模式的使用:只写模式

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

强调:

  1. 在文件不关闭的情况下,连续的写入,后写的内容一定跟在前写内容的后面
  2. 如果重新以w模式打开文件,则会清空文件内容
# 1、引入
with open('d.txt', mode='wt', encoding='utf-8') as f:
    # f.read()  # 不可读,只能写。
    f.write('哈哈哈哈我擦嘞\n')  # 注意:每一次运行,在w模式下,都会先清空原内容。文件指针位于开始位置。(提示:这里的\n是位-------)
'''
d.txt文件中显示结果:
哈哈哈哈我擦嘞

'''    
    
# 1.1 强调1:w模式打开文件没有关闭的情况下,连续的写,新的内存总是更在旧的之后。
with open('d.txt', mode='wt', encoding='utf-8') as f:
    f.write('我擦嘞1\n')
    f.write('我擦嘞2\n')
    f.write('我擦嘞3\n')
'''
d.txt文件中显示结果:
我擦嘞1
我擦嘞2
我擦嘞3

'''
    
# 1.2 强调2:重新以w模式开打文件,则会先清空文件内容。
with open('d.txt', mode='wt', encoding='utf-8') as f:
    f.write('我擦嘞1\n')
with open('d.txt', mode='wt', encoding='utf-8') as f:
    f.write('我擦嘞2\n')    
with open('d.txt', mode='wt', encoding='utf-8') as f:
    f.write('我擦嘞3\n') 
# 以上内容,d.txt文件中,只有:我擦嘞3\n

# 2、案例:w模式用来创建全新文件(应用:文件与文件的copy工具)
'''
# 2.1 下面的内容属于拓展内容:(注意:如果该内容没有添加,必须保证输入的源路径存在,没有存在,程序则会报错。)
import os

source_file = input("源文件路径>>:").strip()
destination_file = input("目标路径>>:").strip()

# 先判断源文件路径在不在,不在帮助用户在指定路径下创建源文件及其内容
if not os.path.isfile(source_file):
    with open(r'{}'.format(source_file), mode='wt', encoding="utf-8") as f:
        for i in range(4):
            f.write('我擦嘞:%s\n' % i)
'''

# 2.2 实际内容:
with open(r'{}'.format(source_file), mode='rt', encoding='utf-8') as f,\
        open(r'{}'.format(destination_file), mode='wt', encoding='utf-8') as f1:
    # 将读出的源文件内容,写入用户指定的目标件中
    # source_file_content = f.read()
    # f1.write(source_file_content)

    # 优化:?上面2步我们发现使用的是f.read(),它是一下子把文件从硬盘读入内存,如果文件过大,会把内存干懵逼!
    for line in f:
        f1.write(line)        

a模式的使用:只追加写模式

特点:只追加写。在文件不存在时,会创建空文档,文件指针只能在开头,其实这里的开头也是末尾。在文件存在时,文件指针会直接跳到文件内容末尾。不能读,只能追加写。

总结:a模式与w模式区别以及a模式与w模式作用如下:

a模式与w模式区别:

  • 相同之处:在文件打开不关闭的情况下,连续的写入,新写的内容总会跟在之前写的内容之后。
  • 不同之处:a模式重新开打文件,只要打开文件,它的指针就会跳到末尾。w模式重新打开文件,直接清空文件。

a模式与w模式作用:

  • w模式用来创建新文件。(通常用来copy文件)
  • a模式争对老文件。(通常用来记录日志文件,通常用与用户注册)
# 1、引入
with open('e.txt', mode='at', encoding='utf-8') as f:
    f.write('我擦嘞1\n')
    f.write('我擦嘞2\n')
    f.write('我擦嘞3\n')  
   
# 2、a模式案例:实现用户注册功能(每来一个用户,打开文件,把新用户的信息追加写到已存在的用户账户末尾。)
"""
# 2.1 下面的内容属于拓展内容:
import os

if not os.path.isfile('user.txt'):
    with open(r'user.txt', mode='wt', encoding='utf-8') as f:
        f.write('egon:123\nalex:alex3714\n')

register = False
while True:
    username = input('请输入注册账号>>:').strip()
    password = input('请输入注册密码>>:').strip()
    confirm_password = input('请确认注册密码>>:').strip()

    # 如果username,password,confirm_password有一个为空那么让用户重新注册
    if '' in [username, password, confirm_password]:
        print('注册账号、注册密码、确认注册密码不能为空。')
        continue
    # 如果用户2次密码不一致,那么让用户重新注册
    if password != confirm_password:
        print('用户2次密码不一致')
        continue
        # 如果用户以及存在,那么让用户重新注册
    with open(r'user.txt', mode='rt', encoding='utf-8') as f:
        for line in f:
            if username == line.strip().split(':')[0]:
                print('用户已经存在,重新注册')
                break
        else:
            register = True
            print('正在为您注册中'.center(50, '-'))
            import time
            time.sleep(2)
    if not register:
        continue

    # 用户注册满足条件,写入文件,注册该用户
    with open(r'user.txt', mode='at', encoding="utf-8") as f:
        f.write('{}:{}\n'.format(username, password))
    print('恭喜你!注册成功!'.center(50, '-'))
    break
"""

# 2.2 实际内容:
username = input('your useranem>>:').strip()
password = input('your password>>:').strip()
with open(r'db.txt', mode='at', encoding='utf-8') as f:
    f.write(f'{username}:{password}\n')

+ 模式的使用(了解):r+、w+、a+模式

# r+ w+ a+ :可读可写
#在平时工作中,我们只单纯使用r/w/a,要么只读,要么只写,一般不用可读可写的模式
"""
注意:+不能单独使用,必须配合r、w、a
"""
# 1、r+模式:以r基准,g.txt文件不存在,直接就报错
'''
提前创建user.txt文件,写入以下内容:
111
222
333
444
5555
444
5555

中国
中国
中国

'''
with open('g.txt', mode='rt+', encoding='utf-8') as f:
    print(f.read())  
    f.write('中国\n')  # 如果上面执行了f.read()操作,文件指针默认就会移动到文件末尾,f.write('中国\n')操作就会把内容写入到文件末尾。(类似于a操作)如果上面没有执行f.read()操作,文件指针默认在开始位置,输入的内容多少,就会把g.txt中的原内容覆盖多少。

# 2、w+模式:以w基准
with open('h.txt', mode='w+t', encoding='utf-8') as f:
    f.write('111\n')
    f.write('222\n')
    f.write('333\n')
    # w模式打开文件,清空源文件,文件指针在起始位置。以上f.write操作会按照先后顺序写入h.tx文件。
    print('=======>', f.read()) 
    # ?上面使用f.read()操作,这个时候文件指针在末尾,并不能读出任何内容。

# 3、a+模式:以a基准    
with open('g.txt',mode='at+',encoding='utf-8') as f:
    # print('f.read()')  # a模式下,一打开文件,文件指针就在末尾。f.read()操作并不能读出任何文件内容。
    f.write('中国')  # 更具上面得知文件指针就在末尾,f.write操作会把输入的内容追加到文件内容的末尾去。

2.控制文件内容内容的模式

大前提t b模式均不能单独使用,必须与r/w/a 之一结合使用

区分b模式与t模式在读写单位、争对文件类型、是否指定字符编码

t(默认的):文本模式

  1. 读写文件都是义字符串为单位的
  2. 只能针对文本文件
  3. 必须指定enconding参数

b:二进制模式

  1. 读写文件都是以bytes/二进制为单位的
  2. 可以针对所有文件
  3. 一定不能指定encoding参数

拓展:b模式下争对英文,会显示英文字符本身。本身bytes类型就是以硬盘默认存的时候的编码格式并以16进制的方式显示,也是转换的过程,像英文为什么这样,这些都只是给你的显示的结果。

实例:t 模式的使用

# t 模式:如果我们指定的文件打开模式为r/w/a,其实默认就是rt/wt/at
 with open('a.txt',mode='rt',encoding='utf-8') as f:
     res=f.read() 
     print(type(res)) # 输出结果为:<class 'str'>

 with open('a.txt',mode='wt',encoding='utf-8') as f:
     s='abc'
     f.write(s) # 写入的也必须是字符串类型

 #强调:t 模式只能用于操作文本文件,无论读写,都应该以字符串为单位,而存取硬盘本质都是二进制的形式,当指定 t 模式时,内部帮我们做了编码与解码

实例:b模式的使用

# b: 读写都是以二进制位单位
 with open('1.mp4',mode='rb') as f:
     data=f.read()
     print(type(data)) # 输出结果为:<class 'bytes'>

 with open('a.txt',mode='wb') as f:
     msg="你好"
     res=msg.encode('utf-8') # res为bytes类型
     f.write(res) # 在b模式下写入文件的只能是bytes类型

实例:演示b模式读写都是以二进制为单位、可以针对所有文件、不能指定字符编码

# 引入:t模式只能读文本文件,不能读取其他文件需要用到b模式。
# with open(r'爱nmlgb的爱情.mp4', mode='rt', encoding='utf-8') as f:
    # f.read()  # 报错:UnicodeDecodeError

# 演示一:b模式下读取图片文件:不能指定encoding  
# with open(r'test.jpg', mode='rb', encoding='utf-8') as f:
#     res = f.read()  
    # 注意:二进制模式一定不能指定encoding,指定会报错(ValueError: binary mode doesn't take an encoding argument)。
    # print(res, type(res))
    
# 演示二:b模式下读取文本文件:打印字符使用decode指定的编码,要与原来存入硬盘的编码格式相同。
with open(r'd.txt', mode='rb') as f:
    date_bytes = f.read()
    # f.read由b模式的加持下,直接将硬盘的二进制读入内容,b模式下,不做任何转换,直接读入内存。(提示:物理层面其实读出来的内容就是二进制,但是python进行处理模式会转成bytes类型,以后我们看到bytes类型,就把它直接当作二进制去看。)

    print(date_bytes)   # b'\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88a'
    print(date_bytes.decode('utf-8'))  # 哈哈哈a
    # 文本模式下,需要解码才能打印显示字符,不然打印出来的像上面一样是硬盘中躺着的是按照utf-8编码的二进制的格式。上面显示的是16进制的格式,每一个斜杠划分了每一个bytes类型。我们看到有三个哈哈哈字符,它们的utf-8对应中文字符采用的是3个字节,因此我们可以看到三个相同的‘xe5\x93\x88’。但是b模式下争对英文,会显示英文字符本身。本身bytes类型就是以硬盘默认存的时候的编码格式并以16进制的方式显示,也是转换的过程,像英文为什么这样,这些都只是给你的显示的结果。
    
# 演示三:b模式下对文本文件执行写操作:使用encode指定一种编码格式往文件中写,读出文件时就需要使用decode指定同样的编码方式读。
with open(r'e.txt', mode='wb') as f:
    f.write('你好hello'.encode("gbk"))  # 以gbk编码格式往e.txt文件中写,读出文件需要使用gbk编码方式读。

    
# 演示四:b模式下对文本文件执行写操作:使用encode将多种编码格式写入文件,将引发文件读取时困难。
with open('f.txt', mode='wb') as f:
     # 冒号前使用utf-8格式写,冒号后使用gbk编码格式写。这样读取文件的时候,就需要判断如果是冒号前,那么就使用utf-8解码。如果冒号后,那么就需要使用gbk解码。
    f.write("嘿嘿嘿你好!".encode('utf-8'))  # 使用utf-8打开看到:嘿嘿嘿你好!:��������ã�
    f.write(":".encode('utf-8'))
    f.write("哈哈哈你好!".encode('gbk'))  # 使用gbk打开看到:鍢垮樋鍢夸綘濂斤紒:哈哈哈你好!

强调:b模式与t模式对比

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

小练习:编写拷贝工具

src_file=input('源文件路径: ').strip()
dst_file=input('目标文件路径: ').strip()
with open(r'%s' %src_file,mode='rb') as read_f,open(r'%s' %dst_file,mode='wb') as write_f:
    for line in read_f:
        # print(line)
        write_f.write(line)

文件拷贝功能优化:用户不仅可以copy文本文件,还能copy任何的文件

soruce_file_path = input('源文件路径>>:').strip()
target_file_path = input('目标文件路径>>:').strip()
with open(fr'{soruce_file_path}', mode='rb') as f1, \
    open(fr'{target_file_path}', mode='wb') as f2:
    for line in f1:
        f2.write(line)

循环读取文件:当行数据过大是使用while循环读取,for循环将不再适用

  • 前提:了解区分t模式、b模式怎么区分每一行
    • t模式下,文本文件以换行符为区分。
    • b模式下,任意文件也是以换行符为区分。
  • 以下forwhile例子再次强调:for循环可以做的事情,while循环也能做到

实例一:for循环读取

  • 结束条件:在循环结束时,文件读取结束。
  • 空行是判断循环时是否有数据的关键条件。
with open(r'g.txt', mode='rt') as f:
    for line in f:
        print(len(line), line)  # 如果当前行时空行,长度为1
with open(r'g.txt', mode='rb') as f:
    for line in f:
        print(len(line), line)  # 如果当前行是空行,b'\n' 长度为1
with open(r'test.jpg', mode='rb') as f:
    for line in f:
        print(line)  # b模式下,line都是bytes类型

实例二:while循环读取

  • 结束条件:在使用f.read读取文件内容的长度为0时,文件读取结束。
  • 使用while循环时,for循环没有的优点:
    • while循环可以自己控制每次读取的数据量(使用时段:当文件一行内容很很长的情况下,使用while循环)
with open(r'test.jpg', mode='rb') as f:
    while True:
        res = f.read(1024)   # f.read()操作,可以指定每次读取数据的字符个数。这里1024指的是,当前次读取1024个字符。
        
        print(len(res), res)  # 如果文件内容读取完毕以后,继续执行f.read(1024)操作,这个时候读取的内容长度就是0,所以我们使用这个条件来作为文件循环取值结束的条件。
        
        # if not len(res):  # 或者使用下面的写法
        if len(res) == 0:
        if not len(res):
            break

补充:得到bytes类型的三种方式

得到bytes类型的三种方式:
    1、字符串编码之后的结果
        '上'.encode("utf-8")
        bytes('上', encoding='utf-8)
    2、b'必须是纯英文字符'
    3、b模式下打开文件,f.read()读出的内容。

x 模式(了解)

  • x模式:文件不存在则创建,存在则报错。只写模式,不可读
# 1、示例一:提前创建a.txt文件,文件存在则报错
# with open('a.tx', mode='xt', encoding='utf-8') as f:  # 报错:FileExistsError: [Errno 17] File exists: 'a.txt'
#     pass  

# 2、示例二:当前目录下文件b.tx不存在并创建 + 不可读。
with open(r'b.txt', mode='xt', encoding='utf-8') as f:
    # f.read()  # x模式只写模式,不可读。执行了f.read以后就。报错:io.UnsupportedOperation: not readable
    
# 3、示例三:当前目录下c.txt不存在 + 只写
with open(r'c.txt', mode='xt', encoding='utf-8') as f:
    f.write('哈哈哈\n')
    
"""
拓展:换行符的概念由来
    linux换行符:\n
    windows换行符:\r\n   
        \r\n由来:\r表示回到行首 \n表示换行(举例:打印机)
        优化:\r\n符号过多,过多占用空间。直至到了现在python3中使用:\n会在windows中默认指定的就是\r\n,而到了linux中默认指定的说就是\n。
"""

四、操作文件的方法

重点

文件的读操作

f.read()  # 读取所有内容,执行完该操作后,文件指针会移动到文件末尾
f.readline()  # 读取一行内容,光标移动到第二行首部
f.readlines()  # 读取每一行内容,存放于列表中

强调:

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

  • 方式一:同一时刻只读入一行内容到内存中

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

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

文件的读操作的其他方法

f.readline`、`f.readlines
with open(r'g.txt', mode='rt', encoding='utf-8') as f:
    # 1、f.readline基本使用:一次读一行。注意:以换行符进行区分每一行
    # print(f.readline())  # 111
    # print(f.readline())  # 222
    # print(f.readline())  # 333

    # 2、模仿f.read操作:使用while循环 + f.readline用来。
    while True:
        line = f.readline()
        print([line], len(line))
        if not len(line):
            break
            
# 3、f.readlines详细介绍:把所有内容读出来,以每一行为区分,一个一个的放入列表中。
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
    res = f.readlines()
    print(res)  # ['111\n', '222\n', '333\n', '444\n', '5555\n', '\n', '\n']
    
# 4、f.readlines与f.read使用注意:read和readlines都是将数据一次性读入内存,在数据内容过大时会导致内存溢出,需谨慎使用

文件的写操作

f.write('1111\n222\n')  # 针对文本模式的写,需要自己写换行符
f.write('1111\n222\n'.encode('utf-8'))  # 针对b模式的写,需要自己写换行符
f.writelines(['333\n','444\n'])  # 文件模式
f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式

文件写的其他方法

f.writelines
# 1、f.writelines基本使用:把列表中的多个值取出来,写入文件中。(注意:列表中的每个元素都必须指定字符串类型。 )
# 验证:列表中的每个元素都必须指定字符串类
with open('h.txt',mode='wt',encoding='utf-8') as f:
    # f.writelines(['111', '222', '333', 444])  # 注意:列表中的每个元素都必须指定字符串类型,否则报错(TypeError: write() argument must be str, not int)

# 验证:换行符的使用
with open('j.txt',mode='wt',encoding='utf-8') as f:    
    f.writelines(['111', '222', '333', '444'])  # 这里的内容会全部在一行显示:111222333444
with open('h.txt',mode='wt',encoding='utf-8') as f:    
    f.writelines(['111\n', '222\n', '333\n', '444\n'])  # 其中每个元素都就加上了换行符,111等与其他元素值,都将独立一行

# 2、f.writelines与for循环的关系:它底层相当于调用了for循环,把列表中的每个元素for循环写入文件。)    
with open('k.txt',mode='wt',encoding='utf-8') as f:   
    # f.writelines相当于调用了for循环,把列表中的每个元素for循环写入文件.
    li = ['111\n', '222\n', '333\n', '444\n']
    for item in li:
        f.write(item)
        
# 3、b模式下的写操作
with open('l.txt', mode='wb') as f:
    li = [
        '111aaa\n'.encode('utf-8'),
        '222bbb\n'.encode('utf-8'),
        '333ccc\n'.encode('utf-8'),
    ]
    # 3.1 b模式下使用writelines,列表中的每个字符串类型的元素,都需要进行编码成bytes类型
    f.writelines(li)

with open('m.txt', mode='wb') as f:
    li = [
        b'aaaaaa\n',
        b'bbbbbb\n',
        '333ccc\n'.encode('utf-8'),
    ]
    # 3.2 b模式下使用writelines,当列表中的字符串元素中的值全为字母时,可以不用特地解码。可以直接在要写入的字符串内容前加前缀小b,得到bytes类型
    f.writelines(li)

with open('n.txt', mode='wb') as f:
    li = [
        bytes('111aaa\n', encoding='utf-8'),
        bytes('222bbb\n', encoding='utf-8'),
        bytes('333ccc\n', encoding='utf-8'),
    ]
    # 3.3 '上'.encode('utf-8')等同于bytes('上', encoding='utf-8')
    f.writelines(li)
    
'''
以上三步查看文件中的结果都是:
111aaa
222bbb
333ccc
''' 
flush
# flush使用场景:主要争对写操作,告诉操作系统,把数据直接从内存写入硬盘中。通常flush主要用于测试行为。
with open('o.txt', mode='wt', encoding='utf-8') as f:
    f.write('哈哈哈')
    f.flush()  # 立刻将文件内容从内存刷到硬盘

了解

其他了解操作:f.readable()f.writeable()f.encodingf.namef.colsed

f.readable()  # 文件是否可读
f.writable()  # 文件是否可读
f.closed  # 文件是否关闭
f.encoding  # 如果文件打开模式为b,则没有该属性
f.flush()  # 立刻将文件内容从内存刷到硬盘
f.name
with open('p.txt', mode='wt', encoding='utf-8') as f:
    print(f.readable())  # 判断f句柄是否可读,False
    print(f.writable())  # 判断f句柄是否可写,True
    
    print(f.encoding)    # 查看f句柄采用的字符编码,utf-8(如果文件打开模式为b,则没有该属性)
    # print(f.encoding)  # 如果没有指定encoding,默认使用操作系统的编码,在windsow中显示:CP936(CP936其实就是GBK)
    
    print(f.name)   # 查看f句柄的文件名,p.txt
print(f.closed)     # 判断文件是否关闭

五、控制文件指针操作

指针移动的单位:

  • 指针移动的单位都是以字节为单位,每次都是移动移动几个字节。(只有一种情况特殊,t模式下的read(n), 这个n代表的是字符个数,而b模式下read(n),这个n代表的是字节数)

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

f.seek的应用

"""
模仿:linux中的tail -f命令
    动态查看程序的变化,动态检测access.log的末尾,有没有追加新的内容,有显示到屏幕上。
    
分析:
    1、你需要有追加写日志数据的程序
    2、你需要有检测当日志数据新增了以后,打印到屏幕上的程序
    
补充:seek的三种模式,在没有移动文件指针的情况下,都可以使用
"""
# 创建一个文件用来存放追加写日志数据的代码
with open('access.log', mode='at', encoding='utf-8') as f:
    import datetime

    f.write(f'日志:{datetime.datetime.now()}'.center(50, '-') + '\n')

# 创建另一个文件用来存放当日志数据新增了以后,打印到屏幕上的代码
import time

with open('access.log', 'rb') as f:  # 注意:只要涉及到文件指针的操作,最好使用b模式操作。
    f.seek(0, 2)  # 2模式下,让光标停留在末尾。
    while True:
        res = f.readline()
        if len(res):
            print(res.decode('utf-8'))  # b模式下读文件,需要decode
        else:
            time.sleep(2)  # 定时2秒检测一次。

f.seek(n,模式):n指的是移动的字节数。模式有三种:

  • 0:默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
    • 举例:f.seek(9,0),文件从开头位置移动8个bytes
  • 1:该模式代表指针移动的字节数是以当前所在的位置为参照的
    • 举例:f.seek(9,1),如果文件是从第2个字节位置开始,执行该操作以后当前文件中指针所在位置在11字节的位置。
  • 2:该模式代表指针移动的字节数是以文件末尾的位置为参照的
    • 举例:f.seek(-3,2),如果文件中总字节数为9,那么当前所在的字节数是6.
  • 强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用

补充:

1、seek默认就是0模式
    2、指针使用默认不移动:012三种模式只要没有进行移动操作t、b模式中都可以使用。
    3、指针移动时:0模式只能用在t模式中使用。012模式都可以用来b模式中
    4、seek推荐用的模式:seek推荐使用在b模式中,一般基于seek的操作,都是用于b模式下的操作。
    5、注意:a模式下,无论你的文件指针在哪个位置。一旦你使用write操作,写入的内容必然要追加到文件的末尾。
    6、f.seek三种模式的正反向移动:正向移动文件指针不受内容的限制。反向移动文件指针不能超出内容部分,超出报错。

f.tell():获取文件指针当前位置。

''''''
创建aaa.txt文件,写入以下内容作为测试需要:本次使用utf-8作为存储单位。一共9个字节。
abc你好
''''''
# 验证:t模式下的read(n), 这个n代表的是字符个数
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.read(4)
    print(res)  # abc你

    
# 验证:b模式下read(n),这个n代表的是字节数
with open('aaa.txt', 'rb') as f:
    print(f.read(3))  # b'abc'
    print(f.read(3).decode('utf-8'))  # 你
    print(f.read(3).decode('utf-8'))  # 好


# 验证:只有0模式可以在t模式下使用
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    f.seek(3, 0)
    # f.seek(3, 1)  # 报错(不支持运算):io.UnsupportedOperation: can't do nonzero cur-relative seeks
    # f.seek(3, 2)    # 报错(不支持运算):io.UnsupportedOperation: can't do nonzero end-relative seeks
    
# 示范一:0模式下指针的移动以文件开头为参照
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    f.seek(3, 0)  # 1、以文件开头为参照:移动3个字节,文件指针在'c'后面'你'前面.
    # print(f.read())  # 你好
    print(f.tell())  # 3

    f.seek(1, 0)  # 2、以文件开头为参照:移动0个字节,文件指针在'a'后面'b'前面.
    print(f.tell())  # 1
    print(f.read())   # bc你好
    print(f.tell())   # 9(这里因为上面的f.read()操作,导致文件指针在末尾)

    f.seek(0, 0)
    f.seek(4, 0)
    # print(f.read().decode('utf-8'))  # 3、注意:如果文件指针移动到的位置不符合解码条件,报错(Unicode解码错误):UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbd in position 0: invalid start byte

# 示范二:1模式下指针的移动以当前位置为参照物(文件指针并不会因为文件内容做停留)
with open('aaa.txt', mode='rb') as f:
    f.seek(9, 1)  # 1、以当前位置为参考:r模式,前面没有任何操作,此时默认在起始位置。执行f.seek操作后,此时文件指针在末尾。
    print(f.tell())  # 9
    print(f.read())  # b''(文件指针在末尾,f.read读不出任何内容)

    f.seek(7, 1)  # 以当前位置为参考:之前文件指针在9字节位置,现在继续向后移动3字节.
    print(f.tell())  # 16(文件已经到末尾了,文件指针并不会因为文件内容做停留)
    print(f.read())  # b''

# 示范三:2模式下指针的移动以末尾为参照物,文件指针移动数值指定负数。(注意: 如果指针往前移动的指定范围超出,报错)
with open('aaa.txt',mode='rb') as f:
    f.seek(-9, 2)    # 以末尾为参照物:文件指针往前移动9个字节。目前在‘a’前面,也就是开头起始位置。
    print(f.tell())  # 0
    print(f.read())  # b'abc\xe4\xbd\xa0\xe5\xa5\xbd'(文件指针在开头,读出了所有数据)

    f.seek(-3, 2)    # 以末尾为参照物:文件指针往前移动3个字节。目前在'你'后面,'好'前面。
    print(f.tell())  # 6
    print(f.read())  # 好
    
    # f.seek(-10, 2)  # 注意: 如果指针往前移动的指定范围超出,报错(操作系统错误):OSError: [Errno 22] Invalid argument

案例: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='')

什么是往文件末尾追加一行内容?是用编辑器打开文件,还是a操作?

"""
注意:我们双击打开文件,往文件的末尾写一行内容,其实这并不叫往文件末尾追加一行内容。我们双击打开文件,看到文件中的内容,这个时候这个文件是在内存中,我们在内存中的这样的修改,只是把文件内容覆盖回硬盘了,你看起来好像是修改了,其实是把硬盘原来的内容,完整的给覆盖了一遍。也就是说你如果自己用编辑器打开文件,在原有的基础之上,往后面写内容,那其实不叫追加操作。
    
模拟追加操作:
    以a模式打开文件,运行一次,那就是往文件末尾追加内容。
    
linux命令拓展:
    tail -f access.log:捕捉access.log这个文件末尾的内容。
"""

相关文章:

  • 函数的基本使用
  • python之迭代器
  • python之三元表达式、生成式、生成器表达式
  • 面向过程编程思想与匿名函数及其应用
  • python之函数递归与调用
  • python之二分法
  • python之json模块
  • python之pickle模块
  • python之time与datetime模块
  • python之random模块
  • python之os模块
  • shutil模块
  • shelve 模块
  • typing模块
  • 压缩zipfile与解压缩tarfile模块
  • 《Java编程思想》读书笔记-对象导论
  • EventListener原理
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • Mysql5.6主从复制
  • SpiderData 2019年2月23日 DApp数据排行榜
  • vue脚手架vue-cli
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 给新手的新浪微博 SDK 集成教程【一】
  • 开源SQL-on-Hadoop系统一览
  • 如何胜任知名企业的商业数据分析师?
  • 使用 @font-face
  • 我的zsh配置, 2019最新方案
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • "无招胜有招"nbsp;史上最全的互…
  • #考研#计算机文化知识1(局域网及网络互联)
  • (done) 两个矩阵 “相似” 是什么意思?
  • (vue)页面文件上传获取:action地址
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (南京观海微电子)——I3C协议介绍
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .Net FrameWork总结
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .NET 回调、接口回调、 委托
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .netcore 获取appsettings
  • .net下的富文本编辑器FCKeditor的配置方法
  • [BJDCTF2020]The mystery of ip1
  • [C++]:for循环for(int num : nums)
  • [C++][基础]1_变量、常量和基本类型
  • [Contiki系列论文之2]WSN的自适应通信架构
  • [C和指针].(美)Kenneth.A.Reek(ED2000.COM)pdf
  • [ESP32] 编码旋钮驱动
  • [GN] Vue3快速上手1
  • [JavaWeb学习] tomcat简介、安装及项目部署
  • [LeetCode]—Simplify Path 简化路径表达式