文章目录

  • 文件和异常
  • 从文件中读取数据
  • 读取整个文件
  • 文件路径
  • 逐行读取
  • 创建一个包含文件各行内容的列表
  • 使用文件的内容
  • 包含一百万位的大型文件
  • 圆周率值中包含你的生日吗
  • 写入文件
  • 写入空文件
  • 写入多行
  • 附加到文件
  • 异常
  • 处理 ZeroDivisionError异常
  • 使用 try-except 代码块
  • 使用异常避免崩溃
  • else 代码块
  • 处理 FileNotFoundError异常
  • Python无法读取不存在的文件,因此它引发一个异常。
  • 查找的文件可能在其他地方,可使用 try-except 代码块以直观的方式处理。
  • 分析文本
  • 使用多个文件
  • 静默失败
  • 决定报告哪些错误
  • 存储数据
  • 使用 json.dump()和 json.load()
  • 保存和读取用户生成的数据
  • 重构


文件和异常

从文件中读取数据

读取整个文件

要读取文件,需要一个包含几行文本的文件

原始文件pi_digits.txt,新建file_reader.py:

with open('pi_digits.txt') as file_object:
    centents = file_object.read()
print(centents.strip())
3.1415926535 
  8979323846 
  2643383279

文件路径

  1. 将类似于 pi_digits.txt 的文件名传递给函数open()时,Python 将在当前执行的文件(即file_reader.py)所在的目录中查找。
  2. 打开非同一目录文件,可使用相对路径,而该位置时相对于当前运行的程序所在的目录的。
  3. 显示路径文件时,windows系统使用反斜杠(\),但是代码中依然可以使用斜杠(/)。
  4. 还可以将文件在计算机中的准确位置告诉Python,这叫绝对路径。绝对路径比相对路径长,因此将其赋给一个变量,再将该变量传递给open()。
  5. 最简单的做法是,要么将数据文件存储在程序文件所在的目录,要么将其存储在程序文件所在目录下的一个文件夹中。
  6. 如果在文件路径中直接使用反斜杠 “D:\pythonProject\study\little_women.txt”,可对路径中的每个反斜杠都进行转义。如"D:\\pythonProject\\study\\little_women.txt"

逐行读取

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

filename = 'pi_digits.txt'
with open(filename) as file_object:      #默认 mode='r'
    for line in file_object:
        print(line.rstrip())
3.1415926535
  8979323846
  2643383279
with open('pi_digits.txt',mode='r') as f:     
    for line in f:
        print(line.rstrip())
3.1415926535
  8979323846
  2643383279

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

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

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
filename = 'pi_digits.txt'
with open(filename) as file_object:
    lines = file_object.readlines()   
#方法readlines() 从文件中读取每一行,并将其存储在一个列表中;该列表被赋给变量lines。
print(lines)
['3.1415926535 \n', '  8979323846 \n', '  2643383279\n']

使用文件的内容

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

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

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

包含一百万位的大型文件

创建一个包含所有这些数字的字符串,将这个文件传递给它

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(f'{pi_string[:52]}...')
print(len(pi_string))

3.14159265358979323846264338327950288419716939937510...
1000002

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

将生日表示为一个由数字组成的字符串,再检查这个字符串是否包含在 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: ')
if birthday in pi_string:
    print('Your birthday appears in the million digits of pi!')
else:
    print('Your birthday does not appear in the million digits of pi.')
    
Enter your birthday,in the form mmddyy: 100149
Your birthday appears in the million digits of pi!

写入文件

写入空文件

相比于计算机中的其他文件,这个文件没有什么不同。

读取模式 r(默认) , 写入模式 w(存在则先清空再写), 附加模式 a, 读写模式 r+

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

写入多行

函数 write()不会在写入的文本末尾添加换行符,因此写入多行时需要指定换行符

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")
#生成了一个programming.txt,内容如下:
I love programming.
I love creating new games.

附加到文件

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

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")
    
更新后的programming.txt,内容如下:
I love programming.
I love creating new games.
I also love finding meaning in large datasets.  #追加模式下写的
I love creating apps that can run in a browser.  #追加模式下写的

异常

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

异常是使用try-except代码块处理的。

处理 ZeroDivisionError异常

不能用数除以 0。

使用 try-except 代码块

当你认为可能会发生错误时,可编写一个 try-except 代码块来处理可能引发的异常。

如果try代码块中的代码运行起来没有问题,Python将跳过except代码块;如果try代码块中的代码导致了错误,Python将查找与之匹配的except代码块并运行其中的代码。

如果 try-except 代码块后面还有其他代码,程序将接着运行。

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

使用异常避免崩溃

发生错误时,如果程序还有工作尚未完成,妥善地处理错误就尤其重要。

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)

    Give me two numbers, and I'll divide them.
Enter 'q' to quit.

First number: 3
Second number: 0
Traceback (most recent call last):
  File "D:\pythonProject\study\division_calculator.py", line 29, in <module>
    answer = int(first_number) / int(second_number)
ZeroDivisionError: division by zero

else 代码块

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

有一些仅在 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:
        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: 5
Second number: 0
You can't divide by 0!

First number: 4
Second number: 2
2.0

处理 FileNotFoundError异常

Python无法读取不存在的文件,因此它引发一个异常。
filename = 'alice.txt'
with open(filename, encoding='utf-8') as f:
    contents = f.read()
    
raceback (most recent call last):
  File "D:\pythonProject\study\alice.py", line 3, in <module>
    with open(filename, encoding='utf-8') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
查找的文件可能在其他地方,可使用 try-except 代码块以直观的方式处理。
filename = 'alice.txt'

try:
    with open(filename, encoding='utf-8') as f:
        contents = f.read()
except FileNotFoundError:
    print(f"Sorry, the file {filename} does not exist.")
else:
    # Count the approximate number of words in the file.
    words = contents.split()
    num_words = len(words)
    print(f"The file {filename} has about {num_words} words.")
    
Sorry, the file alice.txt does not exist.

分析文本

方法split()以空格为分隔符将字符串拆分成多个部分,并将这些部分都存储到一个列表中,结果是一个报告字符串中所有单词的列表,虽然有些单词可能包含标点。

对整篇小说调用 split(),再计算得到的列表包含多少个元素,从而确定整篇童话大致包含多少个单词。

filename = 'alice.txt'

try:
    with open(filename, encoding='utf-8') as f:
        contents = f.read()
except FileNotFoundError:
    print(f"Sorry, the file {filename} does not exist.")
else:
    # Count the approximate number of words in the file.
    words = contents.split()
    num_words = len(words)
    print(f"The file {filename} has about {num_words} words.")
    
The file alice.txt has about 29465 words.

使用多个文件

编写一个简单的循环,计算要分析的任何文本包含多少个单词。

def count_words(filename):
    """计算一个文件大致包含多少个单词"""
    try:
        with open(filename, encoding='utf-8') as f:
            contents = f.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.")

filename = 'alice.txt'
count_words(filename)
def count_words(filename):
    """计算一个文件大致包含多少个单词"""
    try:
        with open(filename, encoding='utf-8') as f:
            contents = f.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.")

filenames = ['alice.txt','siddhartha.txt','moby_dick.txt','little_women.txt']
for filename in filenames:
    count_words(filename)
    
The file alice.txt has about 29465 words.
Sorry, the file siddhartha.txt does not exist.
The file moby_dick.txt has about 215830 words.
The file little_women.txt has about 189079 words.

静默失败

Python有一个 pass 语句,可用于让 Python 在代码块中什么都不要做。

pass 语句充当占位符,提醒你在程序的某个地方什么都没有做,并且也行以后要在这里做些什么。

def count_words(filename):
    """计算一个文件大致包含多少个单词"""
    try:
        with open(filename, encoding='utf-8') as f:
            contents = f.read()
    except FileNotFoundError:
        pass
    else:
        words = contents.split()
        num_words = len(words)
        print(f"The file {filename} has about {num_words} words.")

filenames = ['alice.txt','siddhartha.txt','moby_dick.txt','little_women.txt']
for filename in filenames:
    count_words(filename)
    
The file alice.txt has about 29465 words.
The file moby_dick.txt has about 215830 words.
The file little_women.txt has about 189079 words.

决定报告哪些错误

凭借经验可判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。

存储数据

模块json将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。

JSON(JavaScript Object Natation) 格式最初是为JavaScript 开发的,但随后成了一种常见格式,被包括Python在内的众多语音采用。

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

这是一种在程序之间共享数据的简单方式

import json

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

filename = 'numbers.json'
with open(filename, 'w') as f:
    json.dump(numbers, f)
import json

filename = 'numbers.json'
with open(filename) as f:
    numbers = json.load(f)
    
print(numbers)

[2, 3, 5, 7, 11, 13]

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

使用 json 保存用户生成的数据

import json
username = input("What is your name? ")
filename = "username.json"    #行3可以与行2互换,不影响
with open(filename,"w") as f:
    json.dump(username,f)
    print(f"We'll remember you when you come back,{username}!")
    
What is your name? Alice
We'll remember you when you come back,Alice!
import json
filename = 'username.json'
with open(filename) as f:
    username = json.load(f)
    print(f"Welcome back, {username}!")
    
Welcome back, Alice!

将以上2个程序合并到一个程序

import json
#如果以前存储了用户名,就加载它
#否则,提示用户输入用户名并存储它
filename = 'username.json'
try:
    with open(filename) as f:
        username = json.load(f)
except:
    username = input("What is your name? ")
    with open(filename, "w") as f:
        json.dump(username,f)
        print(f"We'll remember you when you come back,{username}!")
else:
    print(f"Welcome back, {username}!")

Welcome back, Alice!

重构

代码能够正确地运行,但通过将其划分为一系列完成具体工作的函数,还可以改进,这样的过程叫重构。

重构让代码更清晰、更易于理解、更容易扩展。

调用greet_user(),它打印一条合适的信息:要么欢迎老用户回来,要么问候新用户。

它首先调用get_stored_username(),该函数只负责获取已存储的用户名(如果存储了的话)。

最后调用get_new_username(),该函数只负责获取并存储新用户的用户名。

import json

def get_stored_username():
    """Get stored username if available."""
    """如果存储了用户名,就获取它"""
    filename = 'username.json'
    try:
        with open(filename) as f:
            username = json.load(f)
    except FileNotFoundError:
        return None
    else:
        return username

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

def greet_user():
    """Greet the user by name."""
    """问候用户,并指出其名字"""
    username = get_stored_username()
    if username:
        print(f"Welcome back, {username}!")
    else:
        username = get_new_username()
        print(f"We'll remember you when you come back, {username}!")

greet_user()