Python(11)IO编程(上)

简介: Python(11)IO编程(上)

一、IO编程概述


  • 在计算机中,IO指的是input(输入)output(输出)
  • 计算机中程序和运行时产生的数据会在内存中驻留,然后由CPU来执行,其中涉及到数据交换的地方,例如磁盘、网络等,就需要IO接口,下面来看一些日常生活、工作中的案例,来帮助更好的理解IO:


在我们打开浏览器时,访问百度首页,这时浏览器这个程序就需要通过网络IO获取百度的首页,浏览器会先发送数据给百度服务器,告诉服务器想要的HTML页面,这个动作是往外发数据,叫做output,随后百度服务器把网页发过来,这个动作是从外面接收数据,叫做input。


通常来说,程序完成IO操作会有input和output两个数据流,但是也有使用一个的情况,例如:


从磁盘读取文件到内存,只有input操作,从内存把数据写到磁盘,同样只有output操作


在IO编程中,Stream(流)是一个很重要的概念,流就像是一个水管,而数据就是水管里的水,水管里的水只能单向流动,其中inputstream就像是水从B端流向A端,即数据从磁盘读进内存,反之,outputstream就像是水从A端流向B端,即内存把数据写到磁盘,对于浏览网页来说,浏览器和服务器之间就需要至少建立两条水管,来实现输入和输出的目的


由于CPU和内存的速度远远高于外设的速度,所以在IO编程中,就会出现速度严重不匹配的问题,例如:


当我们想把一个100MB的数据写入磁盘时,如果是CPU的话,可能只需要0.01秒,而磁盘接收这个数据,可能就要10秒,这样的问题有两种解决方法:


同步IO:在程序遇到需要把数据读入磁盘时,程序暂停执行后续代码,等待数据读入磁盘后,再执行剩余代码,在数据读入途中CPU进行等待

异步IO:在遇到上面的情况时,CPU不进行等待,后续代码继续执行

上面的两种模式的区别在于,CPU是否等待IO执行的结果,下面来看一下案例,帮助更好的理解两种模式:


背景:小明想去逛商场,但是逛商场之前想吃汉堡


同步IO:小明去点汉堡之后,服务员说,汉堡现做,需要等待5分钟,小明点餐之后,在收银台前等了5分钟,拿到汉堡之后,再去逛商场,这就是同步IO

异步IO:小明去点汉堡之后,服务员说,汉堡现做,需要5分钟,汉堡做好之后通知小明,在这期间,小明可以先去逛商场,这就是异步IO

看过上面的例子之后,很明显的看出,异步IO比同步IO速度快,使用异步IO来编写程序的性能远远高于同步IO,但是异步IO的缺点就是编程模型复杂,根据上面的案例中,我们需要定义在汉堡做好之后,服务员要通过什么方式通知小明,如果是服务员跑过来找到小明说的话,这是回调模式,如果服务员发短信通知小明,小明需要不定期的查看手机短信,这就是轮询模式


操作IO的能力都是由操作系统提供的,每一种编程语言都会把操作系统提供的低级C接口封装起来方便使用,Python也一样,下面只说同步IO模式


二、文件读写


  • 读写文件是最常见的IO操作,Python内置了读写文件的函数,用法和C语言是兼容的
  • 在读写文件之前,我们需要了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象,通常把这个文件对象叫做文件描述符,然后通过操作系统提供的接口从这个文件对象中读取数据,即读取文件,或者把数据写入这个文件对象,即写入文件


- 读取文件

要以读取文件的模式打开一个文件对象,可以使用Python内置的函数open(),例如:

#下面以centos系统为例
[root@centos-1 ~]# echo "aaaaa" > test.txt  #先创建一个文件
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open("/root/test.txt","r")     
>>> 
- open函数的第一个参数是文件路径,第二个参数为标识符,'r'为读
#如果打开的文件对象不存在,则会抛出一个IOError的错误,并且给出错误码和详细的信息,说明文件不存在
>>> f = open("/root/aaa.txt","r")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/root/aaa.txt'
- 文件成功打开后,调用'read()'方法可以一次性读取文件的全部内容,Python会把内容读取到内存,使用'str'字符串对象表示
>>> f = open("/root/test.txt","r")
>>> f.read()
'aaaaa\n'   #\n表示换行符
- 在文件使用完毕之后,我们必须使用'close()'方法来关闭文件,因为文件对象会占用操作系统资源,并且操作系统同一时间能打开的文件数量也是有限的
>>> f.close()
- 由于文件读写时可能会产生'IOError'错误,一旦出错后,后面的'f.close()'方法就不会调用,所以,为了保证无论是否出错都能正确的关闭文件,我们可以使用'try...finally'来达到目的
[root@centos-1 ~]# cat test.py 
#/usr/bin/enc python3
# -*- coding: utf-8 -*-
try:
    f = open('/root/test.txt','r')
    print(f.read())
finally:
    if f:
        f.close()
[root@centos-1 ~]# python3 test.py   #执行脚本
aaaaa
- 但是每次都像上面这么写的话,实在是太麻烦了,Python引入了'with'语句来自动帮我们调用'close()'方法
[root@centos-1 ~]# cat test.py 
#/usr/bin/enc python3
# -*- coding: utf-8 -*-
with open('/root/test.txt','r') as f:
    print(f.read())
[root@centos-1 ~]# python3 test.py 
aaaaa

调用read()方法会一次性读取文件的所有内容,如果文件太大的话,内存就爆了,所以,保险起见,我们可以反复调用read(size)方法,每次最多读取指定size大小的内容


除了read(size),调用readline()可以每次读取一行内容,调用readlines()一次性读取所有内容并按行返回list列表,根据需求决定如何调用


如果文件很小,直接使用read()一次性读取是最方便的,如果不能确定文件大小,我们可以反复调用read(size),如果是配置文件,调用readlines()最方便

[root@centos-1 ~]# cat test.py 
#/usr/bin/enc python3
# -*- coding: utf-8 -*-
f = open('test.txt','r')
n = 1
for line in f.readlines():
  print('第%s行为:' % n,line.strip())  #line.strip()会去掉每行的空格、换行符、制表符等
  n = n + 1
f.close()
[root@centos-1 ~]# python3 test.py 
第1行为: aaaaa
第2行为: bbbbb
第3行为: ccccc

- file-like Object


像open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object对象,除了file之外,还可以是内存的字节流、网络流、自定义流等

file-like Object不要求从特定类继承,只要写个read()方法就行

StringIO就是在内存中创建的file-like Object,常用作临时缓冲

如果要想在内存中对数据进行读写,可以使用StringIO和BytesIO,前者是对字符串数据的读写,后者是对二进制数据的读写


- 二进制文件


  • 如果读取的是二进制文件,我们可以使用rb模式打开文件,例如:


>>> f = open('/root/test.txt', 'rb')    #使用rb模式
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节

- 字符编码


  • 想要读取非utf-8编码的文本文件,需要给open()函数传入encoding参数,例如:


- 读取GBK编码的文件
>>> f = open('/root/test.txt', 'r', encoding='gbk')
>>> f.read()
'测试'


遇到有些编码不规范的文件,可能会遇到报错UnicodeDecodeError,这是因为在文本文件中可能夹杂了一些非法编码的字符,遇到这种情况,open()函数还可以接收一个errors参数,表示如果遇到编码错误后如何处理,最简单的方法就是直接忽略,例如:


>>> f = open('/root/test.txt', 'r', encoding='gbk', errors='ignore')

- 写入文件


  • 写文件和读文件是一样的,区别是在调用open()函数时,传入标识符wwb表示写入文本文件或者二进制文件
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open('/root/test.txt','w')
>>> f.write('aaaaaaaa')
8
>>> f.read()   #因为标识符是w,写入,所以无法读取文件
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
io.UnsupportedOperation: not readable
>>> f.close()
>>> f = open('/root/test.txt','r')
>>> f.read()
'aaaaaaaa'

可以反复调用write()方法来写入文件,但是和读取文件一样,在写入之后必须要使用close()方法来关闭文件,当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再去写入,只有调用close()方法时,操作系统才会保证把没有写入的数据全部写入磁盘


忘记调用close()方法的后果,就是写入的数据可能只有一点到了磁盘,其余数据全部丢失,所以,通常来说,为了防止忘记使用close(),我们可以直接使用with语句

[root@centos-1 ~]# cat test.txt 
aaaaaaaa
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('/root/test.txt','w') as f:
...     f.write('Hello World!!!')
... 
14
>>> with open('/root/test.txt','r') as f:
...     print(f.read())
... 
Hello World!!!

要写入特定编码的文本文件,需要给open()函数传入encoding参数,将字符串自动转换成指定编码


从上面的案例可以看到,test.txt文件原本的内容是aaaaaaaa,在以w模式写入文件时,新的Hello World!!!的内容覆盖了原本的内容,如果我们想要追加到文件末尾的话,可以使用a模式,以追加模式写入

[root@centos-1 ~]# cat test.txt 
Hello World!!![root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('/root/test.txt','a') as f:
...     f.write('aaaaaaa')
... 
7
>>> with open('/root/test.txt','r') as f:
...     print(f.read())
... 
Hello World!!!aaaaaaa

三、StringIO和BytesIO


- StringIO


很多时候,数据读写不一定是文件,也可以在内存中读写,当并不想把数据写到本地磁盘时,就可以使用StringIO


StringIO顾名思义就是在内存中读写str


想要把str写入StringIO,首先需要创建一个StringIO,然后像文件一样写入即可,例如:

[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('Hello')    #直接进行写入
5
>>> f.write(' World!!!')
9
>>> print(f.getvalue())  #使用getvalue()方法获取写入后的str
Hello World!!!

想要读取StringIO,可以使用一个str初始化StringIO,然后像文件一样读取,例如:

[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from io import StringIO
>>> f = StringIO('Hello! \n World!!!')
>>> while True:
...     s = f.readline()
...     if s == '':
...             break
...     print(s.strip())
... 
Hello!
World!!!

- BytesIO


  • 上面的StringIO操作的只能是Str数据,如果要操作二进制数据,就需要使用BytesIO
  • BytesIO实现了在内存中读写bytes,下面来创建一个BytesIO,然后写入一些bytes
[root@centos-1 ~]# python3
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
- 注意上面写入的是经过UTF-8编码的bytes

和StringIO相似,可以使用一个bytes初始化BytesIO,然后像文件一样读取

[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'
  • 小结:StringIO和BytesIO是在内存中操作str和bytes的方法,从内存中读写string或者bytes与直接读写文件类似,有相同的接口
目录
相关文章
|
17天前
|
人工智能 数据可视化 数据挖掘
探索Python编程:从基础到高级
在这篇文章中,我们将一起深入探索Python编程的世界。无论你是初学者还是有经验的程序员,都可以从中获得新的知识和技能。我们将从Python的基础语法开始,然后逐步过渡到更复杂的主题,如面向对象编程、异常处理和模块使用。最后,我们将通过一些实际的代码示例,来展示如何应用这些知识解决实际问题。让我们一起开启Python编程的旅程吧!
|
16天前
|
存储 数据采集 人工智能
Python编程入门:从零基础到实战应用
本文是一篇面向初学者的Python编程教程,旨在帮助读者从零开始学习Python编程语言。文章首先介绍了Python的基本概念和特点,然后通过一个简单的例子展示了如何编写Python代码。接下来,文章详细介绍了Python的数据类型、变量、运算符、控制结构、函数等基本语法知识。最后,文章通过一个实战项目——制作一个简单的计算器程序,帮助读者巩固所学知识并提高编程技能。
|
4天前
|
Unix Linux 程序员
[oeasy]python053_学编程为什么从hello_world_开始
视频介绍了“Hello World”程序的由来及其在编程中的重要性。从贝尔实验室诞生的Unix系统和C语言说起,讲述了“Hello World”作为经典示例的起源和流传过程。文章还探讨了C语言对其他编程语言的影响,以及它在系统编程中的地位。最后总结了“Hello World”、print、小括号和双引号等编程概念的来源。
98 80
|
3天前
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
17 2
|
16天前
|
小程序 开发者 Python
探索Python编程:从基础到实战
本文将引导你走进Python编程的世界,从基础语法开始,逐步深入到实战项目。我们将一起探讨如何在编程中发挥创意,解决问题,并分享一些实用的技巧和心得。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供有价值的参考。让我们一起开启Python编程的探索之旅吧!
41 10
|
18天前
|
机器学习/深度学习 人工智能 数据挖掘
探索Python编程的奥秘
在数字世界的海洋中,Python如同一艘灵活的帆船,引领着无数探险者穿梭于数据的波涛之中。本文将带你领略Python编程的魅力,从基础语法到实际应用,一步步揭开Python的神秘面纱。
37 12
|
17天前
|
IDE 程序员 开发工具
Python编程入门:打造你的第一个程序
迈出编程的第一步,就像在未知的海洋中航行。本文是你启航的指南针,带你了解Python这门语言的魅力所在,并手把手教你构建第一个属于自己的程序。从安装环境到编写代码,我们将一步步走过这段旅程。准备好了吗?让我们开始吧!
|
18天前
|
关系型数据库 开发者 Python
Python编程中的面向对象设计原则####
在本文中,我们将探讨Python编程中的面向对象设计原则。面向对象编程(OOP)是一种通过使用“对象”和“类”的概念来组织代码的方法。我们将介绍SOLID原则,包括单一职责原则、开放/封闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则有助于提高代码的可读性、可维护性和可扩展性。 ####
|
16天前
|
人工智能 数据挖掘 开发者
探索Python编程之美:从基础到进阶
本文是一篇深入浅出的Python编程指南,旨在帮助初学者理解Python编程的核心概念,并引导他们逐步掌握更高级的技术。文章不仅涵盖了Python的基础语法,还深入探讨了面向对象编程、函数式编程等高级主题。通过丰富的代码示例和实践项目,读者将能够巩固所学知识,提升编程技能。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供有价值的参考和启示。让我们一起踏上Python编程的美妙旅程吧!
|
19天前
|
机器学习/深度学习 人工智能 自然语言处理
探索未来编程:Python在人工智能领域的深度应用与前景###
本文将深入探讨Python语言在人工智能(AI)领域的广泛应用,从基础原理到前沿实践,揭示其如何成为推动AI技术创新的关键力量。通过分析Python的简洁性、灵活性以及丰富的库支持,展现其在机器学习、深度学习、自然语言处理等子领域的卓越贡献,并展望Python在未来AI发展中的核心地位与潜在变革。 ###