第六章:存储和管理数据
打开,处理,关闭文件
- 打开一个文件
todos = open('todos.txt','a')
,a
表示以追加的模式
打开一个文件,返回一个文件流(对象)赋给变量todos - 写入数据
print('this is cat',file=todos)
,后面这个参数file
是用来指定要写的文件流;写完数据之后,记得关闭文件,防止丢失数据:todos.close()
- 读取数据
task=open('todos.txt')
,不带第二个模式参数,默认是读取文件,变量task会接收返回的文件流,可以使用for ch in task
去循环输出里面的数据,最后最好使用task.close()
去关闭文件
模式参数
-
r
默认模式,读取数据,并且读取的文件要存在 -
a
追加数据,保留文件的内容,向文件末尾增加新数据 -
w
写数据,写之前会清空原文件的内容;写入的文件不存在的时候,会自动创建该文件;只能将字符串写入文本文件,需要使用str()
去把数值转为字符串
with open('py.txt', 'w') as file_object:
file_object.write('I love programming.')
-
x
打开一个新文件来写数据;如果文件存在则失败
with
语句
tasks = open('todos.txt')
for ch in tasks:
print(ch, end='')
tasks.close()
# 使用with语句
with open('todos.txt') as tasks:
for ch in tasks:
print(ch, end='')
使用了with
语句,可以不用考虑调用close()
方法,充当着管理上下文的作用。符合上下文管理协议
的约定。
- 单独打印文件流对象
print(file_object)
# 读取的文件名称 模式为:read 编码为 cp936 GBK编码
<_io.TextIOWrapper name='py.txt' mode='r' encoding='cp936'>
-
open()
返回的文件流对象只在with代码块内可用
with open('py.txt') as file_object:
contents = file_object.read()
print(contents)
print(file_object.read())
![](https://img.haomeiwen.com/i1811036/1ba3f2b1dcd038fb.png)
- 解决办法:先把文件内容存储为列表,然后在
with
语句外访问
with open('py.txt') as file_object:
lines = file_object.readlines() # readlines() 返回一个列表
print(type(lines)) # list
for line in lines:
print(line.rstrip()) # 清除右边空格
-
以
json
数据格式存储数据,保存数据的原有格式-
dump(存储的数据,存储数据的文件对象)
方法,存储
-
import json
numbers = [2, 3, 5, 7, 11, 13]
filename = 'numbers.json'
with open(filename, 'w') as file_object:
json.dump(numbers, file_object) # 以原有的格式保存数据
-
load()
方法,读取
import json
filename = 'numbers.json'
with open(filename) as file_object:
numbers = json.load(file_object)
print(numbers) # 读取原有的格式数据
这是一种程序之间共享数据的方法
import json
filename = 'username.json'
try:
with open(filename) as file_object: # 如果文件存在就读取到内存里面
username = json.load(file_object)
except FileNotFoundError: # 否则就等待用户的输入,创建文件
username = input('What is your name?')
with open(filename, 'w') as file_object:
json.dump(username, file_object)
print('we`ll remember you when you come back, '+username+'!')
else:
print('welcome back,'+username+'!') # 最后都会执行这条语句
上面的代码重构一下
import json
# 获取存储在username.json文件里面的姓名
def get_stored_username():
filename = 'username.json'
try:
with open(filename) as file_object:
username = json.load(file_object)
except FileNotFoundError:
return None
else:
return username
# 存入新名字到username.json
def get_new_username():
username = input('what is your name?')
filename = 'username.json'
with open(filename, 'w') as file_object:
json.dump(username, file_object)
return username
# 问候用户
def greet_user():
username = get_stored_username()
if username:
print('Welcome back, '+username+'!')
else:
username = get_new_username()
print('we`ll remember you when you come back,'+username+'!')
greet_user()
日志记录
结合笔记三做的WEB应用,现在需要记录其web请求的详细信息和结果。
- 增加一个函数,把日志记录写入文件里面,在
do_search
函数里面去调用;传入当前的请求对象,和执行结果:log_request(request, results)
# req:是当前Flask请求对象 res:查询结果
def log_request(req: 'flask_request', res: str)->None:
with open('vsearch.log', 'a') as log:
print(req, res, file=log)
![](https://img.haomeiwen.com/i1811036/6919bfda8105b63e.png)
- 当然可以在网页中去查看,这就要增加一个
URL:/viewlog
,去读取日志文件里面保存的数据显示在浏览器中
@app.route('/viewlog')
def view_log()->str:
with open('vsearch.log') as log:
contents = log.read() # 一次性读取整个文件
return contents
![](https://img.haomeiwen.com/i1811036/1be2b49862c11d49.png)
转义数据
根据上面的结果,发现:网页显示的和日志文件不一样;那是因为浏览器会自动把一些标记隐藏。可以使用escape
函数去转义,对从日志文件读取出来的内容转义
>>> from flask import escape
>>> escape('this is a request')
Markup('this is a request')
>>> escape('this is a <html>')
Markup('this is a <html>')
@app.route('/viewlog')
def view_log()->str:
with open('vsearch.log') as log:
contents = log.read()
return escape(contents)
就算在网页上面显示了web请求对象,但是目前来说,这些请求对象都是一样的,要查看请求对象的内部,可以使用dir
内置方法,查看它的方法和属性列表。
def log_request(req: 'flask_request', res: str)->None:
with open('vsearch.log', 'a') as log:
print(str(dir(req)), res, file=log)
![](https://img.haomeiwen.com/i1811036/47daba0c3a70801f.png)
记录特定的web请求属性
一般来说,有三个属性是日志记录比较重要的:
-
req.form
:从web应用的html表单提交的数据 -
req.remote_addr
:运行web浏览器的ip地址 -
req.user_agent
:提交数据的浏览器标识
修改我们的日志记录函数:
def log_request(req: 'flask_request', res: str)->None:
with open('vsearch.log', 'a') as log:
print(req.form, file=log)
print(req.remote_addr, file=log)
print(req.user_agent, file=log)
print(res, file=log)
最后日志记录的结果是这样的:
ImmutableMultiDict([('phrase', 'this is my first python'), ('letters', 'aeiou')]) # 提交的数据
127.0.0.1 # ip地址
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 # 浏览器版本
{'o', 'i'} # 查询结果
这样虽然记录了一次web请求的比较关键的信息,但是却要从日志文件里面读取4次,应该想能不能一次web请求,记录一行数据,然后读取一次呢?
def log_request(req: 'flask_request', res: str)->None:
with open('vsearch.log', 'a') as log:
print(req.form, file=log, end='|')
print(req.remote_addr, file=log, end='|')
print(req.user_agent, file=log, end='|')
print(res, file=log)
可读的输出
- 字符串列表转为字符串
join
>>> names=['terry','john','michael']
>>> pythons = '|'.join(names)
>>> pythons
'terry|john|michael'
>>> s =pythons.split('|')
>>> s
['terry', 'john', 'michael']
- 修改
view_log
函数
# 列表的嵌套 contents 就数据读入嵌套列表里面
@app.route('/viewlog')
def view_log()->str:
contents = [] # 空列表
with open('vsearch.log') as log:
for line in log:
contents.append([]) # 追加一个新的空列表
for item in line.split('|'): # 根据 | 分解一行数据
contents[-1].append(escape(item)) # 把分解的数据放在列表的末尾
return str(contents)
-
修改后的输出
修改后的输出
-
Jinja2
模板修改输出log数据,格式化输出
{{ }}和{% block %}
是模板标记:可以使用html
变量和块作为模板的参数;循环变量模板如下,还可以嵌套:
# 希望再 the_row_titles 变量中查找数据
<tr>
{% for row_title in the_row_titles %}
<th>{{ row_title }}</th>
{{% endfor %}} # 循环结尾
</tr>
使用HTML的表格标记<table><tr><th><td>
,格式化输出日志数据,接着修改view_log
函数,使得其向浏览器返回一个html
而不是字符串
@app.route('/viewlog')
def view_log()->'html':
contents = []
with open('vsearch.log') as log:
for line in log:
contents.append([])
for item in line.split('|'):
contents[-1].append(escape(item))
titles=('Form Data','Remote_addr','User_agent','Results')
return render_template('viewlog.html',the_title='View Log',the_row_titles=titles,the_data=contents,)
修改后的view_log
的html
代码:
{% extends 'base.html' %} {% block body %}
<h2>{{ the_title }}</h2>
<table>
<tr>
{% for row_title in the_row_titles %}
<th>{{ row_title }}</th>
{% endfor %}
</tr>
{% for log_row in the_data %}
<tr>
{% for item in log_row %}
<td>{{item}}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endblock %}
格式化输出效果:
![](https://img.haomeiwen.com/i1811036/9b97e95ddf949e6e.png)
网友评论