文件读写处理——测试工程师Python开发实战(15)

发表于:2023-8-29 09:38

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:胡通    来源:51Testing软件测试网原创

#
Python
  4.11  文件读写处理
  Python内置了open()方法,该方法可以对文件进行读写操作,使用open()方法操作文件分3步:打开文件、操作文件和关闭文件。
  4.11.1  基本的语法
  在读写文件之前,我们首先使用open()方法打开文件,open()方法的返回值是一个file对象,可以将它赋值给一个变量。open()方法的格式为:
<变量名>=open(<文件名>,<打开模式>)?
  open()有两个参数:文件名和打开模式。其中,文件名可以是单独的文件名称,也可以是包含完整路径的名称,在写文件名时需包含文件扩展名。open()方法提供的打开模式如表4-28所示。
表4-28  open()方法提供的打开模式
  提示
  如果要读取非UTF-8编码的文件,需要给open()方法传入encoding参数。例如,读取GBK编码的文件:
f = open('gbk.txt', 'r', encoding='gbk')
  如果遇到编码不规范的文件,程序可能会抛出UnicodeDecodeError异常,这表示文件中可能夹杂了一些非法编码的字符。遇到这种情况,我们可以使用errors参数,表示遇到编码错误后的处理方式:
f = open('gbk.txt', 'r', encoding='gbk', errors='ignore')。?
  4.11.2  文件的读写
  在使用open()方法打开文件后,我们可以根据打开方式的不同对文件进行相应的操作。如果文件以文本方式打开,文件的读写将按照字符串方式,采用当前计算机使用的编码或指定编码;如果文件以二进制方式打开,文件的读写将按照字节流方式。
  Python中常用的读取和写入文件内容的方法,如表4-29所示。
表4-29  读取和写入文件内容的方法
  打开文件后,Python提供了多种方法读取文件:
  read()方法用于读取整个文件,通常将文件内容放入一个字符串变量;
  readline()方法用于每次读取一行内容;
  readlines()方法用于一次性读取所有内容并按行返回列表;
  我们应根据需要决定如何调用读取文件的方法。调用read()方法会一次性读取文件的全部内容,如果文件有10GB,内存就承受不住了,所以保险起见,我们可以反复调用read(size)方法,每次最多读取size个字节的内容。如果文件很小,调用read()方法一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()方法读取最方便。
  文件读写相对比较简单,接下来,我们重点讲解一下seek()方法。
  如果我们希望指定读取的起始位置,就需要移动文件指针的位置。seek()方法用于将文件指针移动至指定位置,其语法格式如下:
file.seek(offset[,whence])
  其中,各个参数的含义如下。
  file表示文件对象。
  whence作为可选参数,用于指定文件指针要放置的位置,该参数的值有3种:0表示文件头(默认值);1表示当前位置;2表示文件尾。
  offset表示文件指针相对whence位置的偏移量,值为正数表示向后偏移,值为负数表示向前偏移。例如,当whence==0 && offset==3(即seek(3,0)),表示文件指针移动至距离文件开头3个字符处;当whence==1 && offset==5(即seek(5,1)),表示文件指针向后移动至距离当前位置5个字符处。
  注意,当offset值非0时,Python要求文件必须要以二进制格式打开,否则将抛出io.Unsupported Operation错误。
  提示
  文件指针用于标明文件读写的起始位置,如果以b模式打开,每个数据就是1字节;如果以普通模式打开,每个数据就是一个字符。通过移动文件指针的位置,再借助read()方法和write()方法,我们可以轻松实现读取文件中指定位置的数据(或者向文件中的指定位置写入数据)。
  注意,当向文件中写入数据时,如果不是文件的尾部,写入位置的原有数据不会自行向后移动,新写入的数据会直接覆盖文件中处于该位置的数据。
  4.11.3  文件的关闭
  当处理完一个文件后,调用f.close()方法来关闭文件并释放系统的资源。文件关闭后,如果尝试再次调用该文件对象,则会抛出异常。在4.10节我们提到,可以采用with as语句灵活地管理文件的关闭操作,我们可以不需要再写close语句,但要注意代码缩进。
  4.11.4  大文件处理
  小文件的处理通常不会出现问题,而大文件的处理,稍有不慎或者处理不好将会报错或者使得性能变差。例如,文件约4GB,在处理文本文档时,经常会出现memoryError错误和文件读取太慢的问题。
  这里推荐大家用with open(),它可以让系统自动进行IO缓存和内存管理,不需要管系统如何分配这些内存,并且在读取完成之后,我们不需要使用close()关闭文件句柄。
  另外,结合for line in f,文件对象f将被视为一个迭代器,自动地采用缓冲IO和内存管理,所以我们可以不用担心大文件处理产生异常。
with open(...) as f:
    for line in f:
       process(line) # <do something with line>
  面对百万行的大型数据使用with open()是没有问题的,但是这里面参数的不同会导致效率的不同。这里建议大家使用参数"rb",二进制读取依然是最快的模式,即with open(filename,"rb")as f。
  我们针对rb方式进行简单测试,遍历100万行内容基本在3秒内完成,能满足处理中大型文件的效率需求。
  分块下载大文件
  Python下载文件的方式有很多,其中requests库非常简洁,从下载简单的小文件到用断点续传的方式下载大文件都支持。
  当下载的文件非常大,计算机的内存空间完全不够用的情况下,我们可以使用requests库的流模式。在默认情况下,stream参数值为False,会因文件过大导致内存不足。当stream参数值为True时,requests库并不会立刻开始下载,只有在调用iter_content()方法或者iter_lines()方法遍历内容时下载。其中,iter_content()方法逐块遍历要下载的内容,iter_lines()方法逐行遍历要下载的内容,使用这两个方法下载大文件可以防止占用过多的内存,因为这两个方法每次循环只下载小部分数据。示例如代码清单4-11所示。
# -*- coding: utf-8 -*-
# @Time : 2022/3/2 11:29 上午
# @Project : fileDemo
# @File : downloadFile1.py
# @Author : hutong
# @Describe: 微信公众号:大话性能
# @Version: Python3.9.8
?
import requests
def steam_download(url):
    with requests.get(url, stream=True) as r:
        with open('vscode.exe', 'wb') as flie:
            # chunk_size指定写入大小,每次写入1024*1024字节
            for chunk in r.iter_content(chunk_size=1024*1024):
                if chunk:
                    flie.write(chunk)
代码清单4-11  downloadFile1
  在下载大文件的时候,我们可以加上进度条美化下载界面,实时获取下载的网络速度和已经下载的文件大小。这里使用tqdm库作为进度条显示,具体的tqdm库参数的含义,大家可自行查找。代码优化后如代码清单4-12所示。
# -*- coding: utf-8 -*-
# @Time : 2022/3/2 11:30 上午
# @Project : fileDemo
# @File : downloadFile2.py
# @Author : hutong
# @Describe: 微信公众号:大话性能
# @Version: Python3.9.8
?
from tqdm import tqdm
def tqdm_download(url):
    resp = requests.get(url, stream=True)
    # 获取文件大小
    file_size = int(resp.headers['content-length'])
    with tqdm(total=file_size, unit='B', unit_scale=True, unit_divisor=1024, ascii=
True, desc='vscode.exe') as bar:
        with requests.get(url, stream=True) as r:
            with open('vscode.exe', 'wb') as fp:
                for chunk in r.iter_content(chunk_size=512):
                    if chunk:
                        fp.write(chunk)
                        bar.update(len(chunk))
代码清单4-12  downloadFile2
版权声明:51Testing软件测试网获得作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号