读取文件
读取文件的全部内容
from pathlib import Path path = Path('pi_digits.txt') contents = path.read_text() print(contents)
要使用文件的内容,需要将其路径告诉python,路径指的是在文件或文件夹在系统中的准确位置。Python提供了pathlib模块,让你能够更轻松地在各种系统中处理文件和目录
上述代码先从pathlib模块中导入Path类,Path对象指向一个文件,可用来做很多事情,例如让你在使用文件前核实它是否存在,读取文件的内容,以及将新数据写入文件。
这里创建了一个表示文件pi_digits的Path对象,并将其赋给了变量path。由于这个文件与当前编写的.py文件在同一个目录中,因此Path只需要知道文件名就能访问它
注意:VS Code会在最近打开的文件夹中查找文件,因此如果你使用的是VS Code,请先打开本章程序所在的文件夹。
read_text()将该文件的全部内容作为一个字符串返回。
相比于原文件,该输出唯一不同的地方是末尾多了一个空行;因为read_text()在到达文件末尾时会返回一个空字符串,而这个空字符串会被显示为一个空行。
要删除这个多出来的空行,可以对字符串变脸调用rstrip()
from pathlib import Path path = Path('pi_digits.txt') contents = path.read_text().rstrip() print(contents)
也可以采用方法链式调用
contents = path.read_text().rstrip()
相对文件路径和绝对文件路径
当类似于pi_digits.txt这样简单文件名传递给Path时,Python将在当前执行的文件所在的目录中去查找。
根据你组织文件的方式,有时候可能打不开在程序文件所属目录中的文件。
例如:你可能将程序文件存储在了文件夹python_work中,并且在文件夹python_work中创建了一个名为text_files的文件夹,用于存储程序文件要操作的文本文件。虽然文件夹text_files在文件夹python_work中,但仅想Path传递文件夹text_files中的文件名称也是不行的,因为Python旨在文件夹python_work中查找,而不会在其子文件text_files中查找。
要让Python打开不与程序文件位于同一个目录中的文件,需要提供正确的路径。
在编程中,指定路径的方法有两种,首先,相对文件路径让Python到相对于当前运行的程序所在目录的指定位置去查找,由于文件夹text_files位于文件夹python_work中,因此需要创建一个以text_files打头并以文件名结尾的路径,如下所示:
path = Path('text_files/filename.txt)
其次,可以将文件在计算机中的准确位置告诉Python,这样就不用管当前运行的程序存储在什么地方了,这称为绝对文件路径。加入text_files并不在文件夹python_work中,则上述的相对路径是行不通的。
绝对文件路径通常比相对路径长,因为它们以系统的根文件夹为起点:
path = Path('/home/eric/data_files/text_files/filename.txt)
使用绝对路径,可以读取系统中任何地方的文件。
在显示文件路径时,Windows系统使用反斜杠(\)而不是斜杠(/)。但是你在代码中应该始终使用斜杠。
访问文件中的各行
from pathlib import Path path = Path('pi_digits.txt') contents = path.read_text().rstrip() lines = contents.splitlines() for line in lines: print(line)
可以使用splitlines()方法将冗长的字符串转换为一系列行,再使用for循环以每次一行的方式检查文件中的各行。该方法把冗长字符串转换为一个列表,每一行的内容作为列表中的元素
使用文件的内容
from pathlib import Path path = Path('pi_digits.txt') contents = path.read_text() lines = contents.splitlines() pi_string = '' for line in lines: pi_string += line.lstrip() print(pi_string) print(len(pi_string))
lstrip()删除掉原来位于每行左端的空格
tip:在读取文本文件时,Python将其中的所有文本都解释为字符串。如果读取的是数,并且要将其作为数值使用,就必须使用int()函数将其转换为整数,或者使用float()函数将其转换为浮点数。
包含100万位的大型文件
相关数据文件从网上下载
只打印到小数点后50位
from pathlib import Path path = Path('pi_million_digits.txt') contents = path.read_text() lines = contents.splitlines() pi_spring = '' for line in lines: pi_spring += line.lstrip() print(f"{pi_spring[:52]}...") print(len(pi_spring))
圆周率中包含你的生日吗:
from pathlib import Path
path = Path('pi_million_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_spring = ''
for line in lines:
pi_spring += line.lstrip()
birthday = input("Enter your birthday, in the form mmddyy:")
if birthday in pi_spring:
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.")
练习
练习10.1
from pathlib import Path path = Path('learning_python.txt') contents = path.read_text() print(contents) lines = contents.splitlines() print(lines[0]) for line in lines: print(line)
练习10.2
可以使用replace()方法将字符串中的特定单词替换为另一个单词
from pathlib import Path path = Path('learning_python.txt') contents = path.read_text() print(contents) lines = contents.splitlines() print(lines[0]) for line in lines: print(line.replace('Python', 'C')) print(line)
replace()方法并不会改变原本,而是创造一个副本
练习10.3
简化代码,尽量删减临时变量,如上述代码中的lines
写入文件
写入一行
定义一个文件的路径后,就可以使用write_text()将数据写入该文件里
from pathlib import Path path = Path('learning_python.txt') path.write_text('ssss')
注意,write_text()会将原有的内容全部覆盖
Python只能将字符串写入文本文件,如果要将数值数据存储到文本文件中,必须使用函数str()将其转换为字符串模式。
写入多行
write_text()在幕后完成的工作有:
①如果path变量对应的路径指向的文件不存在,就创建它
②将字符串写入文件后,它会确保文件得以妥善地关闭。如果没有妥善地关闭文件,可能会导致数据丢失或受损。
from pathlib import Path path = Path('ceshi.txt') contents = 'I love programming.\n' contents += 'I love creating new games.\n' contents += 'I also love working with data.\n' path.write_text(contents)
该输入方式对字符串的长度没有任何限制
注意:当对path对象调用write_text()方法时,务必谨慎,如果指定的文件已存在,write_text()将删除其内容。
练习
练习10.4
from pathlib import Path path = Path('tourist.txt') name = input('请输入你的名字:\n') path.write_text(name)
练习10.5
from pathlib import Path path = Path('guest_book.txt') book = '' while True: name = input('请输入你的名字:\n') book += f"{name}\n" tip = input('是否继续输入信息:yes/no\n') if tip == 'no': break path.write_text(book)
异常
处理ZzeroDivisionError异常
print(5/0)
Traceback (most recent call last):
File "C:\Users\xjj\Desktop\python_work\main_idea.py", line 1, in <module>
print(5/0)
~^~
ZeroDivisionError: division by zero
Python无法按照你的要求做的时候,就会创建这种对象
使用try-except代码块
try: print(5/0) except ZeroDivisionError: print("You can't divide by zero")
如果try代码块中的代码导致错误,Python将查找与之匹配的except代码块并运行其中的代码,这样用户看到的是一条友好的错误消息,而不是traceback
如果try-except代码块后面还有其他代码,程序将继续运行,因为Python已经知道了如何处理错误。
使用异常避免崩溃
程序崩溃的话,不懂技术的用户会感到糊涂,怀有恶意的用户还能通过traceback获悉你不想让他们知道的信息,从而发起攻击。
else代码块
通过将可能引发错误的代码放在try-except代码块中,可提高程序低于错误的能力,下面的实例还包含一个else代码块,只有try代码成功执行才需要继续执行的代码,都应放到else代码块中:
print("Give me tow 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 by 0!") else: print(answer)
这样的话如果出现异常,程序还是会继续运行,而用户根本看不到traceback
只有可能引发异常的代码才需要放在try语句中,有时候,有一些仅在try代码成功执行时才需要运行的代码,这些代码应该放在else代码块中,except代码块告诉Python,如果在尝试运行try代码块中的代码时引发了指定的异常该怎么办。
通过预测可能发生错误的代码,可编写稳健的程序,它们即便面临无效数据或缺少资源,也能继续运行,不受无意的用户错误和恶意攻击的影响。
处理FileNotFound异常
在使用文件时,一种常见的问题是找不到文件:要查找的文件可能在其他地方,文件名可能不正确,或者这个文件根本的就不存在。对于所有这些情况,都可使用try-except代码块来处理。
我们来尝试读取一个不存在的文件
from pathlib import Path path = Path('alice.txt') contents = path.read_text(encoding='utf-8')
如果系统的默认编码与要读取的文件的编码不一致,参数encoding必不可少。如果要读取的文件不是在你的系统中创建的,这种情况更容易发生。
引发异常:
Traceback (most recent call last):
File "C:\Users\xjj\Desktop\python_work\main_idea.py", line 4, in <module>
contents = path.read_text(encoding='utf-8')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\xjj\AppData\Local\Programs\Python\Python311\Lib\pathlib.py", line 1056, in read_text
with self.open(mode='r', encoding=encoding, errors=errors) as f:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\xjj\AppData\Local\Programs\Python\Python311\Lib\pathlib.py", line 1042, in open
return io.open(self, mode, buffering, encoding, errors, newline)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
蓝色标记不封是我们获取信息的主要部分。
为了处理这个异常,我们可以将traceback指出的问题的代码行放到try代码块中
from pathlib import Path path = Path('alice.txt') try: contents = path.read_text(encoding='utf-8') except FileNotFoundError: print(f"Sorry, the file {path} does not exist.")
分析文本
from pathlib import Path path = 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")
使用split()方法,它默认以空白为分隔符将字符串拆分为多个部分,生成一个列表。
使用多个文件
from pathlib import Path def count_words(path): """计算一个文件大致包含多少个单词""" 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") filenames = ['alice.txt', 'siddartha.txt', 'moby_dick.txt', 'little_women.txt'] for filename in filenames: path = Path('filename') count_words(path)
在这里。try-except能够避免用户看到traceback,也能让程序继续可以分析找到其他文件。
静默失败
在上面的示例中,我们告诉用户有一个文件找不到,但并非每次捕获异常都需要告诉用户。
有时候希望程序在发生异常时保持静默,就像什么都没有发生一样继续运行。要让程序静默失败,可以像通常那样编写try代码块,但在except中明确告诉Python什么都不要做,Python有一个pas语句,可在代码块中使用它来让Python什么都不做:
try:
except FileNotFoundReeor:
pass
else:
pass语句还充当了占位符,提醒你在程序的某个地方什么都没做,而且以后也许要在这里做些什么。
决定报告哪些错误
如果用户知道要分析哪些文件,他们可能希望在有文件违背分析时出现一条信息来告知原因。
如果用户只想看到结果,并不知道要分析哪些文件,可能就无须在有些文件不存在时告知他们。
向用户显示他们不想看到的信息可能会降低程序的可用性。
Python的错误处理结构让你能够细致地控制与用户共享错误信息的程度,要共享多少信息由你决定
练习
练习10.6
from pathlib import Path print("请输入两个数,程序会将这两个数相加") try: first_number = int(input("请输入第一个数\n")) except ValueError: print("请输入整数!") else: try: second_number = int(input("请输入第二个数\n")) except ValueError: print("请输入整数!") else: number = first_number+second_number print(number)
练习10.7
from pathlib import Path print("请输入两个数,程序会将这两个数相加") while True: try: first_number = int(input("请输入第一个数\n")) except ValueError: print("请输入整数!\n") else: try: second_number = int(input("请输入第二个数\n")) except ValueError: print("请输入整数!\n") else: number = first_number + second_number print(number)
练习10.8
from pathlib import Path path1 = Path('cat.txt') try: print(path1.read_text()) except FileNotFoundError: print("该文件不存在") path2 = Path('dog.txt') try: print(path2.read_text()) except FileNotFoundError: print("该文件不存在")
练习10.9
from pathlib import Path path1 = Path('cat.txt') try: print(path1.read_text()) except FileNotFoundError: print("该文件不存在") path2 = Path('dog.txt') try: pass except FileNotFoundError: print("该文件不存在")
练习10.10
from pathlib import Path
path = Path('alice.txt')
try:
content = path.read_text('utf-8')
except FileNotFoundError:
print("该文件不存在")
else:
print(content.lower().count('the'))
存储数据
一种简单的方式是使用模块json来存储数据,json模块让你能够将简单的Ptython数据结构转换为JSON格式的字符串。JSON格式最初是为JavaScript开发的,但随后成了一种通用的格式,被包括Python在内的众多语言采用。
使用json.dumps()和json.loads()
from pathlib import Path import json numbers = [1, 6, 4, 3, 10, 19] path = Path('numbers.json') content = json.dumps(numbers) path.write_text(content)
首先导入模块json,并创建一个数值列表,然后选择一个文件名,指定要将该数值列表存储到哪个文件,通常使用文件扩展名.json来指出文件存储的数据为JSON格式,接下来使用json.dumps()函数生成一个字符串,它包含我们要存储的数据的JSON表示形式,生成这个字符串后,使用write_text()方法将其写入文件。
使用json.loads()将这个列表读取到文件中。
from pathlib import Path import json path = Path('numbers.json') content = path.read_text() numbers = json.loads(content) print(numbers)
这是一种在程序之间共享数据的简单方式。
保存和读取用户生成的数据
使用json保存用户生成的数据很有必要,因为如果不以某种方式进行存储,用户的信息就会在程序停止运行时丢失
from pathlib import Path import json user_name = input('What is your name?') path = Path('username.json') contents = json.dumps(user_name) path.write_text(contents) print(f"we'll remember you when you are back, {user_name}")
现在再编写一个程序,向名字已被存储的用户发出问候
from pathlib import Path import json path = Path('username.json') contents = path.read_text() user_name = json.loads(contents) print(f"welcome back, {user_name}")
接下来将两个程序合并到一个程序中,在这个程序运行时,将尝试从内存中获取用户的用户名,如果没有找到,就提示用户输入用户名,并将其存储到文件user.name.json中,以供下次使用,这里原本可以编写一个try-except代码块,以便在文件username.json不存在时采取合适的措施,但我们用了pathlib模块提供的一个便利方法——exists():
from pathlib import Path import json path = Path('username.json') if path.exists(): contents = path.read_text() user_name = json.loads(contents) print(f"welcome back, {user_name}") else: user_name = input('What is your name?') contents = json.dumps(user_name) path.write_text(contents) print(f"we'll remember you when you are back, {user_name}")
如果指定的文件或文件夹存在,exists()方法返回True,否则返回False。
重构
虽然代码能够正确运行,但是还可以将其划分为一系列完成具体工作的函数来进行改进,这样的过程称为重构,重构让代码更清晰、更易于理解、更容易扩展。
from pathlib import Path import json def greet_user(): """问候用户,并指出其名字""" path = Path('username.json') if path.exists(): contents = path.read_text() user_name = json.loads(contents) print(f"welcome back, {user_name}") else: user_name = input('What is your name?') contents = json.dumps(user_name) path.write_text(contents) print(f"we'll remember you when you are back, {user_name}") greet_user()
下面进行重构
from pathlib import Path import json def get_sorted_username(path): """如果存储了用户名,就获取它""" if path.exists(): contents = path.read_text() user_name = json.loads(contents) return user_name else: return None def greet_user(): """问候用户,并指出其名字""" path = Path('username.json') username = get_sorted_username(path) if username: print(f"welcome back, {username}") else: user_name = input('What is your name?') contents = json.dumps(user_name) path.write_text(contents) print(f"we'll remember you when you are back, {user_name}")
还可以对对greet_user()进一步重构:
from pathlib import Path import json def get_sorted_username(path): """如果存储了用户名,就获取它""" if path.exists(): contents = path.read_text() user_name = json.loads(contents) return user_name else: return None def greet_new_username(path): """提示用户输入用户名""" user_name = input('What is your name?') contents = json.dumps(user_name) path.write_text(contents) return user_name def greet_user(): """问候用户,并指出其名字""" path = Path('username.json') username = get_sorted_username(path) if username: print(f"welcome back, {username}") else: username = greet_new_username(path) print(f"we'll remember you when you are back, {username}") greet_user()
在这个最终版本中,每个函数都执行单一而清晰的任务。
练习
练习10.11
①
from pathlib import Path import json number = input('请输入你最喜欢的数字\n') path = Path('favorite_number.json') contents = json.dumps(number) path.write_text(contents) print(f"I've remember your favorite number is {number}")
②
from pathlib import Path import json path = Path('favorite_number.json') read_number = path.read_text() contents = json.loads(read_number) print(f"your favorite number is {contents}")
练习10.12
from pathlib import Path import json path = Path('favorite_number.json') if path.exists(): read_number = path.read_text() contents = json.loads(read_number) print(f"your favorite number is {contents}") else: number = input('请输入你最喜欢的数字\n') contents = json.dumps(number) path.write_text(contents) print(f"I've remember your favorite number is {number}")
练习10.13
from pathlib import Path import json favorite_info = {} path = Path('favorite_info.json') if path.exists(): read_info = path.read_text() contents = json.loads(read_info) print(f"your favorite things include {contents}") else: number = input('请输入你最喜欢的数字\n') fruit = input('请输入你最喜欢的水果\n') vegetable = input('请输入你最喜欢的蔬菜\n') favorite_info['number'] = number favorite_info['fruit'] = fruit favorite_info['vegetable'] = vegetable contents = json.dumps(favorite_info) path.write_text(contents) print(f"I've remember your favorite things include {favorite_info}")
练习10.14
from pathlib import Path import json def get_sorted_username(path): """如果存储了用户名,就获取它""" if path.exists(): contents = path.read_text() user_name = json.loads(contents) return user_name else: return None def get_new_username(path): """提示用户输入用户名""" user_name = input('What is your name?') contents = json.dumps(user_name) path.write_text(contents) return user_name def greet_user(): """问候用户,并指出其名字""" path = Path('username.json') username = get_sorted_username(path) if username: answer = input(f"你是{username}吗,yes/no") if answer == 'yes': print(f"welcome back, {username}") else: get_new_username(path) else: username = get_new_username(path) print(f"we'll remember you when you are back, {username}") greet_user()