Python 编程: 从入门到实践笔记.
本章主要是学习 Python 的文件操作, 主要是从文件中读取数据以及将数据存储到文件中, 还有错误处理, 异常类, json 模块等.
1. 从文件中读数据
1.1 读取整个文件
以下文件 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 块时, 文件一定会被关闭.
1.2 逐行读取
上述代码一次性读取整个文件, 这在文件较小或者内存充裕的时候没有问题, 但如果文件特别大, 内存容量又很羞涩, 则只能逐行读取:
- # 代码:
- 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() 方法去掉末尾的空字符.
1.3 将文件每一行放入列表中
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 从文件中读取出的所有内容都是字符串, 如果你想要的是数字, 请记得转换.
2. 写入文件
以下是一个简单的文件写入程序:
- filename = "python.txt"
- with open(filename, "w") as file_obj:
- file_obj.write("I love python!")
执行改代码后你会看到在同一目录下会生成一个名为 "python.txt" 的文件. 需要注意的是, 以 "w" 方式打开文件, 如果要写入的文件不存在, 则会自动创建该文件; 如果该文件存在, 该文件的内容会被清空, 然后再写入. 如果不想文件被清空, 请使用 "a"(文件指针放在文件末尾)或 "r+"(文件指针指向文件开头)方式打开文件. 还有一点, write()函数不会在文件末尾添加换行符, 如果需要换行符, 请自行添加.
3. 异常
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 后面不指定异常类型.
3.1 else 代码块
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 表示当循环结束后执行一些语句, 比如提示之类的.
3.2 决定报告哪些错误
编写得很好且经过详尽测试的代码不容易出现内部错误, 如语法或逻辑错误, 但只要程序依赖于外部因素, 如用户输入, 存在指定的文件, 有网络连接等, 就有可能出现异常. 凭经验可判断改在程序的什么地方包含异常处理块, 以及出现错误时该向用户提供多少相关的信息.
4. 存储数据
很多程序要求用户输入某种信息, 也有可能程序中某些变量的数据在程序结束后不能丢失 (比如机器学习最后训练出来的模型参数), 这是就需要将这些信息以文件的形式存下来. 存储数据的方式有很多, 现在比较简单且通用的是使用 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 模块自动完成.
来源: https://juejin.im/post/5b0d74156fb9a009f13d85fb