《Python编程:从入门到实践》笔记。
本章主要是学习Python的文件操作,主要是从文件中读取数据以及将数据存储到文件中,还有错误处理,异常类,json模块等。
以下文件pi_digits.txt
包含了精确到小数点后30位的圆周率数据
# pi_digits.txt文件 3.1415926535 8979323846 2643383279 # 代码: with open("pi_digits.txt", "r") as file_object: contents = file_object.read() # 一次性读取整个文件 print(contents) # 结果:和上述文件内容一样
从上述代码可以看出,我们打开文件使用open()
函数,该函数至少接收一个参数,即文件路径。读取文件时需要向open()
函数指明是用什么方式读取文件,是只读("r"
),只写("w"
),末尾添加("a"
)还是读写均可("r+"
),open()
函数默认以“只读”方式读取文件。这只是4中常用的文件读取方式,此外还有至少8种读写方式。open()
函数返回一个文件对象,file_object
用于接收该对象。通过文件对象的read()
方法读取文件内容,且该方法返回整个文件的内容。
上述代码中的文件和源代码在同一目录中。注意文件路径的问题,绝对路径(不提倡)和相对路径(相对于源文件的路径)以及Windows和Linux下路径的写法。
注意代码中的with
关键字。其实读写文件不需要该关键字,打开文件使用open()
函数,文件读取完后关闭文件使用close()
函数,读取内容可以调用read()
方法。而之所以使用with
关键字,主要是因为①你最后忘记关闭文件,就想忘了关灯一样;②也可能是在关闭前程序出错,导致close()
语句未执行。这些让文件没有关闭的情况都有可能导致数据丢失或损坏。with
关键字则被用来应对这些情况,它保证在结束with
块时,文件一定会被关闭。
上述代码一次性读取整个文件,这在文件较小或者内存充裕的时候没有问题,但如果文件特别大,内存容量又很羞涩,则只能逐行读取:
# 代码: file_name = "pi_digits.txt" with open(file_name) as file_pi: for line in file_pi: # 也可以通过while循环配合readline()方法逐行读取文件 print(line) # 结果: 3.1415926535 8979323846 2643383279
这里需要注意一个问题,就是对行以及文件末尾空字符的读取问题,read()
和readline()
方法会读取末尾的空字符(这里是换行符)。我们可以通过之前讲的rstrip()
方法去掉末尾的空字符。
readlines()
方法将文件中每一行存入列表并返回,以下代码进一步处理文件中的内容:
# 代码: file_name = "pi_digits.txt" with open(file_name) as file_pi: lines = file_pi.readlines() pi_string = "" for line in lines: pi_string += line.strip() print(pi_string) print(len(pi_string)) # 结果: 3.141592653589793238462643383279 32
注意,Python从文件中读取出的所有内容都是字符串,如果你想要的是数字,请记得转换。
以下是一个简单的文件写入程序:
filename = "python.txt" with open(filename, "w") as file_obj: file_obj.write("I love python!")
执行改代码后你会看到在同一目录下会生成一个名为“python.txt”的文件。需要注意的是,以"w"
方式打开文件,如果要写入的文件不存在,则会自动创建该文件;如果该文件存在,该文件的内容会被清空,然后再写入。如果不想文件被清空,请使用"a"
(文件指针放在文件末尾)或"r+"
(文件指针指向文件开头)方式打开文件。还有一点,write()
函数不会在文件末尾添加换行符,如果需要换行符,请自行添加。
Python中使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当代码运行时如果遇到了不能处理的错误,Python都会创建一个异常对象,如果程序中没有处理该对象的相关代码,程序将会停止,并显示一个traceback
,其中包含异常的相关报告。如果不想程序因为某些异常而终止运行,则需要我们使用try-except
代码块自行处理异常。以下是一个处理除零错误ZeroDivisionError
的例子:
# 代码: # 捕捉异常 try: resule = 5 / 0 except ZeroDivisionError: print("You can't divide by zero!\n") # 不捕捉异常 print(5 / 0) # 结果: You can't divide by zero! Traceback (most recent call last): File "division.py", line 29, in <module> print(5 / 0) ZeroDivisionError: division by zero
如果你打算编写一个计算器应用,那么这段代码必不可少。第一个例子表明,即使发生了异常,只要异常被我们捕捉,那么程序便不会终止。如果只想捕捉异常,但暂时又不想处理,可以将上述的print("You can't divide by zero!\n")
替换为pass
语句。如果想捕获所有的异常,则except
后面不指定异常类型。
try-except
代码块还可以和else语句组合形成try-except-else
代码块,该结构表示,如果捕获了异常,这执行except
中的程序,没有发生异常则执行else
中的程序。以下程序是一个循环统计文件中单词数的例子,文件读取的部分被放到了函数中,该函数检测有没有发生FileNotFoundError
:
# 代码: def count_words(filename): """计算一个文件大致包含多少个单词""" try: with open(filename) as f_obj: contents = f_obj.read() except FileNotFoundError: msg = "Sorry, the file" + filename + " does not exist." print(msg) else: # 计算文件大只包含多少个单词 words = contents.split() 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) # 结果: The file alice.txt has about 29461 words. Sorry, the file siddhartha.txt does not exist. The file moby_dick.txt has about 215136 words. The file little_women.txt has about 189097 words.
对else
的补充:其实else
不光可以和if
,try-except
结合,还可以和for
循环和while
循环结合,比如:
for i in range(10): pass else: pass i = 0 while i < 10: i++ else: pass
这里的else
表示当循环结束后执行一些语句,比如提示之类的。
编写得很好且经过详尽测试的代码不容易出现内部错误,如语法或逻辑错误,但只要程序依赖于外部因素,如用户输入、存在指定的文件、有网络连接等,就有可能出现异常。凭经验可判断改在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。
很多程序要求用户输入某种信息,也有可能程序中某些变量的数据在程序结束后不能丢失(比如机器学习最后训练出来的模型参数),这是就需要将这些信息以文件的形式存下来。存储数据的方式有很多,现在比较简单且通用的是使用json
来存储信息。json
(JavaScript Object Notation)格式最初是为JavaScript 开发的,但随后成了一种常见格式,并被包括Python在内的众多语言采用。以下是一个经过了重构的存储用户信息的例子:
import json def get_stored_username(filename): """如果存储了用户名,就获取它""" try: with open(filename) as f_obj: username = json.load(f_obj) except FileNotFoundError: return None else: return username def get_new_username(filename): """提示用户输入用户名,并存入文件""" username = input("What's your name?") with open(filename, "w") as f_obj: json.dump(username, f_obj) return username def greet_user(filename): """向用户打招呼""" username = get_stored_username(filename) if username: print("Welcome back, " + username + "!") else: username = get_new_username(filename) print("We'll remember you when you come back, " + username + "!") filename = "username.json" greet_user(filename)
代码就不运行了,请各位自行推导程序的结果。最后在username.json
文件中会存有用户的信息。但要注意一点,json
根据数据类型来存储数据,虽然最后都是字符串,但这个过程不需要我们干预,比如要存一个列表,并不需要我们先将其转换为字符串,再存入json
,读取数据时也不需要我们先读取为字符串,再转换成列表,我们只需直接存取即可,转换工作由json
模块自动完成。
个人网站 www.vpointer.net ~
