当前位置: 首页 > news >正文

Python3完全新手小白的学习手册 10 文件和异常

文章目录

    • 读取文件
      • 读取文件的全部内容
    • 相对路径和绝对路径
    • 访问文件中的各行
    • 使用文件的内容
    • 包含100万位的大型文件
    • 圆周率值中包含你的生日吗?
  • 写入文件
    • 写入一行
    • 写入多行
  • 异常
    • 处理ZeroDivisionError异常
    • 使用try-except代码块
    • else代码块
    • 处理FileNotFoundError异常
    • 分析文本
    • 使用多个文件
    • 静默失败
    • 决定报告哪些错误
  • 存储数据
    • 使用json.dump()和json.load()
    • 保存和读取用户生成的数据
    • 重构
  • 往期
  • 代码仓库

从第一到第十章,我们学习了编写程序的所需的基本技能。

在本章节中,我们将学习如何处理文件,以及如何使用异常处理错误。

读取文件

文本文件可以存储许多数据:天气数据、交通数据、社会经济数据、文学作品等。

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

读取文件的全部内容

我们先准备一个文件,名为pi_digits.txt,内容如下:

3.141592653589793238462643383279

然后我们开始编写第一个读取文件的代码file_reader.py:

from pathlib import Pathpath = Path('pi_digits.txt')
contents = path.read_text()
print(contents)

要使用文件的内容,需要将其路径告知 Python。
路径(path)是文件或文件夹在系统中的准确位置。
Python 提供了 pathlib 模块,能处理各种操作系统中处理文件和目录。

首先中 pathlib 模块导入 Path 类,再使用 Path 对像指向一个文件。
这里创建了一个表示文件 pi_digits.txt 的 Path 对象,并将其赋给了变量 path由于这个文件与当前编写的 .py 文件位于同一个目录中,因此 Path 只需要知道其文件名就能访问它。

创建表示文件 pi_digits.txt 的 Path 对象后,使用 read_text() 方法来读取这个文件的全部内容。
**read_text()**将该文件的全部内容作为一个字符串返回。我们将这个字符串赋给了变量 contents,再将其打印出来。

相比原始原件,输出唯一不同的地方是末尾多了一个空行。
因为 read_text()在到达文件末尾时会返回一个空字符串,而这个空字符串会被显示一个空行。
要删除这个多出来的空行,可对字符串变量contents调用 strip() 方法。

contents = path.read_text().rstrip()

这种方式称为方法链式调用。

博主使用的 3.13版本已经没有了这个问题。

相对路径和绝对路径

指定路径的方式有两种:

  • 相对文件路径让 Python 到相对于当前运行的程序文件所在的目录去查找。
  • 绝对文件路径让 Python 去系统的准确位置去查找。

在相对路径行不通时,可使用绝对路径。

绝对路径通常比相对路径长,因为以系统的根文件夹为起点。
在 Linux 系统中,系统的根文件夹为 /,而在 Windows 系统中,系统的根文件夹为 C:\。


path = Path('/home/eric/data_files/text_files/filename.txt')

在 Windows 系统中,可使用反斜杠(\)而不是斜杠(/)来分隔路径中的文件夹。

访问文件中的各行

使用 splitlines() 方法将冗长的字符串转换为一系列行,再使用 for 循环以每次一行的方式检查文件中的各行

from pathlib import Pathpath = Path('pi_digits.txt')
contents = path.read_text()lines = contents.splitlines()
for line in lines:print(line)

和前面一样,读取文件的全部内容,由于没有修改行,因此输出与文件内容相同。

使用文件的内容

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

首先,创建一个字符串,它包含文件中存储的所有数字。

from pathlib import Pathpath = Path('pi_digits.txt')
contents = path.read_text()lines = contents.splitlines()
# 创建变量 pi_string
pi_string = ''
for line in lines:pi_string += lineprint(pi_string)
print(len(pi_string))

输出如下:

3.1415926535  8979323846  2643383279
32

变量pi_string存储的字符串包含原来位于每行左端的空格。要删除这些空格,可对每行调用lstrip()方法。

for line in lines:pi_string += line.lstrip()

输出如下:

3.141592653589793238462643383279
32

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

包含100万位的大型文件

如果一个文本文件包含精确到小数点后 1 000 000 位而不是 30 位的圆周率值,也可以创建一个包含所有这些数字的字符串。

在这个例子中,我们将使用一个包含 100 000 位的圆周率值的文本文件,该文件的第一行只包含前 50 位。

from pathlib import Pathpath = Path('pi_million_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
for line in lines:pi_string += line.lstrip()print(f"{pi_string[:52]}...")
print(len(pi_string))

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

现在我们知道如何使用 Python 来读取文件,我们来编写一个程序,看看圆周率值中包含你的生日吗?

from pathlib import Pathpath = Path('pi_million_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
for line in lines:pi_string += line.lstrip()
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.")

写入文件

保存数据的最简单的方式之一是将其写入文件。

写入一行

定义一个文件的路径后,就可使用 write_text() 将数据写入该文件了。

from pathlib import Pathpath = Path("programming.txt")
# write_text() 方法接受单个实参,即要写入文件的字符串。
path.write_text("I love programming.\n")

。这个程序没有终端输出,但你如果打开文件 programming.txt。

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

写入多行

write_text() 方法会在幕后完成几项工作。

首先,如果 path 变量对应的路径指向的文件不存在,就创建它。
其次,将字符串写入文件后,它会确保文件得以妥善地关闭。

要将多行写入文件,需要先创建一个字符串(其中包含要写入文件的全部内容),再调用 write_text() 并将这个字符串传递给它。

from pathlib import Path
contents = "I love programming.\n"
contents += "I love creating new games.\n"
contents += "I also love working with data.\n"path = Path('programming.txt')
path.write_text(contents)

在对 path 对象调用 write_text() 方法时,务必谨慎。如果指定的文件已存在, write_text() 将删除其内容,再将指定的文本写入其中。

异常

Python 使用称为异常(exception)的特殊对象来管理程序执行期间发生的错误。

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

处理ZeroDivisionError异常

print(5/0)

输出如下:

Traceback (most recent call last):File "C:\Users\Administrator\PycharmProjects\pythonProject\demo.py", line 1, in <module>print(5/0)  # 报错
ZeroDivisionError: division by zero

在上述 traceback 中,错误 ZeroDivisionError 是个异常对象。
Python 在无法按你的要求做时,就会创建这种对象。

使用try-except代码块

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

输出如下:

You can't divide by zero!

当你认为可能发生了错误时,可编写一个 try 语句来告诉 Python,你要执行的操作可能引发这种错误。

else代码块

只有 try 代码块成功执行才需要继续执行的代码,都应放到 else 代码块中


--snip - -
while True:--snip - -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)

如果除法运算成功,就使用 else 代码块来打印结果

处理FileNotFoundError异常

在使用文件时,一种常见的问题是找不到文件:要查找的文件可能在其他地方,文件名可能不正确,或者这个文件根本就不存在。

from pathlib import Pathpath = Path('alice.txt')
contents = path.read_text(encoding='utf-8')

这里使用 read_text() 的方式与前面稍有不同。如果系统的默认编码与要读取的文件的编码不一致,参数 encoding 必不可少。

Python 无法读取不存在的文件,因此引发了一个异常FileNotFoundError

通常最好从 traceback 的末尾着手。从最后一行可知,引发了异常 FileNotFoundError。

from pathlib import Pathpath = Path('alice.txt')
try:contents = path.read_text(encoding='utf-8')
except FileNotFoundError:print(f"Sorry, the file {path} does not exist.")

这个示例中,try 代码块中的代码引发了 FileNotFoundError 异常,因此要编写一个与该异常匹配的 except 代码块。
这样,当找不到文件时,Python 将运行 except 代码块中的代码,从而显示一条友好的错误消息,而不是 traceback。

分析文本

from pathlib import Pathpath = Path('alice.txt')
try:contents = path.read_text(encoding='utf-8')
except FileNotFoundError:print(f"Sorry, the file {path} does not exist.")
else:# 计算文件大致包含多少个单词words = contents.split()num_words = len(words)print(f"The file {path} has about {num_words} words.")

仅当 try 代码块成功执行时才会执行它们。输出指出了文件 alice.txt 包含多少个单词。

使用多个文件

下面分析几本书

from pathlib import Pathdef count_words(filename):"""计算一个文件大致包含多少个单词。"""try:contents = Path(filename).read_text(encoding='utf-8')except FileNotFoundError:msg = f"Sorry, the file {filename} does not exist."print(msg)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)

先将文件名存储为简单字符串,然后将每个字符串转换为 Path 对象,再调用 count_words()。

在这个示例中,使用 try-except 代码块有两个重要的优点:一是避免用户看到 traceback,二是让程序可以继续分析能够找到的其他文件。

静默失败

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

def count_words(path):"""计算一个文件大致包含多少个单词"""try:--snip--except FileNotFoundError:passelse:--snip--

当这种错误发生时,既不会出现 traceback,也没有任何输出。用户将看到存在的每个文件包含多少个单词,但没有任何迹象表明有一个文件未找到

决定报告哪些错误

只要程序依赖于外部因素,如用户输入、是否存在指定的文件、是否有网络连接,就有可能出现异常。凭借经验可判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。

存储数据

一种简单的方式是使用模块 json 来存储数据。

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

第一个程序将使用 json.dumps() 来存储这组数,而第二个程序将使用 json.loads() 来读取它们。

from pathlib import Path
import jsonnumbers = [2,3,5,7,11,13]path = Path('numbers.json')
contents = json.dumps(numbers)
path.write_text(contents)

首先导入模块 json,并创建一个数值列表。然后选择一个文件名,指定要将该数值列表存储到哪个文件中。
接下来,使用 json.dumps() 函数生成一个字符串,将其存储到变量 contents 中。
最后,使用 Path 对象的 write_text() 方法将这个字符串写入到文件 numbers.json 中。

使用 json.loads() 将这个列表读取到内存中

from pathlib import Path
import jsonpath = Path('numbers.json')
contents = path.read_text()
numbers = json.loads(contents)
print(numbers)

这个数据文件是使用特殊格式的文本文件,因此可使用 read_text() 方法来读取它。然后将这个文件的内容传递给 json.loads()。这个函数将一个 JSON 格式的字符串作为参数,并返回一个 Python 对象(这里是一个列表),而我们将这个对象赋给了变量 numbers。最后,打印恢复的数值列表

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

使用 json 保存用户生成的数据很有必要

from pathlib import Path
import json# 首先,提示用户输入名字
username = input("请输入你的用户名:")# 接下来,将收集到的数据写入文件 username.json
path = Path('username.json')
contents = json.dump(username)
path.write_text(contents)# 然后,打印一条消息,指出存储了用户输入的信息
print(f"我们将记住你的用户名,{username}")

再编写一个程序,向名字已被存储的用户发出问候

from pathlib import Path
import jsonpath = Path('username.json')
contents = path.read_text()
username = json.loads(contents)print(f"欢迎回来,{username}!")

我们读取数据文件的内容,并使用 json.loads() 将恢复的数据赋给变量 userusername

from pathlib import Path
import jsonpath = Path('username.json')
# 如果指定的文件或文件夹存在,exists() 方法返回 True,否则返回 False。
# 这里使用 path.exists() 来确定是否存储了用户名
if path.exists():# 如果文件 username.json 存在,就加载其中的用户名,并向用户发出个性化问候contents = path.read_text()username = json.loads(contents)print(f"欢迎回来,{username}!")
else:# 如果文件 username.json 不存在,就提示用户输入用户名,并存储用户输入的值。username = input("请输入你的用户名:")contents = json.dumps(username)path.write_text(contents)print(f"我们将记住你的用户名,{username}")

重构

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

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

from pathlib import Path
import jsondef greet_user():"""问候用户,并指出其名字。"""path = Path('users.json')if path.exists():contents = path.read_text()username = json.loads(contents)print(f'欢迎回来{username}')else:username = input('请输入你的名字:')contents = json.dumps(username)path.write_text(contents)print(f'我们将记住你的名字{username}')greet_user()

greet_user() 函数所做的不仅是问候用户,还在存储了用户名时获取它,在没有存储用户名时提示用户输入。
下面重构 greet_user(),不让它执行这么多任务。

from pathlib import Path
import json# 如果存储了用户名,就获取并返回它;如果传递给 get_stored_username() 的路径不存在,就返回 None
def get_stored_username(path):"""如果存储了用户名,就获取它"""if path.exists():contents = path.read_text()username = json.loads(contents)return usernameelse:# 这是一种不错的做法:函数要么返回预期的值,要么返回 None。return Nonedef get_stored_username(path):"""提示用户输入用户名"""username = input('请输入你的名字? ')contents= json.dumps(username)path.write_text(contents)return usernamedef greet_user():"""问候用户,并指出其名字"""path = Path('username.json')username = get_stored_username(path)if username:print(f"Welcome back, {username}!")else:username = get_stored_username(path)print(f"We'll remember you when you come back, {username}!")greet_user()

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

首先调用 get_stored_username(),这个函数只负责获取已存储的用户名(如果存储了),再在必要时调用 get_new_username(),这个函数只负责获取并存储新用户的用户名。要编写出清晰且易于维护和扩展的代码,这种划分必不可少。


往期

  • Python3完全新手小白的学习手册 9 类
  • Python3完全新手小白的学习手册 8 函数
  • Python3完全新手小白的学习手册 7 用户输入和while循环
  • Python3完全新手小白的学习手册 6 字典
  • Python3完全新手小白的学习手册 5 if语句
  • Python3完全新手小白的学习手册 4 操作列表
  • Python3完全新手小白的学习手册 3 列表
  • Python3完全新手小白的学习手册 2 变量和简单数据类型
  • Python3完全新手小白的学习手册 1 Python 的安装

代码仓库

代码仓库

http://www.lryc.cn/news/579237.html

相关文章:

  • C++ 完美转发(泛型模板函数)
  • Python训练营Day1
  • Spring生态在Java开发
  • AI:什么是Agent
  • [学习记录] HLSL-编译指示及属性
  • C#上位机串口接口
  • Android Studio使用HTTP代理下载依赖
  • 红黑树:高效平衡的秘密
  • linux中的种子下载方案ED2K BT
  • OpenGL空间站场景实现方案
  • 网络协议传输层UDP协议
  • SpringBoot+Docker+Graylog - 让错误自动报警
  • HCIA-实现VLAN间通信
  • 应用密码学纲要
  • vue中ref()和reactive()区别
  • 智能物流革命:Spring Boot+AI实现最优配送路径规划
  • AI之Tool:Glean的简介、安装和使用方法、案例应用之详细攻略
  • STM32F103_Bootloader程序开发11 - 实现 App 安全跳转至 Bootloader
  • OpenHarmony 5.0 解决点击导航栏切换后台按钮再切换到前台导航栏可能覆盖输入法问题,导致输入法下沉,最下面的显示不全
  • RGB下的色彩变换:用线性代数解构色彩世界
  • Flask 安装使用教程
  • Pillow 安装使用教程
  • IO进程线程 (进程)
  • Rust实现黑客帝国数字雨特效
  • CppCon 2018 学习:Feather: A Modern C++ Web Development Framework
  • FPGA的开发流程
  • 旋转不变子空间( ESPRIT) 算法
  • 基于SpringBoot的场地预定管理系统
  • 新版本没有docker-desktop-data分发 | docker desktop 镜像迁移
  • 当AR遇上深度学习:实时超声肾脏分割与测量技术全解析