Python学习_基础_09_文件处理

Python-文件处理

零、基本概念

  1. 什么是文件

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

  2. 为什么要用文件

    用户/应用程序直接操作的是文件,对文件进行的所有操作,都是在向操作系统发送系统调用,然后再由操作系统将其转换成具体的硬盘操作。用户/应用程序可以通过文件将数据永久保存到硬盘中,或者从硬盘中读取数据,即操作文件就是操作硬盘。

  3. 如何使用文件:open()

    f = open(),获得文件对象(文件句柄)

    open()的参数最基本要有文件路径和操作模式。

一、模式介绍

  1. 控制文件读写内容的模式

    控制文件读写内容的模式:t和b,且t和b不能单独使用,必须跟r/w/a连用。

    • t-文本模式(默认的模式)
      1. 读写都是以str(实质在内存中是unicode)为单位的
      2. 操作只能是针对文本文件
      3. 必须指定字符编码,即必须指定encoding 参数
    • b-binary模式(二进制/bytes)
      1. 读写都是以bytes为单位
      2. 可以针对所有文件
      3. 一定不能指定字符编码,即一定不能指定encoding 参数
  2. 控制文件读写操作的模式

    • r 只读模式
    • w 只写模式
    • a 只追加写模式
    • +:r+、w+、a+

二、基本操作流程

  1. 打开文件(绝对路径/相对路径)

    # windows路径分隔符问题
    
    open('C:\a.txt\nb\c\d.txt')
    # 但因为有转义字符的存在,这么写有时候会给我们造成一些困扰。
    
    # 解决方案一:前面加'r'(推荐)
    open(r'C:\a.txt\nb\c\d.txt')
    # 解决方案二:使用'/'
    open('C:/a.txt/nb/c/d.txt')
    
    f = open(r'aaa/a.txt',mode='rt') # f的值是一种变量,也被称为“文件对象”。
    # 变量占用的都是应用程序的内存空间。
    
  2. 读/写文件:应用程序对文件的读写请求都是在向操作系统发送系统调用,然后由操作系统控制硬盘把数据读入内存、或者写入硬盘

    res = f.read()
    print(type(res))  # <class 'str'>
    
  3. 关闭文件

    f.close()  # 回收操作系统资源,因为python不能自动回收操作系统资源
    # f.read()  # 变量f存在,但是不能再读了(报错),后续f被垃圾回收机制回收
    

三、资源回收与with语法

使用with语法,在执行完子代码块后,相当于自动在末尾增加执行close()程序,不用再手动进行回收。

with open('a.txt', mode='rt') as f1:  # f1=open('a.txt',mode='rt')
    res = f1.read()
    print(res)
# 可以用with同时打开多个文件,用逗号分隔开即可
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)

    # 自动增加执行f1.close()
    # 自动增加执行f2.close()
    
# '\ + 换行(回车)'可在代码过长时将代码分割开,在语法上还是在同一行。如果不加'\'进行转义就会有语法错误。

四、详细介绍文件模式

t文本(默认的文件模式)

  1. 读写都以str(unicode)为单位的
  2. 纯文本文件
  3. 必须指定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
    # 是一个解码过程,所以需要指定encoding
    print(res, type(res))
    
# 内存中:utf-8格式的二进制-----解码-----》unicode
# 硬盘中(c.txt内容:utf-8格式的二进制)

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

with open('c.txt',mode='rt',encoding='utf-8') as f:
    res = f.read()  # 把所有内容从硬盘读入内存,指针从头到尾
    print(res)
# == == == == == == == =案例 == == == == == == == == ==
# user.txt文件内容的数据格式为“用户名:密码”
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操作模式:只写模式,当文件不存在时会创建空文件,当文件存在会清空文件,指针位于开始位置.

with open('d.txt', mode='wt', encoding='utf-8') as f:
    # f.read()  # 报错,不可读
    f.write('我擦勒\n')

强调两个点:

  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')
    
  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')
    
# == == == == == == == =案例 == == == == == == == == ==
# 案例: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)
    for line in f1:
        f2.write(line)
    # 这么写并不会提升程序运行速度,但相对会大大减少内存占用。
    # res = f1.read()会在一瞬间加大内存占用量,特别是在文件数据量大的情况下。

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

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

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

  • 相同点:在打开的文件不关闭的情况下,连续的写入,新写的内容总会跟在前写的内容之后。
  • 不同点:以 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))

“+”操作模式(了解):"+"不能单独使用,必须配合r、w、a

r+ w+ a+ :可读可写

在平时工作中,我们只单纯使用r/w/a,要么只读,要么只写,一般不用可读可写的模式。

with open('g.txt', mode='rt+', encoding='utf-8') as f:
    f.write('中国')
    # print(f.read())  # 读不出来,因为写了之后光标在末尾。

with open('g.txt', mode='w+t', encoding='utf-8') as f:
    print(f.read())  # 读不出来,w模式已经把内容清空了
    f.write('111\n')
    f.write('222\n')
    f.write('333\n')
    print('====>', f.read())  # 读不出来,因为光标在末尾。

with open('g.txt', mode='a+t', encoding='utf-8') as f:
    print(f.read())  # 读不出来,因为a模式下光标在末尾,后面没有内容。

    f.write('444\n')
    f.write('5555\n')  # 写入后光标停在了文档末尾的地方
    print(f.read())  # 读不出来,因为光标指针后面没有内容。

x操作模式(了解):只写模式【不可读;不存在则创建,存在则报错】

with open('a.txt', mode='x', encoding='utf-8') as f:
    pass  # 默认t文件模式,所以实质是xt

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('哈哈哈\n')

b文件模式:binary模式

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

# d.txt内容为:哈哈哈a
with open(r'd.txt', mode='rb') as f:
    res = f.read()  # utf-8的二进制
    print(res, type(res))  # b'\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88a' <class 'bytes'>
    # 为什么末尾的a没有以二进制形式输出?因为解释器自动对英文的二进制输出做了转换。
    print(res.decode('utf-8'))  # 哈哈哈a  # 这里bytes解码后为str
with open(r'f.txt', mode='wb') as f:
    f.write('你好hello'.encode('utf-8'))
# 重写文件拷贝工具,可以拷贝任意文件
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:
    for line in f1:
        f2.write(line)

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

循环读取文件的两种方式
# 方式一:自己控制每次读取的数据的数据量
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)

总结:

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

五、文件操作的其他方法

读相关操作

  1. readline:一次读一行

    g.txt文件内容:

    111
    222
    333
    444
    5555
    
    with open(r'g.txt', mode='rt', encoding='utf-8') as f:
        res1 = f.readline()
        res2 = f.readline()
        print(res1)  # 111
        while True:
            line = f.readline()
            if len(line) == 0:
                break
            print(line.strip())
            # 333
            # 444
            # 5555
    
  2. 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']
    

    **强调:**f.read()与f.readlines()都是将内容一次性读入内存。

写相关操作

  • f.writelines():

    有一个列表l,将列表的内容写入h.txt中。

    l = ['11111\n', '2222', '3333']
    # l = ['11111\n', '2222', '3333', 4444]  若列表中有非字符串类型元素,f.write()就会报错。
    with open('h.txt', mode='wt', encoding='utf-8') as f:
        for line in l:
            f.write(line)
            
    # h.txt内容为:
    # 11111
    # 22223333
    

    f.writelines()的作用,就是上述代码块中for循环代码所做的事情。

    l = ['11111\n', '2222', '3333']
    with open('h.txt', mode='wt', encoding='utf-8') as f:
        f.writelines(l)
    

    b模式写入需要编码:

    with open('h.txt', mode='wb') as f:
        l = [
            '1111aaa1\n'.encode('utf-8'),
            '222bb2'.encode('utf-8'),
            '33eee33'.encode('utf-8')
        ]
        f.writelines(l)
        
    # h.txt内容为:
    # 1111aaa1
    # 222bb233eee33
    
    补充两点:
    1. 补充1:如果是纯英文字符,可以直接加前缀b得到bytes类型。
      with open('h.txt', mode='wb') as f:
          l = [
              b'1111aaa1\n',
              b'222bb2',
              b'33eee33'
          ]
          f.writelines(l)
      
      # 写法虽然不一样但结果和上一个一样
      
    2. 补充2:‘文字’.encode(‘utf-8’) 等同于bytes(‘文字’ , encoding=‘utf-8’)
      with open('h.txt', mode='wb') as f:
          l = [
              bytes('上啊', encoding='utf-8'),
              bytes('冲呀', encoding='utf-8'),
              bytes('小垃圾们', encoding='utf-8'),
          ]
          f.writelines(l)
          
      # h.txt内容为:
      # 上啊冲呀小垃圾们
      
      # 所以将文字转换为bytes类型就有了两种方法。
      # '文字'.encode('utf-8')
      # bytes('文字' , encoding='utf-8')
      

了解的几个:

  • f.flush():立刻将文件内容从内存刷到硬盘
  • f.readable():判断文件是否可读
  • f.writable():判断文件是否可写
  • f.closed():判断文件是否已经关闭
  • f.encoding():文件的字符编码是什么(如果文件打开模式为b,则没有该属性)
  • f.name():文件的名字是什么

六、文件的高级操作:控制文件指针的移动

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

# aaa.txt内容为:abc你好
# 共五个字符9个字节
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.read(4)
    print(res)  # abc你

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

f.seek(n , 模式):n指的是移动的字节个数

三种模式:
  • 0:参照物是文件开头位置
  • 1:参照物是指针当前所在位置
  • 2:参照物是文件末尾位置,应该倒着移动

强调:只有0模式可以在t模式下使用,1、2 必须在b模式下用,否则会报错。

with open('aaa.txt', mode='rb') as f:
    f.seek(9, 0)
    f.seek(3, 0)  # 3
    print(f.tell())  # 结果为3
# aaa.txt内容为:abc你好
# 9个字节(一个英文占一个字节,一个汉字占三个字节)
with open('aaa.txt', mode='rb') as f:
    f.seek(9, 0)
    f.seek(3, 0)  # 3
    res = f.read()
    print(res.decode('utf-8'))  # 你好
    # f.seek(4, 0)  # 移动到第四个字节末尾的位置
    # res2 = f.read()
    # print(res.decode('utf-8'))  # 报错,因为识别不完整。
with open('aaa.txt', mode='rb') as f:
    f.seek(9, 1)
    f.seek(3, 1)  # 12
    print(f.tell())  # 结果为12
# aaa.txt内容为:abc你好
with open('aaa.txt', mode='rb') as f:
    f.seek(-9, 2)
    # print(f.tell())  # 结果为:0
    f.seek(-3, 2)
    # print(f.tell())  # 结果为:6
    print(f.read().decode('utf-8'))  # 结果为:好

小案例:实现日志监控功能

两个程序,一个写日志,一个监控并输出。

写日志:

import time

n = 0
with open('access.log', mode='at', encoding='utf-8') as f:
    while True:
        n += 1
        f.write('第{}次日志\n'.format(n))
        time.sleep(2)  # 延时2秒
        if n == 10:
            break

监控输出:

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.3)
        else:
            print(line.decode('utf-8'), end='')

七、文件修改的两种方式

# 文件a.txt内容如下
张一蛋     山东    179    49    12344234523
李二蛋     河北    163    57    13913453521
王全蛋     山西    153    62    18651433422

# 执行操作
with open('a.txt',mode='r+t',encoding='utf-8') as f:
    f.seek(9)
    f.write('<妇女主任>')

# 文件修改后的内容如下
张一蛋<妇女主任> 179    49    12344234523  # 发生了覆盖,并不是在中间插入
李二蛋     河北    163    57    13913453521
王全蛋     山西    153    62    18651433422

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

文件对应的是硬盘空间,硬盘不能修改对应着文件本质也不能修改,那我们看到文件的内容可以修改,是如何实现的呢?

大致的思路是将硬盘中文件内容读入内存,然后在内存中修改完毕后再覆盖回硬盘,具体的实现方式分为两种:

c.txt内容:

张三 is sb
李四 is dsb
王五 is xsb

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

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

  • 优点: 在文件修改过程中同一份数据只有一份
  • 缺点: 会过多地占用内存
with open('c.txt', mode='rt', encoding='utf-8') as f:
    res = f.read()
    data = res.replace('sb', '**')
    print(data)

with open('c.txt', mode='wt', encoding='utf-8') as f1:
    f1.write(data)
    
# 运行后,c.txt内容如下
# 张三 is **
# 李四 is d**
# 王五 is x**

方式二

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

  • 优点: 不会占用过多的内存
  • 缺点: 在文件修改过程中同一份数据存了两份
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('sb', '***'))
        
os.remove('c.txt')  # 删除c.txt文件
os.rename('.c.txt.swap', 'c.txt')  # 将新文件文件名改成c.txt


# 注意:.c.txt.swap文件在硬盘里,不在内存里。

# 运行后,c.txt内容如下
# 张三 is ***
# 李四 is d***
# 王五 is x***
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值