自动化测试

第9章-文件和异常

2023-02-27  本文已影响0人  程一川

本章内容:

1、从文件中读取数据

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

(1)读取整个文件

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

pi_digits.txt

3.1415926535
   8979323846
   2643383279

将文件保存在程序所在目录中:


图片.png

file_reader.py

#coding:utf-8
with open('pi_digits.txt') as file_object:
    contents = file_object.read()
    print(contents)

输出结果:
3.1415926535
  8979323846
  2643383279

上面例子中:

#coding:utf-8
with open('pi_digits.txt') as file_object:
    contents = file_object.read()
    print(contents.rstrip())
(2)文件路径

使用相对文件路径打开文件夹中的文件:
在Linux和OS X中:

with  open('text_files/filename.txt') as file_object:

在Windows系统中,使用反斜杠(\)而不是斜杠(/):

with  open('text_files\filename.txt')  as  file_object:

使用绝地文件路径打开文件夹中的文件:
在Linux和OS X中:

file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:

在Windows系统中:

file_path = 'C:\python_study\text_files\filename.txt'
with  open(file_path) as  file_object:

注意:
如果你使用的是Windows系统,且结果不符合预期,请确保在文件路径中使用的是反斜杠。另外,由于反斜杠在python中被视为转义标记,为在Windows中确保万无一失,应以原始字符串的方式指定路径,即在开头的单引号前加上r。

(3)逐行读取

读取文件时,常常需要检查其中的每一行:你可能要在文件中查找特定的信息,或者要以某种方式修改文件中的文本。
要以每次一行的方式检查文件,可对文件对象使用for循环:

#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
    for line in file_object:
        print(line)

输出结果:
3.1415926535

  8979323846

  2643383279

以上例子中:

#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
    for line in file_object:
        print(line.rstrip())

输出结果:
3.1415926535
  8979323846
  2643383279
(4)创建一个包含文件各行内容的列表

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

#coding:utf-8
filename = 'pi_digits.txt'
with open(filename) as file_object:
    lines = file_object.readlines()
    
for line in lines:
    print(line.rstrip())

输出结果:
3.1415926535
  8979323846
  2643383279

以上代码中,方法readlines()从文件中读取每一行,并将其存储再一个列表中;接下来,该列表被存储到变量lines中,在with代码块外,我们仍然可以使用这个变量。

(5)使用文件的内容

将文件读取到内存中后,就可以以任何方式使用这些数据了:

#coding:utf-8
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))

输出结果:
3.1415926535  8979323846  2643383279
36

在变量pi_string存储的字符串中,包含原来位于每行左边的空格,为删除这些空格,可使用strip()而不是rstrip():

#coding:utf-8
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)
print(len(pi_string))

输出结果:
3.141592653589793238462643383279
32

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

(6)包含一百万位的大型文件
#coding:utf-8
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[:52] + "...")
print(len(pi_string))

输出结果:
3.14159265358979323846264338327912345678952356565623...
36177008

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

(7)圆周率中包含你的生日吗
#coding:utf-8
filename = 'pi_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:")
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.")

输出结果:
Enter your birthday,in the form mmddyy:910213
Your birthday appears in the first million digits of pi!

2、写入文件

保存数据的最简单的方式之一就是将其写入到文件中。通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在:你可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可编写程序来将这些输出读取到内存中并进行处理。

(1)写入空文件

要将文本写入文件,你在调用open()时需要提供另一个实参,告诉python你要写入打开的文件:

#coding:utf-8
filename = 'programming.txt'
with open(filename,'w') as file_object:
    file_object.write("I love programming.")

运行结果:


图片.png

以上示例中:

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

(2)写入多行

函数write()不会在你写入的文本末尾添加换行符:

#coding:utf-8
filename = 'programming.txt'
with open(filename,'w') as file_object:
    file_object.write("I love programming.")
    file_object.write("I love creating new games.")

运行结果:


图片.png

要让每个字符串都单独占一行,需要在write()语句中包含换行符:

#coding:utf-8
filename = 'programming.txt'
with open(filename,'w') as file_object:
    file_object.write("I love programming.\n")
    file_object.write("I love creating new games.\n")

运行结果:


图片.png
(3)附加到文件

如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件:

#coding:utf-8
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")  

运行结果:


图片.png

最终运行的结果是,文件原来的内容还在,他们后面是我们刚添加的内容。

3、异常

python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理异常的代码,程序将继续运行,如果未对异常进程处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
异常是使用try-except代码块处理的。try-except代码块让python执行指定的操作,同时告诉python发生异常时怎么办。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误信息,而不是令用户迷惑的traceback。

(1)处理ZeroDivisionError异常

下面来看一种导致python引发异常的简单错误:


图片.png
(2)使用try-except代码块
#coding:utf-8
try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")

输出结果:
You can't divide by zero!

上述代码中:

(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的除法运算时,它将崩溃:

d:\>python test.py
Give me two numbers,and I'll divide them.
Enter 'q' to quit.

First number:2
Second number:0
Traceback (most recent call last):
  File "test.py", line 12, in <module>
    answer = int(first_number)/int(second_number)
ZeroDivisionError: division by zero
(4)else代码块

通过将可能引发错误的代码放在try-except代码块中,可提高这个程序抵御错误的能力:

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:
        answer = int(first_number)/int(second_number)
    except ZeroDivisionError:
        print("You can't divide by 0!")
    else:
        print(answer)

运行结果:
Give me two numbers,and I'll divide them.
Enter 'q' to quit.

First number:2
Second number:0
You can't divide by 0!

First number:5
Second number:1
5.0

try-except-else代码块的工作原理大致如下:python尝试执行try代码块中的代码;只有可能引发异常的代码才需要放在try语句中。有时候,有一些仅在try代码块成功执行时才需要运行的代码;这些代码应放在else代码块中。except代码块告诉python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。

(5)处理FileNotFoundError异常

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

filename = 'alice.txt'

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

运行时,python无法读取不存在的文件,会发生下面的错误:

d:\>python test.py
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    with open(filename) as f_obj:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'

上面示例中,这个错误是函数open()导致的,所以将try语句放在包含open()的代码行之前:

filename = 'alice.txt'

try:
    with open(filename) as f_obj:
        contents = f_obj.read()
except FileNotFoundError:
    msg = "Sorry,the file " + filename + " doesn't exist."
    print(msg)

运行结果:
d:\>python test.py
Sorry,the file alice.txt doesn't exist.
(6)分析文本

下面来提取通话Alice in Wonderland的文本,并尝试计算它包含多少个单词。我们使用方法split(),它根据一个字符串创建一个单词列表:


图片.png

方法split()以空格为分隔符将字符串分拆成多个部分,并将这些部分都存储到一个列表中。
现将alice.txt文件放在正确的目录下:


图片.png
filename = 'alice.txt'

try:
    with open(filename) as f_obj:
        contents = f_obj.read()
except FileNotFoundError:
    msg = "Sorry,the file " + filename + " doesn't exist."
    print(msg)
else:
        #计算文件大致包含几个单词
    words = contents.split()
    print(words)
    num_words = len(words)
    print("The file " + filename +" has about " +str(num_words) +" words.")
运行结果:
d:\>python test.py
['Study', 'hard,', 'and', 'make', 'prograss', 'everyday!']
The file alice.txt has about 6 words.
(7)使用多个文件
图片.png
#coding:utf-8
def count_words(filename):
    """计算一个文件大致包含几个多少个单词"""
    try:
        with open(filename) as f_obj:
            contents = f_obj.read()
    except FileNotFoundError:
        msg = "Sorry,the file " + filename + " doesn't exist."
        print(msg)
    else:
        #计算文件大致包含几个单词
        words = contents.split()
        print(words)
        num_words = len(words)
        print("The file " + filename +" has about " +str(num_words) +" words.")

filenames = ['alice.txt','siddhartha.txt','moby_dick.txt','little_women.txt']
for filename in filenames:
    count_words(filename)
输出结果:
d:\>python test.py
['Study', 'hard,', 'and', 'make', 'prograss', 'everyday!']
The file alice.txt has about 6 words.
['Gautama', 'Buddha', '(c.', '563', 'BCE/480', 'BCE', '–', 'c.', '483', 'BCE/400', 'BCE),', 'also', 'known', 'as', 'Siddhārtha', 'Gautama', '[sid???ɑ?rt???', 'g?ut??m?],', 'Shakyamuni', 'Buddha', '[?ɑ?kj?mun?i', 'bud????],[4]', 'or', 'simply', 'the', 'Buddha,', 'after', 'the', 'title', 'of', 'Buddha,', 'was', 'an', 'ascetic', '(?rama?a)', 'and', 'sage,[4]', 'on', 'whose', 'teachings', 'Buddhism', 'was', 'founded.[5]', 'He', 'is', 'believed', 'to', 'have', 'lived', 'and', 'taught', 'mostly', 'in', 'the', 'eastern', 'part', 'of', 'ancient', 'India', 'sometime', 'between', 'the', 'sixth', 'and', 'fourth', 'centuries', 'BCE.[6][note', '3]', 'Gautama', 'taught', 'a', 'Middle', 'Way', 'between', 'sensual', 'indulgence', 'and', 'the', 'severe', 'asceticism', 'found', 'in', 'the', '?rama?a', 'movement[7]', 'common', 'in', 'his', 'region.', 'He', 'later', 'taught', 'throughout', 'other', 'regions', 'of', 'eastern', 'India', 'such', 'as', 'Magadha', 'and', 'Kosala.[6][8]', 'Gautama', 'is', 'the', 'primary', 'figure', 'in', 'Buddhism.', 'He', 'is', 'recognized', 'by', 'Buddhists', 'as', 'an', 'enlightened', 'teacher', 'who', 'attained', 'full', 'Buddhahood,', 'and', 'shared', 'his', 'insights', 'to', 'help', 'sentient', 'beings', 'end', 'rebirth', 'and', 'suffering.', 'Accounts', 'of', 'his', 'life,', 'discourses,', 'and', 'monastic', 'rules', 'are', 'believed', 'by', 'Buddhists', 'to', 'have', 'been', 'summarized', 'after', 'his', 'death', 'and', 'memorized', 'by', 'his', 'followers.', 'Various', 'collections', 'of', 'teachings', 'attributed', 'to', 'him', 'were', 'passed', 'down', 'by', 'oral', 'tradition', 'and', 'first', 'committed', 'to', 'writing', 'about', '400', 'years', 'later.']
The file siddhartha.txt has about 181 words.
Sorry,the file moby_dick.txt doesn't exist.
[]
The file little_women.txt has about 0 words.
(8)失败时一声不吭

在上面示例中,我们告诉用户有一个文件找不到。但并非每次捕获到异常时都需要告诉用户,有时候你希望程序在发生异常时一声不吭,就像什么都没有发生一样继续运行。python有一个pass语句,可在代码块中使用它来让python什么都不做:

#coding:utf-8
def count_words(filename):
    """计算一个文件大致包含几个多少个单词"""
    try:
        with open(filename) as f_obj:
            contents = f_obj.read()
    except FileNotFoundError:
        pass
    else:
        #计算文件大致包含几个单词
        words = contents.split()
        print(words)
        num_words = len(words)
        print("The file " + filename +" has about " +str(num_words) +" words.")

filenames = ['alice.txt','siddhartha.txt','moby_dick.txt','little_women.txt']
for filename in filenames:
    count_words(filename)

运行结果:
d:\>python test.py
['Study', 'hard,', 'and', 'make', 'prograss', 'everyday!']
The file alice.txt has about 6 words.
['Gautama', 'Buddha', '(c.', '563', 'BCE/480', 'BCE', '–', 'c.', '483', 'BCE/400', 'BCE),', 'also', 'known', 'as', 'Siddhārtha', 'Gautama', '[sid???ɑ?rt???', 'g?ut??m?],', 'Shakyamuni', 'Buddha', '[?ɑ?kj?mun?i', 'bud????],[4]', 'or', 'simply', 'the', 'Buddha,', 'after', 'the', 'title', 'of', 'Buddha,', 'was', 'an', 'ascetic', '(?rama?a)', 'and', 'sage,[4]', 'on', 'whose', 'teachings', 'Buddhism', 'was', 'founded.[5]', 'He', 'is', 'believed', 'to', 'have', 'lived', 'and', 'taught', 'mostly', 'in', 'the', 'eastern', 'part', 'of', 'ancient', 'India', 'sometime', 'between', 'the', 'sixth', 'and', 'fourth', 'centuries', 'BCE.[6][note', '3]', 'Gautama', 'taught', 'a', 'Middle', 'Way', 'between', 'sensual', 'indulgence', 'and', 'the', 'severe', 'asceticism', 'found', 'in', 'the', '?rama?a', 'movement[7]', 'common', 'in', 'his', 'region.', 'He', 'later', 'taught', 'throughout', 'other', 'regions', 'of', 'eastern', 'India', 'such', 'as', 'Magadha', 'and', 'Kosala.[6][8]', 'Gautama', 'is', 'the', 'primary', 'figure', 'in', 'Buddhism.', 'He', 'is', 'recognized', 'by', 'Buddhists', 'as', 'an', 'enlightened', 'teacher', 'who', 'attained', 'full', 'Buddhahood,', 'and', 'shared', 'his', 'insights', 'to', 'help', 'sentient', 'beings', 'end', 'rebirth', 'and', 'suffering.', 'Accounts', 'of', 'his', 'life,', 'discourses,', 'and', 'monastic', 'rules', 'are', 'believed', 'by', 'Buddhists', 'to', 'have', 'been', 'summarized', 'after', 'his', 'death', 'and', 'memorized', 'by', 'his', 'followers.', 'Various', 'collections', 'of', 'teachings', 'attributed', 'to', 'him', 'were', 'passed', 'down', 'by', 'oral', 'tradition', 'and', 'first', 'committed', 'to', 'writing', 'about', '400', 'years', 'later.']
The file siddhartha.txt has about 181 words.
[]
The file little_women.txt has about 0 words.

4、存储数据

模块json()让你能够将简单的python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。

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

我们来编写一个存储一组数字的简短程序,再编写一个将这些数字读取到内存中的程序。第一个程序将使用json.dump()来存储这组数字,第二个程序将使用json.load()。
函数json.dump()接受两个实参:要存储的数据以及可用于存储数据的文件对象:

import json
numbers = [2,3,5,7,11,13]
filename = 'numbers.json'
with open(filename,'w') as f_obj:
    json.dump(numbers,f_obj)

以上程序中:

import json
filename = 'numbers.json'
with open(filename) as f_obj:
    numbers = json.load(f_obj)
print(numbers)
输出结果:
D:\>python test_01.py
[2, 3, 5, 7, 11, 13]
图片.png
(2)保存和读取用户生成的数据

保存数据:

import json
username = input("What is your name?")
filename = 'username.json'
with open(filename,'w') as f_obj:
    json.dump(username,f_obj)
    print("We'll remember you when you come back, " + username + "!")

运行结果:
D:\>python test.py
What is your name?chuanchuan
We'll remember you when you come back, chuanchuan!
图片.png
读取数据:
import json
filename = 'username.json'
with open(filename) as f_obj:
    username = json.load(f_obj)
    print("Welcome back, " + username + "!")
运行结果:
D:\>python test_01.py
Welcome back, chuanchuan!
图片.png
下面将存储和读取过程合在一处:
#coding:utf-8
import json
#如果以前存储了用户名,就加载它,否则,就提示用户输入用户名并存储它
filename = 'username_01.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'll remember you when you come back, " + username + "!")
else:
    print("Welcome back, " + username + "!")

运行结果:
D:\>python test.py
What is your name?yichuan
We'll remember you when you come back, yichuan!

D:\>python test.py
Welcome back, yichuan!
图片.png
(3)重构

经常会遇到这样的情况:代码能够正确的运行,但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。

#coding:utf-8
import json
def greet_user():
    """问候用户,并指出其名字"""
    filename = 'username_02.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'll remember you when you come back, " + username + "!")
    else:
        print("Welcome back, " + username + "!")

greet_user()

运行结果:
D:\>python test.py
What is your name?yisan
We'll remember you when you come back, yisan!

D:\>python test.py
Welcome back, yisan!
图片.png
下面来重构上述函数:
#coding:utf-8
import json

def get_stored_username():
    """如果存储了用户名,就获取他"""
    filename = 'username_03.json'
    try:
        with open(filename) as f_obj:
            username = json.load(f_obj)
    except FileNotFoundError:
        return None
    else:
        return username

def greet_user():
    """问候用户,并指出其名字"""
    username = get_stored_username()
    if username:
        print("Welcome back, " + username + "!")
    else:
        username = input("What is your name?")
        filename = "username_03.json"
        with open(filename,'w') as f_obj:
            json.dump(username,f_obj)
            print("We'll remember you when you come back, " + username + "!")

greet_user()

运行结果:
D:\>python test.py
What is your name?chengchuanchuan
We'll remember you when you come back, chengchuanchuan!

D:\>python test.py
Welcome back, chengchuanchuan!
图片.png

我们还可以将函数greet_user()中的另一个代码块提取出来:将没有存储用户名时提示用户名输入的代码放在一个独立的函数中:

#coding:utf-8
import json

def get_stored_username():
    """如果存储了用户名,就获取他"""
    filename = 'username_04.json'
    try:
        with open(filename) as f_obj:
            username = json.load(f_obj)
    except FileNotFoundError:
        return None
    else:
        return username

def get_new_username():
    """提示用户输入用户名"""
    username = input("What is your name?")
    filename = "username_04.json"
    with open(filename,'w') as f_obj:
        json.dump(username,f_obj)
    return username

def greet_user():
    """问候用户,并指出其名字"""
    username = get_stored_username()
    if username:
        print("Welcome back, " + username + "!")
    else:
        username = get_new_username()
        print("We'll remember you when you come back, " + username + "!")

greet_user()

运行结果:
D:\>python test.py
What is your name?jack
We'll remember you when you come back, jack!

D:\>python test.py
Welcome back, jack!
图片.png
上一篇 下一篇

猜你喜欢

热点阅读