37号文件 (37号文件解读)

打开文件

Python 中,打开文件使用的是 open 函数。 open 函数的基本语法如下:

open(file_name [, access_mode][, buffering])

参数解析:

  • file_name 变量:是一个包含要访问的文件名称的字符串值。
  • access_mode 变量:指打开文件的模式,对应有只读、写入、追加等。 access_mode 变量值不是必需的(不带 access_mode 变量时,要求 file_name 存在,否则报异常),默认的文件访问模式为只读 (r)
  • buffering :如果 buffering 的值被设为0,就不会有寄存;如果 buffering 的值取1,访问文件时就会寄存行;如果将 buffering 的值设为大于1的整数,表示这就是寄存区的缓冲大小;如果取负值,寄存区的缓冲大小就是系统默认的值。

open 函数返回一个 File (文件)对象。 File 对象代表计算机中的一个文件,是 Python 中另一种类型的值,就像我们熟悉的列表和字典。

例如(file_open_1.py):

path = 'd:/test.txt'
f_name = open(path)
print(f_name.name)

执行结果如下:

d:/test.txt

执行结果告诉我们打开的是 d盘 下的 test.txt文件 (执行该程序前,已经创建了一个名为 test.txt 的文件)。

这里有几个概念要先弄清楚:

  • 文件路径:在该程序中,我们先定义了一个 path 变量,变量值是一个文件的路径。文件的路径是指文件在计算机上的位置,如该程序中的 d:/test.txt 是指文件在 d盘 、文件名为 test.txt 。文件路径又分为绝对路径和相对路径。
    • 绝对路径:总是从根文件夹开始。比如在Windows环境下,一般从c盘、d盘等开始,c盘、d盘被称为根文件夹,在该盘中的文件都得从根文件夹开始往下一级一级查找。在Linux环境下,一般从usr、home等根文件开始。比如在上面的示例程序中,path变量值就是一个绝对路径,在文件搜索框中输入绝对路径可以直接找到该文件。
    • 相对路径:相对于程序当前工作目录的路径。比如当前工作文件存放的绝对路径是d:\python\workspace,如果使用相对路径,就可以不写这个路径,用一个“.”号代替这个路径值。

例如(file_open_2.py):

path = './test.txt'
f_name = open(path, 'w')
print(f_name.name)

执行结果如下:

./test.txt

除了单个点 (.) ,还可以使用两个点 (..) 表示父文件夹(或上一级文件夹)。此处不具体讨论,有兴趣可以自己尝试。

文件模式

我们在前面讲到,使用 open 函数时可以选择是否传入 mode 参数。在前面的示例中, mode 传入了一个值为 w 的参数,这个参数是什么意思呢? mode 可以传入哪些值呢?

mode常用的模式:

  • r:表示文件只能读取
  • w:表示文件只能写入
  • a:表示打开文件,在原有内容的基础上追加内容,在末尾写入
  • w+:表示可以对文件进行读写双重操作

mode二进制常用模式:

  • rb:以二进制格式打开一个文件,用于只读
  • wb:以二进制格式打开一个文件,用于只写
  • ab:以二进制格式打开一个文件,用于追加
  • wb+:以二进制格式打开一个文件,用于读写

使用 open 函数时,明确指定读模式和什么模式都不指定的效果是一样的,我们在前面的示例中已经验证。

使用写模式可以向文件写入内容。 + 参数可以用到其他任何模式中,指明读和写都是允许的。比如 w+ 可以在打开一个文件时用于文件的读写。

当参数带上字母 b 时,表示可以用来读取一个二进制文件。 Python 在一般情况下处理的都是文本文件,有时也不能避免处理其他格式的文件。

基本文件方法

读和写

open函数 返回的是一个 File对象 ,有了 File对象 ,就可以开始读取内容。如果希望将整个文件的内容读取为一个字符串值,可以使用 File对象 read() 方法。

read() 方法从一个打开的文件中读取字符串。需要注意, Python 字符串可以是二进制数据,而不仅仅是文字。

语法如下:

fileObject.read([count])

fileObject open函数 返回的 File对象 count 参数是从已打开的文件中读取的字节计数。该方法从文件的开头开始读入,如果没有传入 count ,就会尝试尽可能多地读取内容,很可能一直读取到文件末尾。

比如我们在 test.txt 文件中写入 Hello world!Welcome! ,执行如下代码(file_read.py):

path = './test.txt'
f_name = open(path, 'r')
print(f'read result: {f_name.read(12)}')

执行结果如下:

read result: Hello World!

print('read result:', f_name.read(12)) 更改为 print('read result:',f_name.read()) ,得到的执行结果如下:

read result: Hello world!Welcome!

由执行结果看到,没有指定读取字节数时, read 方法会读取打开文件中的所有字节。

除了读取数据外,我们还可以向文件中写入数据。在 Python 中,将内容写入文件的方式与 print 函数将字符串输出到屏幕上类似。

如果打开文件时使用读模式,就不能写入文件,即不能用下面这种形式操作文件:

open(path, 'rw')

Python 中,用 write() 方法向一个文件写入数据。 write() 方法可将任何字符串写入一个打开的文件。需要注意, Python 字符串可以是二进制数据,而不仅仅是文字。

write() 方法不会在字符串结尾添加换行符 ('\n') ,语法如下:

fileObject.write(string)

fileObject open函数 返回的 File对象 string 参数是需要写入文件中的内容。

该方法返回写入文件的字符串的长度。

例如(file_write.py):

f_name = open(path, 'w')
print(f"write length: {f_name.write('Hello World!')}")

执行结果如下:

write length: 12

由执行结果看到,我们向 test.txt 文件中写入了12个字符。下面验证一下写入的是否是我们指定的字符,在上面的程序中追加两行代码并执行:

f_name = open(path, 'r')
print('read result: ', f_name.read())

执行结果如下:

read result: Hello World!

由执行结果看到,写入文件的是我们指定的内容。不过这里有一个疑问,我们在这里执行了两次写入操作,得到的结果怎么只写入了一次?

写文件 write方法 的处理方式是:将覆写原有文件,从头开始,每次写入都会覆盖前面所有内容,就像用一个新值覆盖一个变量的值。若需要在当前文件的字符串后追加字符,该怎么办呢?

可以将第二个参数 w 更换为 a ,即以追加模式打开文件,例如(file_add.py):

path = './test.txt'
f_name = open(path, 'w')
print(f"write length: {f_name.write('Hello World!')}")
f_name = open(path, 'r')
print(f'read result: {f_name.read()}')

# 内容追加写入
f_name = open(path, 'a')
print(f"add length: {f_name.write('welcome!')}")
f_name = open(path, 'r')
print(f'read result: {f_name.read()}')

执行结果如下:

write length: 12
read result: Hello World!
add length: 8
read result: Hello World!welcome!

由执行结果看到,输出结果在文件末尾成功添加了对应字符串。

提示: 如果传递给open函数的文件名不存在,写模式 w 和追加模式 a 就会创建一个新的空文件,然后执行写入或追加。

如果想追加的字符串在下一行,该怎么办呢?

Python 中,用 \n 表示换行。对于上面的示例,若需要追加的内容在下一行,可以如下操作(file_change_line.py):

path = './test.txt'
f_name = open(path, 'w')
print(f"write length: {f_name.write('Hello World!')}")
f_name = open(path, 'r')
print(f'read result: {f_name.read()}')

f_name = open(path, 'a')
print('add length: ', f_name.write('\nwelcome!'))
f_name = open(path, 'r')
print(f'read result: {f_name.read()}')

执行结果如下:

write length: 12
read result: Hello World!
add length:  9
read result: Hello World!
welcome!

由执行结果看到,后面追加的内容在下一行了。

提示: 若需要读或写特定编码方式的文本,则需要给open函数传入encoding参数;若需要读取GBK编码的文件,则前面的示例可以改写为 f_name=open(path, 'r',encoding='gbk') ,这样读取到的文件就是 GBK 编码方式的文件了。

读写行

我们目前对文件的读操作是按字节读或整个读取,而写操作是全部覆写或追加,这样的操作在实际应用中很不实用。

Python 为我们提供了 readline() readlines() writelines() 等方法用于行操作,例如(file_read_write.py):

path = './test.txt'
f_name = open(path, 'w')
f_name.write('Hello World!\n')
f_name = open(path, 'a')
f_name.write('welcome!')
f_name = open(path, 'r')
print(f'readline result: {f_name.readline()}')

执行结果为:

readline result: Hello World!

由执行结果得知, readline 方法会从文件中读取单独一行,换行符为 \n readline 方法如果返回一个空字符串,说明已经读取到最后一行了。

readline 方法也可以像 read 方法一样传入数值读取对应的字符数,传入小于0的数值表示整行都输出。

如果将上面示例的最后一行:

print(f'readline result: {f_name.readline()}')

更改为:

# 当前读取方式为: readlines
print(f'readlines result: {f_name.readlines()}')

得到的输出结果为:

readlines result: ['Hello World!\n', 'welcome!']

输出结果为一个字符串的列表。列表中的每个字符串就是文本中的每一行,并且换行符也会被输出。

readlines 方法可以传入数值参数,当传入的数值小于等于列表中一个字符串的长度值时,该字符串会被读取;当传入小于等于0的数值时,所有字符都会被读取。

例如(file_read_lines.py):

path = './test.txt'
f_name = open(path, 'w')
str_list = ['Hello World!\n', 'welcome!\n', 'welcome!\n']
# 当前代码使用writelines进行数据写入
f_name.writelines(str_list)
f_name = open(path, 'r')
print(f'read result: {f_name.read()}')
f_name = open(path, 'r')
print(f'readline result: {f_name.readline()}')

执行结果如下:

read result: Hello World!
welcome!
welcome!

readline result: Hello World!

由执行结果看到, writelines 方法和 readlines 方法相反,传给它一个字符串列表(任何序列或可迭代对象),它会把所有字符串写入文件。如果没有 writeline 方法,那么可以使用 write 方法代替这个方法的功能。

关闭文件

我们前面介绍了很多读取和写入文件的内容,都没有提到在读或写文件的过程中出现异常时该怎么处理。在读或写文件的过程中,出现异常的概率还是挺高的,特别对于大文件的读取和写入,出现异常更是家常便饭。在读或写文件的过程中,出现异常该怎么处理呢?

这就需要用到前面介绍的异常的知识了,用 try 语句捕获可能出现的异常。在捕获异常前有一个动作要执行,就是使用 close 方法关闭文件。

一般情况下,一个文件对象在退出程序后会自动关闭,但是为了安全起见,还是要显式地写一个close方法关闭文件。

一般显式关闭文件读或写的操作如下(file_close.py):

path = './test.txt'
f_name = open(path, 'w')
print(f"write length: {f_name.write('Hello World!')}")
f.name.close()

这段代码和没有加 close 方法的执行结果一样。这样处理后的函数比没有加 close 方法时更安全,可以避免在某些操作系统或设置中进行无用的修改,也可以避免用完系统中所打开文件的配额。

对内容更改过的文件一定要记得关闭,因为写入的数据可能被缓存,如果程序或系统因为某些原因而崩溃,被缓存部分的数据就不会写入文件了。为了安全起见,在使用完文件后一定要记得关闭。

当使用 try 语句出现异常时,即使使用了 close 方法,也可能不被执行,这时该怎么办呢?

还记得 finally 子句吗?可以将 close 方法放在 finally 子句中执行,从而保证无论程序是否正常执行都会调用 close 方法。

上面的示例可以更改成更安全的形式(file_safe_close.py):

f_name = None
path = './test.txt'
try:
    f_name = open(path, 'w')
    print(f"write length: {f_name.write('Hello World!')}")
except Exception as e:
    print(f'程序异常: {e}')
finally:
    if f_name:
        print(f_name.name)
        f_name.close()

如果每次都要这么写,就会很烦琐,是否有更简便的方式处理呢?

Python 中引入了 with 语句自动帮我们调用 close 方法。可以使用 with 语句将上面的程序更改为(file_safer_close.py):

path = './test.txt'
with open(path, 'w') as f:
    print(f"write length: {f.write('Hello World!')}")


with open(path, 'r') as f:
    print(f'文件内容: {f.read()}')

这段代码和上面使用 try/finally 的效果一样,并且会自动调用 close 方法,不用显式地写该方法。可以发现,代码比前面简洁多了,后面可以多用这种方式编写。