Python:文件和异常 《Python编程:从入门到实践 第2版》笔记

文件和异常

从文件中读取数据

文本文件可以存储大量的数据,有时候我们处理数据,需要从文件中分部读取进行处理,再写回到文件中。所以,掌握文件读取很重要。文件数据在处理的时候,是将信息从外存读取到内存的一个过程。要使用文件,首先需要将信息读取到内存中。我们可以一次读取整个文件,也可以逐行读取文件。

读取整个文件

我们先向一个txt文件中随意写入一些信息,并将这个文件命名为demo1.txt,

13367648740
13578648902

with open('demo1.txt') as file_object:
    contents = file_object.read()
print(contents)

13367648740
13578648902

通过上面的程序,我们打开文件demo1.txt并读取其中的内容,再将其内容显示到屏幕上。

在程序的第一行中我们调用了open()函数来打开文件。def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True): *# known special case of open* 这里我们可以看到open()函数有一个必须要写入的参数,就是文件名file。Python在当前执行的文件所在的目录中查找指定的文件。也就是在我们当前的代码文件所在的目录寻找这个文件。函数open()返回一个表示文件的对象。Python将open()函数返回的对象赋给file_object供后续使用。

关键字with在不再需要访问文件后将其关闭。 在这个程序中, 注意到我们调用了open(), 但没有调用close()。 也可以调用open()和close()来打开和关闭文件, 但这样做时, 如果程序存在bug导致方法close()未执行, 文件将不会关闭。这看似微不足道, 但未妥善关闭文件可能导致数据丢失或受损。 如果在程序中过早调用close(), 你会发现需要使用文件时它已关闭(无法访问) , 这会导致更多的错误。 并非在任何情况下都能轻松确定关闭文件的恰当时机, 但通过使用前面所示的结构, 可让Python去确定: 你只管打开文件, 并在需要时使用它,Python自会在合适的时候自动将其关闭。

作为一个良好的程序员,应该要清楚什么时候打开文件,什么时候关闭文件,因为有时候with结构并不适合使用,我们应当记住,有打开文件的操作就应该有关闭文件的操作。

当我们获得了文件的对象后,我们可以通过read()函数来读取文件,并将读取到的文件作为一个字符串赋给变量contents,并通过打印输出这个内容。

由于read()函数到达文件末尾的时候会默认返回一个空字符串,所以在给到contents变量的时候,就会多出一个空行。要删除多出来的空行,可以使用rstrip()函数来删除字符串末尾的空白。

文件路径

open('demo1.txt') 在这里我们将demo1.txt作为参数传递给open()函数,Python会在当前执行的文件所在的目录中查找。这种参数实际上是一种简单的相对路径。

相对路径就是指由这个文件所在的路径引起的跟其它文件(或文件夹)的路径关系。使用相对路径可以为我们带来非常多的便利。有时候我们的文件可能与我们的程序文件不在同一个文件目录下,但是他们具有一些相对关系,我们就可以通过这种方式来获取文件信息。例如,我们在File.py文件的上级目录下重新建立一个 demo1.txt文件,并向其中写入信息:

13367648740
13578648902
18967409302

然后我们重新编辑File.py中的代码,注意open()函数中的参数。

with open('../demo1.txt') as file_object:
    contents = file_object.read()

print(contents)

13367648740
13578648902
18967409302

open('../demo1.txt') 这里面的参数与上次不同之处在于../ ,…表示当前文件的上级目录,/作为分割,后面给出文件名。

绝对路径是指目录下的绝对位置,直接到达目标位置,通常是从盘符开始的路径。一般来说,绝对路径比较长,因为它是从盘符开始,例如File.py目录下的demo1文件的绝对路径是:E:\PycharmProjects\learn\demo1.txt ,我们也可以把这个作为参数传递给open()函数,同样可以找到文件。

with open('E:\PycharmProjects\learn\demo1.txt') as file_object:
    contents = file_object.read()
print(contents.rstrip())

13367648740
13578648902

通过使用绝对路径, 可读取系统中任何地方的文件。如果你无法搞清楚确切的相对路径,那我建议使用绝对路径,这样是比较明确的。或者,最简单的做法是,将数据文件放在程序文件所在的目录中。

注意 如果在文件路径中直接使用反斜杠, 将引发错误, 因为反斜杠用于对字符串中的字符进行转义。 例如, 对于路径"C:\path\to\file.txt", 其中的\t将被解读为制表符。 如果一定要使用反斜杠, 可对路径中的每个反斜杠都进行转义,如"C:\path\to\file.txt"。

open()函数

========= ===============================================================
Character Meaning
--------- ---------------------------------------------------------------
'r'       open for reading (default)
'w'       open for writing, truncating the file first
'x'       create a new file and open it for writing
'a'       open for writing, appending to the end of the file if it exists
'b'       binary mode
't'       text mode (default)
'+'       open a disk file for updating (reading and writing)
'U'       universal newline mode (deprecated)
========= ===============================================================

open()函数有一个参数mode,其默认值为‘r’,也就是说我们打开文件,默认是以读模式进行。但是我们也可以采用其他方式,如写模式’w’,写模式下,是会将文件进行重写,即文件中原有的内容被清除掉,然后重新写入新的数据。也可以使用追加方式’a’方式写文件,以追加模式写文件不会清除文件中原有内容,而是在文件末尾写入新的内容。当然我们可以以多种方式读写问价,例如’r+w’,即以读写两种模式对文件进行操作,或者’r+a’方式操作文件。这就是文件的常见的几种操作模式。

逐行读取

有时候我们并不想一次性读取文件中的所有数据,而是希望能够读一行数据处理一行数据,毕竟如果数据太多,未必能将文件中的数据都一次性读入到内存中,或者一次性读入后占用大量内存不容易处理。

要以每次一行的方式读取文件,可以通过for循环来实现。

filename = 'demo1.txt'
with open(filename) as file_object:
    for line in file_object:
        print(line)

13367648740

13578648902

在程序中,我们可以将文件名先赋给一个变量,再将这个字符串变量传递给open()函数。这里我们发现,原本文件中这两个字符串之间没有空行,但是输出时却有了空行。原因是:因为在这个文件中, 每行的末尾都有一个看不见的换行符, 而函数调用print()也会加上一个换行符, 因此每行末尾都有两个换行符: 一个来自文件, 另一个来自函数调用print()。

这种情况,有两种方式进行处理,一种是print(line, end='') ,我们确保print语句不会再单独输出一个换行符’\n’,另外一种方式是print(line.rstrip()) 通过rstrip()函数将读入的数据的换行符去掉。

创建一个包含文件各行内容的列表

使用关键字with时, open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容, 可在with代码块内将文件的各行存储在一个列表中, 并在with代码块外使用该列表: 可以立即处理文件的各个部分, 也可以推迟到程序后面再处理。

这时我们可以使用readlines()函数。readlines()函数会逐行读取数据,并将每行读取的数据存储到一个列表中,并返回这个列表。我们只要将返回的数据给到一个变量即可按行读取数据。

with open(filename) as file_object:
    lines = file_object.readlines()
for line in lines:
    print(line)
print(lines)

13367648740
13578648902
['13367648740\n', '13578648902']

这里,利用readlines()函数,将逐行读取到的数据赋给变量lines,这里我也输出了lines变量,来清楚地看到readlines()函数具体读取到的是什么信息,又是如何存储的。

加工文件内容

将文件读取到内存中后, 就能以任何方式使用这些数据了。首先我们在demo1.txt文件中写入新的数据,pi的值。

3.1415926535
8979323846
2643383279
5028841971
6939937510
5820974944

然后我们按行读取这个文件,

with open(filename) as file_object:
    lines = file_object.readlines()
pi_string = ''
for line in lines:
    pi_string += line.rstrip()
print(pi_string)
print(len(pi_string))

3.141592653589793238462643383279502884197169399375105820974944
62

这样,我们就读取到这个文件中的pi值,并让它成为一段完整的字符串,而不是带有空行

注意 读取文本文件时, Python将其中的所有文本都解读为字符串。 如果读取的是数, 并要将其作为数值使用, 就必须使用函数int()将其转换为整数或使用函数float()将其转换为浮点数。

pi = float(pi_string)
print(pi)

3.141592653589793

很明显,由于文件中的pi值过长,所以float也会将其进行处理,保留一定的位数,如果想要更精确的数字,我们可以处理成double类型。这里给出的一种处理方式是,通过导入decimal模块中的Decimal函数,将字符串转换为double类型。

from decimal import Decimal
pi = Decimal(pi_string)
print(pi)

写入文件

我们通常会用文件来保存大量数据,所以写文件操作是非常重要的。

写入空文件

要将文本写入文件,我们需要在调用open()函数时,对于mode参数给出’w’或者‘a’,来告诉Python要以什么方式打开文件,不然就以默认的’r’读方式打开文件,这一点在上面都有提及过。

ofilename = 'w_demo1.txt'
with open(ofilename, mode='w') as file_object:
    file_object.write("See you again!")

这里我们在with结构中,写了一个名为‘w_demo1.txt’的文件,并向其中写入”See you again!“

如果要写入的文件不存在, 函数open()将自动创建它。 然而, 以写入模式(‘w’) 打开文件时千万要小心, 因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件的内容。

注意 Python只能将字符串写入文本文件。 要将数值数据存储到文本文件中, 必须先使用函数str()将其转换为字符串格式。

写入多行

函数write()不会在写入的文本末尾添加换行符,所以我们可以自己来进行换行写入。

ofilename = 'w_demo1.txt'
with open(ofilename, mode='w') as file_object:
    file_object.write("See you again!\n")
    file_object.write("谁会了解我们经历过怎样的旅程\n")
    file_object.write("谁会了解我们见证过怎样的美好\n")
    file_object.write("我都会在这里\n")
    file_object.write("与你聊聊另一种选择的可能")

这样,得到的文件就会带有换行了。

以追加模式写文件

写模式’w’会清除掉文件中原有的信息,所以我们可以考虑通过追加模式‘a’写文件,这种写文件会先将问定位符调整到文件的末尾再开始写文件。例如,我以追加模式向w_demo1.txt文件中写入see you again的歌词。

with open(ofilename, mode='a') as file_object:
    file_object.write("我懂我们都喜欢速度与激情\n")
    file_object.write("但有个声音告诉我 这美好并不会永恒\n")
    file_object.write("如何才能改变观点 用更宏观的视野看这世界\n")
    file_object.write("有付出的日子终有收获的时节\n")

异常

Python使用称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时, 它都会创建一个异常对象。 如果你编写了处理该异常的代码, 程序将继续运行; 如果未对异常进行处理, 程序将停止并显示traceback, 其中包含有关异常的报告。

通过在代码中添加一些异常处理,能够增强程序的健壮性,

异常是使用try-except代码块处理的。 try-except代码块让Python执行指定的操作, 同时告诉Python发生异常时怎么办。

在这里我只介绍一种异常处理,实际上,Python中有多种异常。

处理FileNotFoundError异常

使用文件时, 一种常见的问题是找不到文件: 查找的文件可能在其他地方, 文件名可能不正确, 或者这个文件根本就不存在。 对于所有这些情形, 都可使用try-except代码块以直观的方式处理。

filename = "pride and prejudice.txt"
try:
    with open(filename, mode='r', encoding='ansi') as file_object:
        contents = file_object.read()
except FileNotFoundError:
    print(f"Sorry, the file {filename} does not exist.")

Sorry, the file pride and prejudice.txt does not exist.

因为在File.py目录下确实不存在”pride and prejudice.txt“文件,所以open()函数会返回一个FileNotFoundError,而这里有一个except关键字,即当发生FileNotFoundError时,执行except中的代码块。所以处理此类错误。

分析文本

filename = "pride and prejudice.txt"
try:
    with open(filename, mode='r', encoding='utf-8') as file_object:
        contents = file_object.read()
except FileNotFoundError:
    print(f"Sorry, the file {filename} does not exist.")
else:
    words = contents.split()
    num_words = len(words)
    print(f"The file {filename} has about {num_words} words")

The file pride and prejudice.txt has about 121612 words

这一段程序是用来计算《傲慢与偏见》的文本中到底包含多少个单词。

str1 = "Pride and Prejudice is a book written by Jane Austen"
words = str1.split()
print(words)

['Pride', 'and', 'Prejudice', 'is', 'a', 'book', 'written', 'by', 'Jane', 'Austen']

str1是一个字符串变量,我们在这里面写了一句话,然后我们调用split()函数,这里要描述一个split()函数。

Python split() 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串,具体语法为str.split(str="", num=string.count(str)) ,str – 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t),num – 分割次数。默认为 -1, 即分隔所有。其返回分割后的字符串列表。

这样看来,第一段程序就好理解的多了,我们现在try-except结构中通过open()函数打开《傲慢与偏见》的txt文件,并且在open()函数中表明以只读方式打开文件,并且给出文件编码格式为utf-8,如果目录下没有发现该文件,则会执行except模块内的代码,否则执行else模块,进入else代码后,通过split()函数对contents字符串进行分割操作,得到一个列表。我们将返回的列表赋给words变量,最后用len()函数来计算words列表中的个数,即可得到《傲慢与偏见》中单词个数。

静默失败

程序并不是将每一次的失败信息都返回给用户,有时候,对于一些失败,我们并不希望程序返回什么信息,因为即使程序出现失败也仍能正常执行。对于这种情况,我们可以考虑静默失败,即不对用户输出失败信息。让程序能够静默失败,只需要在except代码中不做任何事情就可以。Python中pass关键字就能用于告诉Python编译器不做任何事情。

存储数据

大部分的程序都是交互的,即需要一些输入,并且给出一些输出。我们在前面介绍过列表,字典,元组等存储数据的方式。这里给出一种新的数据存储方式,使用模块json来存储数据。

模块json让你能够将简单的Python数据结构转储到文件中, 并在程序再次运行时加载该文件中的数据。 你还可以使用json在Python程序之间分享数据。 更重要的是, JSON数据格式并非Python专用的, 这让你能够将以JSON格式存储的数据与使用其他编程语言的人分享。 这是一种轻便而有用的格式, 也易于学习。

使用json.dump()和json.load()

函数json.dump()有两个必要的实参: 要存储的数据, 以及可用于存储数据的文件对象。json.dump()函数也有些默认好的参数,如果需要可以自己去找这部分代码理解其他的参数的作用。

函数json.load()有一个必要的实参:要读取的.json文件对象。同时也包括一些具有默认值的参数,可以自己查找理解。

# json.dump()函数
filename = 'output.json'
str1 = "Pride and Prejudice is a book written by Jane Austen"
with open(filename, mode='w') as file_object:
    json.dump(str1, file_object)

with open(filename, mode='r') as file_object:
    contents = json.load(file_object)
print(contents)

Pride and Prejudice is a book written by Jane Austen

这里我们先利用open()函数以写模式打开一个.json文件,然后我们将这个文件对象,连同字符串str1都作为参数传递给json.dump()函数,这样将字符串保存在一个json文件中了。我们在以读模式打开这个.json文件,将文件对象传递给json.load()函数,json.load()函数则会将文件中的内容作为一个字符串读取并返回。这样我们就得到文件中的内容。

注意:json.dump()和json.load()函数传入的参数都不是文件名,而是一个文件对象,我们需要先利用open()函数打开文件,获取一个文件对象,再将这个文件对象传递给json.dump()函数。

你可能感兴趣的:(Python,原创,python,开发语言)