8、Python之文件操作基础

Python入门 / 2020-10-26

一、文件介绍

1.1、什么是文件?

文件是存储于存储设备上的一段数据流,也叫信息的集合。

1.2、为什么要用文件?

比如Linux系统上,一切皆文件!软件也好硬件也罢,都被虚拟化成一个又一个的文件,操作这些文件即是操作系统即控制计算机硬件来为我们达到某种目的!

其实我们对文件的所有操作,都是在向操作系统发起系统调用(也叫调用API),然后再由操作系统转换为对具体硬件的操控!

为什么不能直接控制计算机硬件?因为计算机底层硬件只识别二进制数字即0和1,而我们人类习惯了用自己的语言来交流沟通,普通人很难用01来跟计算机打交道,所以才有了字符编码来充当二进制数字和我们的语言之间的翻译官;才有了程序员使用各种编程语言来将晦涩难懂的代码写成一个又一个的简单易用的程序来帮我们达到控制计算机的目的!因此程序员帮我们写好了API,我们只需要触发某种条件就可以调用别人写好的API来控制计算机完成某项操作,比如Windows系统上点击删除,就会向系统发起系统调用控制磁盘来删除你想删除的那个文件!

1.3、如何使用文件?

Python3为我们提供了操作文件的方法:open(),但是使用open()后一定要记得关闭文件,即调用close()!

控制文件读写内容的模式分两种:t和b;t和b不能单独使用,必须与r/w/a连用

1.3.1、t、b概述:

此章我们讨论文件的控制,涉及到文件也必然会谈到I/O,因为控制文件必定涉及到读写文件,只要有读写操作就一定会产生I/O!

1.3.1.1、I/O简述:

I:input,即输入;在键盘上敲按钮,向计算机输入数据,即是输入!

O:output,即输出;通过终端等设备打印/显示数据出来即是输出,比如显示器上的图文!

Python区分二进制和文本I/O。以二进制模式打开的文件(包括 mode 参数中的 'b' )返回的内容为 bytes对象,不进行任何解码。在文本模式下(默认情况下,或者在 mode 参数中包含 ``'t')时,文件内容返回为 [str`](https://docs.python.org/zh-cn/3/library/stdtypes.html#str) ,首先使用指定的 encoding (如果给定)或者使用平台默认的的字节编码解码。

以上内容来自Python官方文档:https://docs.python.org/zh-cn/3/library/functions.html#open

简单提一下即可,现在还是原始学习阶段,先不去追求原理,学会使用更为紧迫,原理待基础扎实后再去深究!

1.3.2、t模式:

t即文本:也是默认模式,特点如下

  • 读写都以str为单位

  • 必须得是文本文件

  • 必须指定字符集,如:encoding='utf-8',如不选择,默认使用当前操作系统的字符集编码。

1.3.3、b模式

b即二进制,bytes

  • 读写都是以bytes,即字节为单位
  • 可以是任意类型文件,因为计算机任何类型数据存在都是以bytes模式存在,因此适用范围广泛!
  • 不能指定字符集

二、文件基本操作流程

2.1、打开文件

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

上面是一个完整的示例,但是平时我们可能用不到那么多参数,下面来练习一下简单的:

我这边操作系统是国人基于Linux发行版Debian10研发的Deepin V20,先写点文字进去

sanxi@sanxi-PC:~$ echo "sanxi666" >> sanxi.txt

然后用Python3打开它

Python 3.7.3 (default, Aug 26 2020, 21:26:28) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> open('sanxi.txt',mode='rt')
<_io.TextIOWrapper name='sanxi.txt' mode='rt' encoding='UTF-8'>

上面的演示中,最好是指定文件的绝对路径,我上面不指定是因为和改文件处于同一目录中,来个规范示例:

>>> open('/home/sanxi/sanxi.txt',mode='rt')
<_io.TextIOWrapper name='/home/sanxi/sanxi.txt' mode='rt' encoding='UTF-8'>
2.1.1、文件路径简述:
2.1.1.1、绝对路径

文件存储于文件系统之上,有其唯一对应位置。它们的关系可以这样简单理解,我是三溪,我是一个文件,而文件系统就是我的祖国:中国;文件路径就是我的真实地址,通过这个地址可以找到我这个人,比如中国广东省广州市天河区建工路某处下水道,而在文件系统中文件路径则对应的是如上/home/sanxi/sanxi.txt,这就是绝对路径了!

2.1.1.2、相对路径

上面的绝对路径就保证了依着这个地址绝对可以找到这个人(信不信老子顺着网线来揍你.JPG),但是不发现有点长吗?所以就有了相对路径,相对路径是说二者处于同一级目录,就好比我们在同一家公司同一个办公室上班,你吼一声:三溪,肯定找得到我!如果不同级目录,不用说啦,你去北京喊能找得到我就有鬼了!

但是呢,Windows这家伙装X!明明当初抄的unix,偏偏就要反其道而行之!你们unix和Linux文件路径用左斜杠/是吧!好!我偏偏要用右斜杠\,我就是要告诉所有人我们不一样!所以呢,在Windows系统下,文件路径需要注意这点!

好了,关于绝对路径和相对路径就说这些了!有兴趣自己去查下文献资料!

2.2、读/写文件

程序对文件的读写请求均为向操作系统发起系统调用,由操作系统来控制存储设备将数据读入内存或写入硬盘等!

2.2.1、读取:read()

read是调用操作系统将存储设备(一般是指磁盘)数据读入内存

>>> sanxi=open('/home/xuxin/sanxi.txt')
>>> sanxi.read()
'sanxi666\n'
#当再次读时,会发现内容为空!原因稍后再进行解释
>>> sanxi.read()
''

上述演示中,其实是开辟了两个内存空间,一个是变量赋值;一个是open打开文件所需要维持的内存空间!

2.2.2、关闭:close()

关闭文件,回收操作系统维持打开文件的内存空间,关闭后就不能再读了,需要再打开

>>> sanxi.close()
>>> sanxi.read()
#关闭后再读直接报错了
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
2.2.2.1、为何要关闭文件?

因为操作系统能够打开的文件数量是有限的,维持一个文件的打开需要耗费一定的系统资源!所以不使用时,关闭文件是很重要的!各系统默认文件打开上限都是不一样的,比如Linux执行ulimit -n可以看到

xuxin@seafile:~$ ulimit -n
1024
2.2.3、with open()

上面的open,每次打开文件后得手动调用close()来关闭文件,说实话有点烦!所以就有了with open(),它们区别在于执行完所有子代码后,with open会自动调用close()来关闭文件!使用基础语法如下所示:

with open ('SOME_FILE') as VARIABLE:
    VAR1=VARIABLE.read()
    ...

​ 来演示一下

with open('haha') as sanxi:
    hihi=sanxi.read()
    print(hihi)

# 打印得出结果是
/home/sanxi/PycharmProjects/untitled/venv/bin/python /home/sanxi/PycharmProjects/untitled/test
sanxi zhen de 6

当with的子代码执行完后,我们再来read一下,可以看到直接报错说文件已经被关闭!因为自动调用close了!

with open('haha') as sanxi:
    hihi=sanxi.read()
    print(hihi)
sanxi.read()

# pycharm执行得到结果,还有内容是因为在read前有个print!
sanxi zhen de 6
Traceback (most recent call last):
  File "/home/sanxi/PycharmProjects/untitled/test", line 4, in <module>
    sanxi.read()
ValueError: I/O operation on closed file.
#  可以看到,with open自动调用了close关闭了文件!with可以跟多个子语句,语句过长时跟Linux一样用\续行,意思是下面一行和\是同一行!
2.2.4、案例

写一个最简单的登录小程序,实现输入账号密码是否匹配;我提前在auth文件里写入了:sanxi:gaga777

with open('/home/sanxi/PycharmProjects/untitled/auth') as sanxi:
    for login in sanxi:
        user,passwd=login.strip().split(':')
        print(user,passwd)
        username=input('请输入账号:').strip()
        password=input('请输入密码:').strip()
        if username==user and password==passwd:
            print('登录成功')
            break
        else:
            print('账号或密码错误')

三、r、w、a、x模式

3.1、以t模式为基础操作

3.1.1、r:只读模式
r特性

r是默认的操作模式,当文件不存在时会报错,存在则文件指针从文件首部读到末尾,结束时指针停留在尾部!

with open('haha') as sanxi:
    hihi=sanxi.read()
    print(hihi)
    gaga=sanxi.read()
    print(gaga)
    
# 运行得到结果
sanxi zhen de 6

可以看到,第二次read是没有文件内容的,只有空行!因为r特性就是这样,每一次都是从指针所在处往文件末尾处跳转;因此第一次读完haha文件后,指针跳到了6后面,也就是文件末尾,再往后已经没有内容了!此时再进行第二次r操作的话因为指针所在处本来就是文件末尾,什么也没有,因此为空!

r+:读写模式

基于r的基础上可读写,即主要继承r特性,依旧是不存在就报错,写的话是开头覆盖写!

with open('haha', 'r+t', encoding='utf-8') as go:
    go.write('77')
    
# 此时再去看haha文件,就会发现,前两个字符被77覆盖掉!
77er:passwd

有趣的是,因为r特性是从头读到尾,因此在写r+的write之前加上一条read(),那么后面的write()就变成了追加了,因为read后文件指针位于文件末尾,末尾为空,直接写入内容!

3.1.2、w只写模式:

当文件不存在时,创建空文件;存在时会先清空文件内容,再写入新内容!

with open('/home/sanxi/PycharmProjects/untitled1/haha', 'wt') as sanxi:
    andy = sanxi.write('sanxi666')
# 执行完后去看看haha文件,可以看到内容已经被重写

这个时候再来读是会报错的,因为用的是wt仅写入模式

with open('/home/sanxi/PycharmProjects/untitled1/haha', 'wt') as sanxi:
    andy = sanxi.write('sanxi666')

Traceback (most recent call last):
  File "/home/sanxi/PycharmProjects/untitled1/test.py", line 3, in <module>
    andy6 = sanxi.read()
io.UnsupportedOperation: not readable

在以w模式打开文件但没有关闭情况下,连续写入,新写入的内容总是跟在旧内容之后;如果重新以w模式打开文件,会清空文件内容!

with open('/home/sanxi/PycharmProjects/untitled1/haha', 'wt') as sanxi:
    andy = sanxi.write('sanxi666')
    haha = sanxi.write('dawoa')
    hihi = sanxi.write('gagaya')
# 执行后看下文件内容
sanxi666dawoagagaya
w+:读写模式

打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。

3.1.3、a只追加写模式:

文件不存在时,创建空文件并写入内容;存在则指针跳到末尾,并追加新内容

a+模式

打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写!

with open('/home/sanxi/PycharmProjects/untitled1/haha', 'at') as sanxi:
    andy = sanxi.write('gogo')
# 执行后查看haha文件,发现已被追加到文件尾部,这跟w模式下不关闭文件连续写入一样效果!
sanxi666dawoagagayagogo
3.1.4、a和w的异同
相同点

不关闭文件情况下,是一样的!

不同点

a重新打开不会清空,永远是将文件指针直接跳到最后面然后写入新内容,而w是全新写,跟原来没有关系!

3.1.5、x模式

x,只写模式,不可读,不存在则创建并写入;存在则报错

with open(r'haha', mode='xt', encoding='utf-8') as go:
    rw = go.write('yes')
    print(rw)
    
# 执行pycharm得到报错结果

Traceback (most recent call last):
  File "/home/sanxi/PycharmProjects/untitled1/test.py", line 1, in <module>
    with open(r'haha', 'xt', encoding='utf-8') as go:
FileExistsError: [Errno 17] File exists: 'haha'

四、应用小案例

4.1、拷贝

我们可以使用w模式用来创建全新的文件,比如拷贝文件!

搞起来!!!

搞事搞事! - 蘑菇头搞事表情包_蘑菇头_装逼_表情头牌表情- 发表情- fabiaoqing.com

src = input('请输入需要复制的文件路径:')
dest = input('请输入目标路径:')

with open(src, 'rt', encoding='utf-8') as src_path:
    src_read = src_path.read()

with open(dest, 'wt', encoding='utf-8') as dest_path:
    dest_path.write(src_read)
    
# 执行pycharm得到结果
请输入需要复制的文件路径:/home/sanxi/PycharmProjects/untitled1/haha
请输入目标路径:/home/sanxi/PycharmProjects/untitled1/hihi

这时候可以看到,hihi文件已被创建并将haha里面的user:passwd写入!

世间微尘里 独爱茶酒中