学习文件处理,你将能够快速的分析大量的数据;学习错误处理,你将会编写在意外情形下也不崩溃的程序;学习异常,你将会管理程序运行时出现的错误;你还将学习Json,从而能够保存用户数据,以免在程序停止运行后丢失。异常一般来源于破坏程序的恶意企图以及错误数据。举个例子:假设QQ的密码不能包含字母,并且后台程序在判别时也忽略了对字母的识别(没有相关代码)。那么此时,如果你在设置密码时,输了字母,后台程序接受到了,但不知道如何处理这个“不按常理出牌”的密码,此时程序就又可能瘫痪,进而出现错误。“错误及异常”和其他面向对象语言中的一样,就是Java中俗称的“异常抛出机制”。学习这些机制的主要目的是提高程序的适用性、可用性和稳定性。
一、从文件中读取数据文本文件中可存储的数据量多得难以置信,每当需要分析或修改存储在文件中的信息时,读取文件是很有必要的。要使用文本文件中的信息,首先需要将信息读取到内存中。为此,可以一次性读取文件的全部内容,也可以以每一次的方式逐步读取。
1.1 读取整个文件我们先来创建一个包含几行文本的文件,名称为pi_digits.txt。内容如下截图:
接下来我们编写代码,读取这个txt文件中的内容,并输出:
with open('pi_digits.txt') as file_object: contents =file_object.read() print(contents)
要知道,无论我们以任何方式使用文件——哪怕仅仅是打印其内容,都得先打开文件,这样才能访问它。
A、函数open()接受一个参数:要打开的文件的名称。Python在当前执行的程序文件所在的目录中查找指定的文件,找到后,执行open()函数,然后返回一个表示文件的对象,并存储在file_object中。
B、关键字with的作用是在不再需要访问文件后将其关闭。在这个过程中,我们只调用了open(),没调用close()。你也可以调用open()和close()来打开和关闭文件,但这样做时,如果程序存在BUG,导致close()语句未执行,文件将不会关闭。看似微不足道,但未妥善地关闭文件可能会导致数据丢失或受损。如果在程序中过早的调用close(),你会发现需要使用文件时它已关闭(无法访问),这会导致更多地错误。(并非在任何情况下都能轻松确定关闭文件的恰当时机)但我们可以通过使用with,让Python去确定:我们只管打开文件,并在需要时使用它,Python自会在合适地时候自动将其关闭。
C、有了表示pi_digits.txt的文件对象后,我们使用read()方法读取这个文件的全部内容,并将其作为一个长长的字符串存储在变量contents中。这样我们就可以打印全部的内容了。
细心的人已经发现问题了,为什么输出后有这么多空行?这是因为read()到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除末尾的空行,可在print语句中使用rstrip():(我实验了,貌似没什么用啊)
with open('pi_digits.txt') as file_object: contents =file_object.read() print(contents.rstrip())
1.2 文件路径如果要Python打开不与程序文件位于同一目录中的文件,需要提供文件路径,它让Python到系统的特定位置去查找。文件路径分为绝对路径和相对路径。
A、相对路径:文件所处的位置是相对于当前运行的程序文件所属目录而言的路径,举个例子:(注意使用的是反斜杠)
C:\Users\Jeffery_PC\Desktop\code # py文件的目录C:\Users\Jeffery_PC\Desktop\code3 # 目标文件目录目标文件目录和py文件目录的前面一部分都是一样的,所以省略不写(相当于以py文件的目录为参考)所以目标文件的相对路径最终表示为:123\pi_digits.txt我们可以实验一下,看看能否读取文件成功:代码如下:with open('123\pi_digits.txt') as file_object: contents =file_object.read() print(contents.rstrip())
B、绝对路径:目标文件在计算机中的准确位置就是绝对路径,举个例子:
C:\Users\xxxxxx\Desktop\code3\pi_digits.txt # xxxxxx是我的信息,这里隐写了
在相对路径行不通时,可使用绝对路径。绝对路径通常比相对路径更长,因此将其存储在一个变量中,再将这个变量传递给open()会有所帮助。就目前而言,最简单的做法是:要么将数据文件存储在程序文件所在的目录,要么将其存储在程序文件所在目录下的一个文件夹中。
1.3 逐行读取读取文件时,常常需要检查其中的每一行:可能要在文件中查找特定的信息,或者要以某种方式修改文件中的文本。要以每次一行的方式检查文件,可对文件对象使用for循环:
filename = 'pi_digits.txt'with open(filename) as file_object: for line in file_object: print(line)
这段代码与之前的代码类似,就不解释了。出现空白行的原因是:读取文档每行的末尾都有一个看不见的换行符,而print语句也会加上一个换行符,因此每行末尾都有两个换行符:一个来自文件,一个来自print语句。要消除这些多余的空白行,可在print语句末尾使用rstrip():
filename = 'pi_digits.txt'with open(filename) as file_object: for line in file_object: print(line.rstrip())
1.4 创建一个包含文件各行内容的列表使用关键字with时,open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表里,并在with代码块外使用该列表:你可以立即处理文件的各个部分,也可以推迟到程序后面再处理。
例代码:
filename = 'pi_digits.txt'with open(filename) as file_object: lines = file_object.readlines() # 按行读取,并存储在列表中for line in lines: print(line.rstrip()) # 使用for循环来打印每一行的内容
1.5 使用从文件中读取的内容我们将会读取文件内容,并组成一个新的字符串(组成新的字符串就是我们的“使用”)(将文件读取到内存中后,就可以以任何方式使用这些数据了)
filename = 'pi_digits.txt'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))
看出这个字符串中间还有空格,所以可以使用strip()函数不是rstrip():
filename = 'pi_digits.txt'with open(filename) as file_object: lines = file_object.readlines() # 按行读取,并存储在列表中pi_string = ''for line in lines: pi_string += line.strip()print(pi_string) # 精确到30位小数的圆周率值print(len(pi_string)) # 输出字符串的长度
注意:读取文本文件时,Python将其中的所有文本都解读为字符串。如果读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换为整数,或使用函数float()将其转换为浮点数。
1.6 处理大体量文件即将要处理的文件内部包含圆周率小数点后一百万位,我们将其打印出来。
filename = 'pi_million_digits.txt'with open(filename) as file_object: lines = file_object.readlines() # 按行读取,并存储在列表中pi_string = ''for line in lines: pi_string += line.strip()print(pi_string[:52] + "...") # 打印到小数点后50位,因为整数和小数点也需要2位print(len(pi_string))
对于你可处理的数据量,Python没有任何限制;只要系统的内存足够多,你想处理多少数据都可以。
1.7 牛刀小试如果大家关注过北大数院微信公众号,那么对其中一个功能“在圆周率中寻找你的生日”就很熟悉了,这里我们自己也实现一下,检测某个人的生日是否在圆周率的100万当中。逻辑是:将自己的生日字符串化,再检查这个字符串是否包含在pi_string中。
filename = 'pi_million_digits.txt'with open(filename) as file_object: lines = file_object.readlines() # 按行读取,并存储在列表中pi_string = ''for line in lines: pi_string += line.strip()birthday = input ("Enter your birthday, in the form mmddyy: ") # input()函数需要用户输入信息if birthday in pi_string: print("Your birthday appears in the first million digits of pi!")else: print("Your birthday does not appear in the first million digits of pi!")
作者的生日的确出现在Π的前100万位中。
二 将数据写入文件保存数据的最简单的方式之一是将其写入到文件中。通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在。
2.1 写入空文件要将文本写入文件,你在调用open()时需要提供另一个实参,告诉Python你要写入打开的文件。接下来以一段具体代码演示一下:
filename = 'programming.txt'# 如果你写入的文件不存在,open()将自动创建with open(filename, 'w') as file_object: # 为open()函数提供两个实参:第一个指明要打开的文件名称 # 第二个实参指明:我们将以“写入模式”打开这个文件 # ‘r’:读取模式 # ‘w’:写入模式 # ‘a’:附加模式 # ‘r+’:读取写入模式 # 如果省略了模式实参,Python将以默认的只读模式打开文件 file_object.write("I love programming!")
成功写入!这里需要注意的是:
A、以写入(‘W’)模式打开文件时,千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
B、Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数str()将其转换为字符串格式。
2.2 写入多行这里只需要注意一点,函数write()不会在你写入的文本末尾自动添加换行符,需要你自己添加。如果还需要其他格式,可以使用“空格”、“制表符”、“空行”来设置文本格式:
filename = 'programming.txt'with open(filename, 'w') as file_object: # 为open()函数提供两个实参:第一个指明要打开的文件名称 # 第二个实参指明:我们将以“写入模式”打开这个文件 # ‘r’:读取模式 # ‘w’:写入模式 # ‘a’:附加模式 # ‘r+’:读取写入模式 # 如果省略了模式实参,Python将以默认的只读模式打开文件 file_object.write("I love programming!\n") file_object.write("I love creating new games.\n")
2.3 附加到文件(追加到文件)如果你要给文件添加内容,而不是覆盖原有的内容,可以以附加模式打开文件。如果你以附加模式打开文件时,Python不会在返回文件对象前清空文件,而你写入到文件的行都将添加到文件末尾。如果指定的文件不存在,Python将会为你创建一个空文件。
filename = 'programming.txt'with open(filename, 'a') as file_object: file_object.write("I also love finding meaning in large datasets. \n") file_object.write("I love creating apps that can run in a browser.\n")
三 异常
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
异常是使用try-except代码块处理的。try-except代码块让Python执行指定的 *** 作,同时告诉Python发生异常时怎么办。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。
3.1 处理ZerodivisionError异常举个简单的例子:在数学中,0不可以作除数。那如果在Python中出现这样的问题呢?
print(5/0)
同样,程序因为出错了被迫停止,Python指出了出错的地方及错误内容。接下来,我们使用try-except代码块来避免这种情况发生:告诉Python,如果出现了这样的情况该怎么办。
3.2 使用try-except代码块处理异常在编写代码时,我们可能会预知某些异常的发生,此时可以写一个try-except代码块来处理可能引发的异常。就像英文的意思一样,让Python试一下,如果行就继续;不行的话,那就执行别的代码(except),反正程序别停止了。我们对上述代码进行改写:
try: # 我们将导致错误的代码行print(5/0)放在了一个try代码块中。 # 如果try代码块运行起来没问题,那就自动跳过except代码块 # 如果try代码块中的代码导致了错误,Python将查找这样的except代码块, # 并运行其中的代码,即其中指定的错误与引发的错误相同 print(5/0)except ZerodivisionError: print("You can't divIDe by zero! ")
3.3 使用异常避免崩溃发生错误时,如果程序还有工作没有完成,妥善地处理错误就尤其重要。这种情况经常会出现在要求用户提供输人的程序中;如果程序能够妥善地处理无效输入,就能再提示用户提供有效输入,而不至于崩溃。下面来创建一个只执行除法运算的简单计算器:
print("Give me two numbers, and I'll divIDe them. ")print("Enter 'q' to quit. ")while True: first_number = input("\nFirst number: ") if first_number == 'q': break second_number = input("Second number: ") if second_number == 'q': break answer =int(first_number) / int(second_number) print(answer)
当以上这段代码输入除数为0的算式时,问题就出现了:
这种计算器除法程序可不是我们需要的,如果被怀有恶意的用户遇到,他们会得知很多信息,从而做一些破坏代码的事情。所以我们得使用try-except代码块,不过这里还得加一个“else”,相关的解释是:通过将可能引发错误的代码放在try-except代码块中,可提高这个程序抵御错误的能力。错误是执行除法运算的代码行导致的,因此我们需要将它放到try-except代码块中。这个示例还包含一个else代码块;依赖于try代码块成功执行的代码都应放到else代码块中。
print("Give me two numbers, and I'll divIDe them. ")print("Enter 'q' to quit. ")while True: first_number = input("\nFirst number: ") if first_number == 'q': break second_number = input("Second number: ") if second_number == 'q': break # try代码块里只能包含可能导致错误的代码。 try : answer = int(first_number) / int(second_number) except ZerodivisionError: # 出现错误后的解决代码 print("You can't divIDe by 0. ") # 依赖于try代码块成功执行的代码都放在else代码块中 else: print(answer)
try-except-else代码块工作原理小结:Python尝试执行try代码块中的代码;只有可能引发异常的代码才需要放在try语句中。有时候,有一些仅在try代码块成功执行时才需要运行的代码,这些代码应放在else代码块中。except代码块告诉Python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。通过预测可能发生错误的代码,可编写健壮的程序,它们即便面临无效数据或缺少资源,也能继续运行,从而能够抵御无意的用户错误和恶意的攻击。
3.4 处理fileNotFundError异常使用文件时,一种常见的问题是找不到文件:你要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本不存在。对于所有这些情形,都可以使用try-except代码块以直观的方式进行处理。
filename = 'alice.txt'with open(filename) as f_obj: contents = f_obj.read()
这个错误是函数open()导致的,因此要处理这个错误,必须将try语句放在包含open()的代码之前:
filename = 'alice.txt'try: with open(filename) as f_obj: contents = f_obj.read()except fileNotFoundError: msg = "Sorry, the file " + filename + " does not exist. " print(msg)
四 处理文本4.1 分析文本
我们将会分析整本书的文本文件,下面我们来提取童话“Alice in Wonderland”的文本,并尝试计算它包含多少个单词。我们将使用方法split(),它根据一个字符串创建一个单词列表。下面是对只包含童话名“”Alice in Wonderland“的字符串调用方法split(
)的结果:
Title = 'Alice in Wonderland'print(Title.split())
方法split()以空格为分隔符将字符串拆成多个部分,并将这些部分都存储到一个列表中。结果是一个包含字符串中所有单词的列表,虽然有些单词可能包含标点。接下来我们使用这个方法,对整篇小说进行处理分析,统计这篇童话大致包含多少个单词:
filename = 'alice.txt'try: with open(filename) as f_obj: contents = f_obj.read()except fileNotFoundError: msg = "Sorry, the file " + filename + " does not exist. " print(msg)else: # 计算文件大致包含多少个单词 words = contents.split() num_words = len(words) print("The file " + filename + " has about " + str(num_words) + " words")
4.2 使用多个文件下面多分析几本书:
def count_words(filename): """计算一个文件大致包含多少个单词""" try: with open(filename) as f_obj: contents = f_obj.read() except fileNotFoundError: msg = "Sorry, the file " + filename + " does not exist. " print(msg) else: # 计算文件大致包含多少个单词 words = contents.split() num_words = len(words) print("The file " + filename + "has about " + str(num_words) + " words. ")# sIDdhartha.txt文件不存在,以此来展示程序的出色。filenames = ['alice.txt','sIDdhartha.txt','moby_dict.txt','little_women.txt']for filename in filenames: count_words(filename)
在这个示例中,使用try-except代码块提供了两个重要的优点:A、避免让用户看到traceback;B、让程序继续分析找到其他文件。
4.3 失败时一声不吭上面这个例子,在我们找不到对应的文件夹时,Python会提示用户有一个文件没有找到。但并非每次捕获到异常时都需要告诉用户,有时候你希望程序发生异常时一声不吭:
我们可以将“except fileNotFoundError:”中的代码全部删除,改为pass。
def count_words(filename): """计算一个文件大致包含多少个单词""" try: with open(filename) as f_obj: contents = f_obj.read() except fileNotFoundError: # msg = "Sorry, the file " + filename + " does not exist. " # print(msg) pass else: # 计算文件大致包含多少个单词 words = contents.split() num_words = len(words) print("The file " + filename + "has about " + str(num_words) + " words. ")# sIDdhartha.txt文件不存在,以此来展示程序的出色。filenames = ['alice.txt','sIDdhartha.txt','moby_dict.txt','little_women.txt']for filename in filenames: count_words(filename)
又多了一个关键字“pass”,pass语句还充当了占位符,他提醒你在程序的某个地方什么都没做,并且以后也许要在这里做些什么。
4.4 决定报告哪些错误在什么情况下该向用户报告错误?在什么情况下又应该在失败时一声不吭呢? 如果用户知道要分析哪些文件,他们可能希望在有文件没有分析时出现一条消息,将其中的原因告诉他们。如果用户只想看到结果,而并不知道要分析哪些文件,可能就无需在有些文件不存在时告知他们。
向用户显示他不想看到的信息可能会降低程序的可用性。Python的错误处理结构让你能够细致地控制与用户分亨错误信息的程度,要分享多少信息由你决定。编写得很好且经过详尽测试的代码不容易出现内部错误,如语法或逻辑错误,但只要程序依赖于外部因素,如用户输人、存在指定的文件、有网络链接,就有可能出现异常。凭借经验可判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。
五 存储数据很多程序都要求用户输入某种信息,程序都把用户提供的信息存储在列表和字典等数据结构中。用户关闭程序时,你几乎总是要保存他们提供的信息。一种简单的方式是使用模块Json来存储数据。
模块Json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。你还可以使用Json在Python程序之间分享数据。更重要的是,JsON数据格式并非Python专用的,这让你能够将以JsON格式存储的数据与使用其他编程语言的人分享。这是一种轻便格式,很有用,也易于学习。(JsON(JavaScript Object Notation)格式最初是为JavaScript开发的,但随后成了一种常见格式,被包括Python在内的众多语言采用)
5.1 使用Json.dump()和Json.load()我们编写一个存储一组数字的简短程序,再编写一个将这些数字读取到内存中的程序。
import Jsonnumbers = [2,3,5,7,11,13]filename = 'number.Json'# 以写的形式打开文件,使用的是Json.dump()with open(filename,'w') as f_obj: # 把数字写入文件 Json.dump(numbers,f_obj)# 读取数据,使用的是Json.load()with open(filename) as f_obj: # 为了区别numbers,后面加了下划线和2 numbers_2 = Json.load(f_obj)# 输出print(numbers_2)
用记事本打开numbers.Json文件,显示如下:
代码先导入了Json模块,随后创建了一个数字列表,存储了一些数字。先是将数字存储到了Json文件里,后又从Json文件中读取出来。其中使用了Json.dump()和Json.load()方法。
5.2 保存和读取用户生成的数据对于用户生成的数据,使用Json保存将大有好处。因为如果不以某种方式进行存储,等程序停止运行时,用户的信息将会丢失。举个例子来说明:
将用户的姓名存储在Json文件里:
# 导入Json包import Json# 需要用户输入一个名字username = input("What is your name? ")# 将Json文件名存储在filename变量中filename = 'username.Json'# 以写的方式打开文件,没有就创建一个Json文件,并重新命名为f_objwith open(filename,'w') as f_obj: # 使用Json.dump()方法写入输入的名字 Json.dump(username,f_obj) # 输出提示,表明已将用户输入的名字存储了起来 print("We'll remember you when you come back, " + username +"! ")
将Json文件的内容再读取出来,比如对这个人进行问候:
# 导入Json包import Json# 将文件名存入filename变量中filename = 'username.Json'# 读取Json文件with open(filename,'r') as f_obj: username = Json.load(f_obj) # 打印问候语 print("Welcome back, " + username + "! ")
@H_463_419@
下面我们将这两个程序合并:
# 导入Json包import Json# 如果以前存储了用户名就加载# 否则,就提示用户输入用户名并存储filename = 'username.Json'try: with open(filename) as f_obj: username = Json.load(f_obj)except fileNotFoundError: username = input("What is your name? ") with open(filename,'w') as f_obj: Json.dump(username,f_obj) print("We will remember you when you back, " + username + "! ")else: print("Welcome come back, " + username + "! ")
如果这个文件存在,就将其中的用户名读取到内存中,再执行else代码块,即打印一条欢迎用户回来的消息。用户首次运行这个程序时,文件username.,Json不存在,将引发fileNotFoundError异常,因此Python将执行except代码块:提示用户输人其用户名,再使用Json.dump()存储该用户名,并打印一句问候语。
5.3 重构将代码划分为一系列完成具体工作的函数的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。接下来,我们对上述问候用户的程序进行重构:
第一次重构:
# 导入Json包import Jsondef greet_user(): # 考虑到现在使用了一个函数,我们删除注释,取而代之的是一个文档字符串来指出程序是做什么的。 """问候用户,并指出其名字""" filename = "username.Json" try: with open(filename) as f_obj: username = Json.load(f_obj) except fileNotFoundError: username = input("What is your name? ") with open(filename, 'w') as f_obj: Json.dump(username, f_obj) print("We will remember you when you back, " + username + "! ") else: print("Welcome come back, " + username + "! ")greet_user()
但上述的函数所做的不仅仅是问候用户,还在存储了用户时获取它,而在没有存储用户名时提示用户输入一个。所以我们可以对greet_user()再次重构:
# 导入Json包import Jsondef get_stored_username(): """如果存储了用户名,就获取它""" filename = 'username.Json' try: with open(filename) as f_obj: username = Json.load(f_obj) except fileNotFoundError: return None else: return usernamedef greet_user(): """问候用户,并指出其名字""" username = get_stored_username() if username: print("Welcome come back, " + username + "! ") else: username = input("What is your name? ") filename = "username.Json" with open(filename,'w') as f_obj: Json.dump(username,f_obj) print("We will remember you when you back, " + username + "! ") greet_user()
这是该文件不存在时的输入输出:
这是该文件存在时的输出:
上述代码的功能更细化了,我们还可以进一步重构:将greet_user()中的提示用户输入的代码放在一个独立的函数中。
# 导入Json包import Jsondef get_stored_username(): """如果存储了用户名,就获取它""" filename = 'username.Json' try: with open(filename) as f_obj: username = Json.load(f_obj) except fileNotFoundError: return None else: return usernamedef get_new_username(): """提示用户输入用户名""" username = input("What is your name? ") filename = "username.Json" with open(filename, 'w') as f_obj: Json.dump(username, f_obj) return usernamedef greet_user(): """问候用户,并指出其名字""" username = get_stored_username() if username: print("Welcome come back, " + username + "! ") else: username = get_new_username() print("We will remember you when you back, " + username + "! ") greet_user()
自行体会问候用户程序的三个版本的差异,可以看出代码的功能逐渐具体、清晰,易读性增强。因此,重构是编写代码中必不可少的一环。
总结以上是内存溢出为你收集整理的十四、Python第十四课——文件和异常全部内容,希望文章能够帮你解决十四、Python第十四课——文件和异常所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)