Python编程从入门到实践——第十章

第十章——文件和异常

将学习异常,它们是Python创建的特殊对象,用于管理程序运行时出现的错误;你还将学习模块json,它让你能够保存用户数据,以免在程序停止运行后丢失。

10.1 从文件中读取数据

要使用文本文件中的信息,首先需要将信息读取到内存中。为此,你可以一次性读取文件的
全部内容,也可以以每次一行的方式逐步读取。

10.1.1 读取整个文件

创建一个文件,它包含精确到小数点后30位的圆周率值,且在小数点后每10位处都换行:

将该文件保存到本章程序所在的目录中

使用下述代码读取文件,并将其显示在屏幕上

file_reader.py

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

输出结果如下

函数open()接受一个参数:要打开的文件的名称。 Python在当前执行的文件所在的目录中查找指定的文件,在这里, open('pi_digits.txt')返回一个表示文件pi_digits.txt的对象; Python将这个对象存储在我们将在后面使用的变量中。

你也可以调用open()和close()来打开和关闭文件,但这样做时,如果程序存在bug,导致close()语句未执行,文件将不会关闭。所以我们只要负责打开文件,调用文件,等合适的时机python会自动关闭文件,不用自己添加close来判断关闭文件的时间。

read()函数读取文件,相比于原始文件,该输出唯一不同的地方是末尾多了一个空行,原因是read读取文件时会返回一个空字符串,所以就会有空格了。

删除空格的方式是使用rstrip()函数,rstrip()函数就是用来删除字符串末尾的空格,这样输出就不会有空格了。

10.1.2 文件路径

将类似pi_digits.txt这样的简单文件名传递给函数open()时, Python将在当前执行的文件(即.py程序文件)所在的目录中查找文件。

要让Python打开不与程序文件位于同一个目录中的文件,需要提供文件路径,它让Python到系统的特定位置去查找。

相对文件路径:可以打开当前运行程序文件下的子文件夹的文件

with open ('text_files\\test.txt') as file_object:
    contents = file_object.read()
    print(contents.rstrip())

输出结果

绝对路径:可读取系统任何地方的文件,一定要注意,python中的斜杠是反斜杠

file_path = 'E:\\Chapter10\\text_files\\test.txt'
with open (file_path) as file_object:
    contents = file_object.read()
    print(contents.rstrip())

输出结果如下

10.1.3 逐行读取

要以每次一行的方式检查文件,可对文件对象使用for循环:

with open ('pi_digits.txt') as file_object:
    for line in file_object:
        print(line)

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

使用关键字with时, open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表中。

with open ('pi_digits.txt') as file_object:
    lines = file_object.readlines()
    
for line in lines:
    print(line.rstrip())

readlines()函数指从文件中读取每一行,并将其存储在一个列表中。

10.1.5 使用文件的内容

输出文件内容,显示文件中字符的长度

with open ('pi_digits.txt') 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()区别

strip() 去除字符串两端的字符,rstrip() 只去除字符串末尾的字符。

10.1.6 包含一百万位的大型文件

对于你可处理的数据量, Python没有任何限制;只要系统的内存足够多,你想处理多少数据
都可以。

10.1.7 圆周率值中包含你的生日吗

10.1动手试一试

10-1 Python 学习笔记

在文本编辑器中新建一个文件,写几句话来总结一下你至此学到的 Python 知识,其中每一行都以“In Python you can”打头。将这个文件命名为learning_python.txt,并将其存储到为完成本章练习而编写的程序所在的目录中。编写一个程序,它读取这个文件,并将你所写的内容打印三次:第一次打印时读取整个文件;第二次打印时遍历文件对象;第三次打印时将各行存储在一个列表中,再在 with 代码块外打印它们。
读取整个文件

with open("learning_python.txt") as file_object:
    contents = file_object.read()
    print(contents.rstrip())

逐行读取文件

with open("learning_python.txt") as file_object:
    for line in file_object:
        print(line.rstrip())

将文件存储在列表里

with open("learning_python.txt") as file_object:
    lines = file_object.readlines()
for line in lines:
    print(line.rstrip())
    

输出结果均为

10-2 C 语言学习笔记

可使用方法 replace()将字符串中的特定单词都替换为另一个单词。下面是一个简单的示例,演示了如何将句子中的'dog'替换为'cat':
>>> message = "I really like dogs."
>>> message.replace('dog', 'cat')
'I really like cats.'
读取你刚创建的文件 learning_python.txt 中的每一行,将其中的 Python 都替换为另一门语言的名称,如 C。将修改后的各行都打印到屏幕上。
读取整个文件

with open("learning_python.txt") as file_object:
    contents = file_object.read()
    message=contents.replace('Python','C')
    print(message)

逐行读取文件

with open("learning_python.txt") as file_object:
    for line in file_object:
        message = line.replace('Python','C')
        print(message.rstrip())

将文件存储在列表里

with open("learning_python.txt") as file_object:
    for line in file_object:
        content=line.replace('Python','C')
        print(content.rstrip())

输出结果均为

10.2 写入文件

10.2.1 写入空文件

file_name = 'programming.txt'

with open(file_name,'w') as file_object:
    file_object.write('I love programming!')

第一个实参也是要打开的文件的名称;第二个实参( 'w')告诉Python,我们要以写入模式打开这个文件。打开文件时,可指定读取模式( 'r')、 写入模式( 'w')、 附加模式( 'a')或让你能够读取和写入文件的模式( 'r+')。如果你省略了模式实参, Python将以默认的只读模式打开文件。

运行代码之后,会在运行代码的根目录下生成一个progrming文件,如下

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

10.2.2 写入多行

函数write()不会在你写入的文本末尾添加换行符,要让每个字符串都单独占一行,需要在write()语句中包含换行符:

file_name = 'programming.txt'

with open(file_name,'w') as file_object:
    file_object.write('I love programming!\n')
    file_object.write('I love playing new game!\n')

10.2.3 附加到文件

如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件。你以附加模式打开文件时, Python不会在返回文件对象前清空文件,而你写入到文件的行都将添加到文件末尾。如果指定的文件不存在, Python将为你创建一个空文件。

file_name = 'programming.txt'

with open(file_name,'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')

programming.txt中的内容如下:

I love programming!
I love playing new game!
I also love finding meaning in large datasets.
I love creating apps that can run in a browser.

10.2动手试一试

10-3 访客

编写一个程序,提示用户输入其名字;用户作出响应后,将其名字写入到文件guest.txt 中。

file_name = 'programming.txt'

with open(file_name,'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')

输出结果


10-4 访客名单

编写一个 while 循环,提示用户输入其名字。用户输入其名字后,在屏幕上打印一句问候语,并将一条访问记录添加到文件 guest_book.txt 中。确保这个文件中的每条记录都独占一行。

错误代码如下:

file_name = 'guest.txt'

with open(file_name,'w') as file_object:
    message = input('Please input your name:\n')
    while message:
        print("欢迎你进入快乐世界!")
        file_object.write(message+'\n')
        message = input('Please input your name:\n')

怎么只在屏幕上显示呢,不将写的名字保存在txt文件中呢?打开文件要写在循环里面,顺序应该是,用户输入内容了,再打开文件,然后将用户输入的内容写入文件。

file_name = 'guest.txt'

while True:
    message = input('Please input your name:\n')
    if message == 'quit':
        break
    else:
        with open(file_name, 'a') as file_object:
            print("欢迎你进入快乐世界!")
            file_object.write(message + '\n')

print("已退出循环,文件已保存。")
10-5 关于编程的调查

编写一个 while 循环,询问用户为何喜欢编程。每当用户输入一个原因后,都将其添加到一个存储所有原因的文件中。

file_name = 'reason.txt'

while True:
    message = input("输入你的名字:")
    if message == 'quit':
        break
    else:
        with open(file_name,'a') as file_object:
            file_object.write('The reasons are:')
            file_object.write(message+'\n')

输出结果如下(前三行是因为它不显示中文)

10.3 异常

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

异常是使用try-except代码块处理的。显示你编写的友好的错误消息,而不是令用户迷惑的traceback。

10.3.1 处理 ZeroDivisionError 异常

不能将一个数字除以0,但我们还是让Python这样做吧:

print(5/0)

python的输出结果如下:

10.3.2 使用 try-except 代码块

你让Python尝试运行一些代码,并告诉它如果这些代码引发了指定的异常,该怎么办。ZeroDivisionError就是刚才控制台输出的特定错误。

书写代码如下:

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")
    

控制台输出结果:

10.3.3 使用异常避免崩溃

在division.py中输入下面的代码:

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("\nSecond number:")
    if second_number == 'q':
        break
    answer = int(first_number) / int(second_number)
    print(answer) 
    

当除数是0的时候,程序将崩溃,让用户看到traceback的信息不太好,有些黑客可能会根据你的traceback了解更多信息,所以不应该将这样的错误展现出来。

10.3.4 else 代码块

将可能引发错误的代码放在try-except代码块里,提高执行能力,正确执行的代码放在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("\nSecond number:")
    if second_number == 'q':
        break
    try:
        answer = int(first_number) / int(second_number)
    except ZeroDivisionError:
        print("You can't divide zero!")
    else:
        print(answer) 
    

输出结果如下,出现错误的代码输出了友好的提示信息,正确的代码也可以运行。

10.3.5 处理 FileNotFoundError 异常

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

alice.py

filename = 'alice.txt'

with open(filename) as file_object:
    contents = file_object.read()
    

控制台显示没有此文件

添加try-except代码块,判断出现问题的代码在open函数打开的时候,在此处添加代码块

filename = 'alice.txt'

try:
    with open(filename) as file_object:
        contents = file_object.read()
except:
    print("Sorry, the file alice.txt does not exist.")
    

在这个示例中, try代码块引发FileNotFoundError异常,因此Python找出与该错误匹配的
except代码块,并运行其中的代码。最终的结果是显示一条友好的错误消息,而不是traceback:

10.3.6 分析文本

方法split()以空格为分隔符将字符串分拆成多个部分,并将这些部分都存储到一个列表中。

filename = 'pi_digits.txt'

try:
    with open(filename) as file_object:
        contents = file_object.read()
except:
    print("Sorry, the file alice.txt does not exist.")
else:
    #计算里面包含了多少单词
    words = contents.split()
    long = len(words)
    print(long)   

输出结果如下

10.3.7 使用多个文件

将计算文件里有多少单词的操作放在函数下,将需要计算单词数量的文件写在列表中

def count_words(filename):
    """计算文件有多少单词"""
    try:
        with open(filename) as file_object:
            contents = file_object.read()
    except:
        print("Sorry, the file alice.txt does not exist.")
    else:
        #计算里面包含了多少单词
        words = contents.split()
        long = len(words)
        print(long)   

filenames = ["pi_digits.txt",'reason.txt']
for filename in filenames:
    count_words(filename)

输出结果如下,aaa.txt文件不存在,并不影响其他存在的文件进行单词数量的计算。

10.3.8 失败时一声不吭

并非每次捕获到异常时都需要告诉用户,有时候你希望程序在发生异常时一声不吭,就像什么都没有发生一样继续运行。要让程序在失败时一声不吭,可像通常那样编写try代码块,但在except代码块中明确地告诉Python什么都不要做。 Python有一个pass语句,可在代码块中使用它来让Python什么都不要做:
在上面一个实例程序本来需要输出的提示信息的位置except写为pass

def count_words(filename):
    """计算文件有多少单词"""
    try:
        with open(filename) as file_object:
            contents = file_object.read()
    except:
        pass
    else:
        #计算里面包含了多少单词
        words = contents.split()
        long = len(words)
        print(long)   

filenames = ["pi_digits.txt",'aaa.txt','reason.txt']
for filename in filenames:
    count_words(filename)

输出结果如下

10.3.9 决定报告哪些错误

编写得很好且经过详尽测试的代码不容易出现内部错误,如语法或逻辑错误,但只要程序依赖于外部因素,如用户输入、存在指定的文件、有网络链接,就有可能出现异常。凭借经验可判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。

10.3动手试一试

10-6 加法运算

提示用户提供数值输入时,常出现的一个问题是,用户提供的是文本而不是数字。在这种情况下,当你尝试将输入转换为整数时,将引发 TypeError 异常。

编写一个程序,提示用户输入两个数字,再将它们相加并打印结果。在用户输入的任何一个值不是数字时都捕获 TypeError 异常,并打印一条友好的错误消息。对你编写的程序进行测试:先输入两个数字,再输入一些文本而不是数字。

代码如下:

while True:
    first_number = input("Please input first number: ")
    second_number = input("Please input second number: ")
    
    if first_number =='q':
        break
    elif second_number =='q':
        break
    else:
        try:
            sum = int(first_number) + int(second_number)
        except TypeError:
            print("Please input some number!")
        else:
            print(sum)

控制台显示如下:

值出错了应该是value.error,所以修改一下代码这一行

重新输出

这时显示不正确的话会提示:请输入数字!

补充:用户输入值之后,相加需要将string类型转化为int类型,要不然+号的作用是字符串的连接

10-7 加法计算器

将你为完成练习 10-6 而编写的代码放在一个 while 循环中,让用户犯错(输入的是文本而不是数字)后能够继续输入数字。

我10-6就是这么做的~所以不用写了!!!

10-8 猫和狗

创建两个文件 cats.txt 和 dogs.txt,在第一个文件中至少存储三只猫的名字,在第二个文件中至少存储三条狗的名字。编写一个程序,尝试读取这些文件,并将其内容打印到屏幕上。将这些代码放在一个 try-except 代码块中,以便在文件不存在时捕获 FileNotFound 错误,并打印一条友好的消息。将其中一个文件移到另一个地方,并确认 except 代码块中的代码将正确地执行。

def read_files(filename):
    try:
        with open(filename) as file_object:
            contents = file_object.read()       
    except FileNotFoundError:
        print("Sorry, I can't find this file!")
    else:
        print(contents)

filenames = ['cats.txt','dogs.txt','fogs.txt']
for filename in filenames:
    read_files(filename)        

输出结果:

10-9 沉默的猫和狗

修改你在练习 10-8 中编写的 except 代码块,让程序在文件不存在时一言不发。

代码如下:

def read_files(filename):
    try:
        with open(filename) as file_object:
            contents = file_object.read()       
    except FileNotFoundError:
        pass
    else:
        print(contents)

filenames = ['cats.txt','fogs.txt','dogs.txt']
for filename in filenames:
    read_files(filename)        

主要改了这一行代码:

输出结果为:

10-10 常见单词

访问项目 Gutenberg( http://gutenberg.org/),并找一些你想分析的图书。下载这些作品的文本文件或将浏览器中的原始文本复制到文本文件中。

补充:确定特定的单词或短语在字符串

你可以使用方法 count()来确定特定的单词或短语在字符串中出现了多少次。例如,下面的代码计算'row'在一个字符串中出现了多少次:
>>> line = "Row, row, row your boat"
>>> line.count('row')
2
>>> line.lower().count('row')
3
请注意,通过使用 lower()将字符串转换为小写,可捕捉要查找的单词出现的所有次数,而不管其大小写格式如何。

编写一个程序,它读取你在项目 Gutenberg 中获取的文件,并计算单词'the'在每个文件中分别出现了多少次。

with open('reason.txt') as file_object:
    
    contents = file_object.read()
    num = contents.lower().count('the')
    print(num)

输出结果:

10.4 存储数据

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

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

编写一个存储一组数字的简短程序,再编写一个将这些数字读取到内存中的程序。一个程序将使用json.dump()来存储这组数字,而第二个程序将使用json.load()。
json.dump()来存储数字列表:

import json

numbers = [2,3,5,7,11,13,15]

filename = 'number.json'
with open(filename,'w') as file_object:
    json.dump(numbers,file_object)

json.dump()要存储的数据以及可用于存储数据的文件对象。

import json

filename = "number.json"
with open(filename) as file_object:
    numbers = json.load(file_object)
print(numbers)

这次我们以读取方式打开这个文件,因为Python只需读取这个文件。使用函数json.load()加载存储在numbers.json中的信息,并将其存储到变量numbers中

10.4.2 保存和读取用户生成的数据

保存用户姓名

import json

username = input("What's your name?")

filename = "username.json"
with open(filename,'w') as file_object:
    json.dump(username,file_object)

读取用户姓名

import json

filename = 'username.json'

with open(filename) as file_object:
    name = json.load(file_object)
    print(name)
    

注意:一定不要将json.load写成json.loads,会显示错误的

load是对文件进行操作的,因此load的主要参数是打开的文件,调用格式通常为 load(f)

loads是对字符串进行操作的,因此loads的主要参数是字符串,调用格式通常为 load(str)

10.4.3 重构

代码能够正确地运行,但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。
现有原代码:

import json

def greet_user():
    """问候用户"""

    filename = "username.json"
    try:
        with open(filename) as file_object:
            username = json.load(file_object)
    except FileNotFoundError:
        username = input("What's your name?")
        with open(filename,"w") as f_object:
            json.dump(username,f_object)
            print('We remember your name.')
    else:
        print("Welcome back, " + username + "!")
greet_user()
    

重构:

import json

def get_stored_username():
    """如果存储了用户名,就获取它"""

    filename = "username.json"
    try:
        with open(filename) as file_object:
            username = json.load(file_object)
    except FileNotFoundError:
        return None
    else:
        return username

def greet_user():
    
    """问候用户并指出其姓名"""       
    
    username = get_stored_username()
    if username:
        print("Welcome back, " + username + "!")
    else:
        username = input("What's your name?")
        with open(filename,"w") as f_object:
            json.dump(username,f_object)
            print('We remember your name.')
        
greet_user()
    

再次详细重构:

import json

def get_stored_username():
    """如果存储了用户名,就获取它"""

    filename = "username.json"
    try:
        with open(filename) as file_object:
            username = json.load(file_object)
    except FileNotFoundError:
        return None
    else:
        return username
def get_new_username():
    """提示用户输入用户名"""
    username = input("What's your name?")
    with open(filename,"w") as f_object:
            json.dump(username,f_object)
            print('We remember your name.')
            return username

def greet_user():
    
    """问候用户并指出其姓名"""       
    username = get_stored_username()
    if username:
        print("Welcome back, " + username + "!")
    else:
        get_new_username()
                
greet_user()
    

在remember_me.py的这个最终版本中,每个函数都执行单一而清晰的任务。我们调用
greet_user(),它打印一条合适的消息:要么欢迎老用户回来,要么问候新用户。

10.4动手试一试

10-11 喜欢的数字

编写一个程序,提示用户输入他喜欢的数字,并使用json.dump()将这个数字存储到文件中。再编写一个程序,从文件中读取这个值,并打印消息“I know your favorite number! It’s _____.”。

写入

import json

favorite_number = input('Please input your favorite number: ')

filename = 'myfavorite_num.json'
with open(filename,'w') as f_object:
    json.dump(favorite_number,f_object)
    print("I'll remember your favorite number!")

读取

import json

filename = "myfavorite_num.json"
with open(filename) as file_object:
    number = json.load(file_object)
    print("I know your favorite number! It’s "+number+".")

10-12 记住喜欢的数字

将练习 10-11 中的两个程序合而为一。如果存储了用户喜欢的数字,就向用户显示它,否则提示用户输入他喜欢的数字并将其存储到文件中。运行这个程序两次,看看它是否像预期的那样工作。

import json

def get_stored():
    filename = "myfavorite_num.json"
    try:
        with open(filename) as file_object:
            number = json.load(file_object)
    except FileNotFoundError:
        return None
    else:
        return number

def show_number():
    number =get_stored()
    if number:
        print("I know your favorite number! It’s "+number+".")
    else:
        favorite_number = input('Please input your favorite number: ')
        filename = 'myfavorite_num.json'
        with open(filename,'w') as f_object:
            json.dump(favorite_number,f_object)
            print("I'll remember your favorite number!")

show_number()

10-13 验证用户

最后一个 remember_me.py 版本假设用户要么已输入其用户名,要么是首次运行该程序。我们应修改这个程序,以应对这样的情形:当前和最后一次运行该程序的用户并非同一个人。

为此,在 greet_user()中打印欢迎用户回来的消息前,先询问他用户名是否是对的。
如果不对,就调用 get_new_username()让用户输入正确的用户名。
情况一:用户名是否是对的

import json

def get_stored_username():
    """如果存储了用户名,就获取它"""

    filename = "username.json"
    try:
        with open(filename) as file_object:
            username = json.load(file_object)
    except FileNotFoundError:
        return None
    else:
        return username
def get_new_username():
    """提示用户输入用户名"""
    username = input("What's your name?")
    with open(filename,"w") as f_object:
            json.dump(username,f_object)
            print('We remember your name.')
            return username

def greet_user():
    
    """问候用户并指出其姓名"""       
    username = get_stored_username()
    if username:
        check = input("This "+ username +" is your name,Please input 'right' or 'not'?")
        if check == 'right':
            print("Welcome back, " + username + "!")
        else:
            get_new_username()
                
greet_user()
    

情况二:用户名是否是错的

import json

def get_stored_username():
    """如果存储了用户名,就获取它"""

    filename = "username.json"
    try:
        with open(filename) as file_object:
            username = json.load(file_object)
    except FileNotFoundError:
        return None
    else:
        return username
def get_new_username():
    """提示用户输入用户名"""
    username = input("What's your name?")
    filename = "username.json"
    with open(filename,"w") as f_object:
            json.dump(username,f_object)
            print('We will remember your name.')
            return username

def greet_user():
    
    """问候用户并指出其姓名"""       
    username = get_stored_username()
    if username:
        check = input("This "+ username +" is your name,Please input 'right' or 'not'?")
        if check == 'right':
            print("Welcome back, " + username + "!")
        else:
            get_new_username()
                
greet_user()
    

10.5 小结

读取文件:一行一行,整个文件,使用文件内容

写入文件:写入空文件,写入多行

异常:处理异常,报告异常,异常沉默

存储数据:json,数据的写入和读取,重构

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值