交互日记系统
fork this on github
- 任务要求
- Version 1.0
- Version 1.1
- Versin 2.0
- Version 2.1
- Version 2.2
- Version 2.3
- Version 3.0
- Verision 4.0
任务要求
- 完成一个极简交互式日记系统,需求如下:
- 一次接收输入一行日记
- 保存为本地文件
- 再次运行系统时,能打印出过往的所有日记
Version 1.0
代码
# coding=utf-8
import sys
def main():
message = sys.argv[1] #读取命令行中输入的参数
message += '\n' #添加换行符
file_path = "/Users/alan/Desktop/diary.txt" #设定文件路径
f = open(file_path, 'a+') #建立跟file的连接
f.write(message) #写入信息
#显示file里现有的内容
f.seek(0, 0) #将读写位置移到file最开头
print f.read()
f.close() #建立完链接最后记得关掉是一个好习惯
if __name__=="__main__":
main()
关于中文编码
python的预设编码是ascii,这个可通过运行下列代码知道
import sys
print sys.getdefaultencoding()
但是ascii编码并不支持中文,所以要将编码模式设置成支持中文的utf-8,(win上好像是gbk),所以要在.py文件开头加这样一句
# coding=utf-8
或者
# -*- coding: utf-8 -*-
如果这样还是没有解决中文编码问题,可以试试用下面三句
import sys
reload(sys)
sys.setdefaultencoding('utf8')
关于file open mode
open()函数第二个参数用于确定文件打开的模式:
r
for read
只读模式。起始位置在文件开头,也是参数缺省时的模式w
for write
写入模式。若文件不存在,新建文件进行写入;若文件已存在,先清空原有内容,然后开始写入。只能写不能读。a
for append
补充模式。若文件不存在,新建文件;若文件已存在,从文件末尾开始写入。只能写不能读。r+
=r
+w
能读能写,起始位置在文件开头。w+
=w
+r
能读能写,但原有内容会被清空。a+
=a
+w
能读能写,起始位置在文件末尾。
Version 1.1
加入时间标记
既然是日记系统,那么应该需要有时间标记。 time库里的time.ctime()能输出当时的时间信息,但里面内容太多了,我只需要'年月日'这三个信息就行。 而time.strftime()可以做到这一点,然后我选用的时间格式是'%b %d %Y',关于时间格式的详细介绍可以看time库的官方文档,也可以看strftime.org。
代码
# coding=utf-8
import sys
import time
def main():
#加入时间信息
message = time.strftime('%b %d %Y')+': '+sys.argv[1]+'\n'
file_path = "/Users/alan/Desktop/" #设定文件路径
f = open(file_path+'diary.txt', 'a+') #建立跟file的连接
f.write(message) #写入信息
#显示file里现有的内容
f.seek(0, 0) #将读写位置移到file最开头
print f.read()
f.close() #建立完链接最后记得关掉是一个好习惯
if __name__=="__main__":
main()
Versin 2.0
存储为md文件
由txt文件存储改为md文件存储有两个好处
- 更为美观的信息设计。我将时间信息改为2级标题。
- 信息输入能使用markdown标记,例如
这条信息
,我可以写成这条**信息**
,用来强调信息
。
代码
# coding=utf-8
import sys
import time
def main():
# 建立模板,'\'为续行符
template = \
'''
## {my_time}
{message}
'''
my_time = time.strftime('%b %d %Y')
message = sys.argv[1]
to_write = template.format(my_time=my_time, message=message)
file_path = "/Users/alan/Desktop/" #设定文件路径,后缀名改为.md
f = open(file_path+'diary.md', 'a+') #建立跟file的连接
f.write(to_write) #写入信息
#显示file里现有的内容
f.seek(0, 0) #将读写位置移到file最开头
print f.read()
f.close()
if __name__=="__main__":
main()
Version 2.1
在命令行中直接预览md
命令行中输出的是纯文本格式,如果能显示markdown格式就更好了
网上进行搜索,在第一个链接的问答中了解到了terminal_markdown_viewer这个项目。
然后阅读项目说明发现这是用python写的,能作为第三方库被python作如下调用
安装 直接把项目下载下来,解压缩放到diary.md同级的目录下
使用
这个项目里,核心的mdv.py
模块调用了目录下很多其他的模块,所以需要将整个terminal_markdown-viewer
目录作为一个库进行调用。
from terminal_markdown_viewer-master import mdv
然后的使用方法很简单,项目官网给出的说明是
# config like this:
mdv.term_columns = 60
# calling like this
formatted = mdv.main(my_raw_markdown, c_theme=...) # all CLI options supported
代码
# coding=utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import time
from terminal_markdown_viewer-master import mdv
def main():
#建立初始模板
template = \
'''
## {my_time}
{message}
'''
my_time = time.strftime('%b %d %Y')
message = sys.argv[1]
to_write = template.format(my_time=my_time, message=message)
file_path = "/Users/alan/Desktop/" #设定文件路径,后缀名改为.md
f = open(file_path+'diary.md', 'a+') #建立跟file的连接
f.write(to_write) #写入信息
f.seek(0, 0) #将读写位置移到file最开头
#调用mdv.py显示md文本
mdv.term_columns = 60
formatted = mdv.main(f.read(), theme='528.9419')
print formatted
f.close()
if __name__=="__main__":
main()
使用效果如下
ps: 输入 '`',要用逃逸符号,前面加'\'
Version 2.2
将md转换成html在浏览器中浏览
尝试了在命令行中输出结果后,准备尝试一下把diary.md转换成html格式,然后在浏览器中浏览。
找到了python的一个扩展库markdown, 发现里面的markdown.markdownFromFile ()函数可以将diary.md转换为diary.html
然后配合webbrowser.open()函数在浏览器里打开diary.html
代码
# coding=utf-8
import sys, time, markdown, webbrowser
def main():
#建立初始模板
template = \
'''
## {my_time}
{message}
'''
my_time = time.strftime('%b %d %Y')
message = sys.argv[1]
to_write = template.format(my_time=my_time, message=message)
file_path = "/Users/alan/Desktop/" #设定文件路径,后缀名改为.md
f = open(file_path+'diary.md', 'a') #建立跟file的连接,改为'a'模式
f.write(to_write) #写入信息
f.close()
#将diary.md转换为diary.html
markdown.markdownFromFile('diary.md', 'diary.html')
#浏览器里打开本地的diary.md
webbrowser.open('file://'+file_path+'diary.html') #文件路径前面加file://
if __name__=="__main__":
main()
打开后中文显示为乱码,将safari编码设置改为unicode(utf-8)之后中文显示就正常了,效果如下:
Version 2.3
用pandoc进行格式转换和样式添加
可以看到version2.2中,inline code并没有正确显示,这是因为markdown.markdownFromFile()
只是将markdown的语法改为了html中的一些tag,并没有提供样式信息,之前出现的中文乱码,也是因为html文件开头没有编码格式的说明。
这时候想到了从阳老博客里知道的格式转换神器pandoc
,印象中pandoc可以将md转换为完整的html,而且可以添加样式。
安装
官网里有pandoc
的安装说明,
mac用户可以在这里下载pandoc-1.15.1-osx.pkg
直接进行安装。
使用
用$ pandoc --help
看参数说明
-f
后面输入源格式(markdown) #optional-t
后面输入目的格式(html) #optional-o
后面输入目的文件(diary.html)-c
后面输入目标样式(style.css) #optional- 源文件(diary.md)前面可以不加参数
我在网上下了一个css样式,文件名为github.css
,解压后将它放到diary.md的同级目录
之后想把diary.md
转换为运用github.css
样式的diary.html
,
就可以$ pandoc diary.md -o diary.html -c style.css
了解了pandoc
的使用,就可以开始码python代码了
- 用subprocess.call()调用命令行的
pandoc
命令 - 还是webbrowser.open()打开diary.html
代码
# coding=utf-8
import sys, time, webbrowser
from subprocess import call
def main():
#建立初始模板
template = \
'''
## {my_time}
{message}
'''
my_time = time.strftime('%b %d %Y')
message = sys.argv[1]
to_write = template.format(my_time=my_time, message=message)
file_path = "/Users/alan/Desktop/"
f = open(file_path+'diary.md', 'a')
f.write(to_write)
f.close()
#将diary.md转换为diary.html
call(['pandoc', 'diary.md', '-o', 'diary.html', '-c', 'github.css'])
#浏览器里打开本地的diary.md
webbrowser.open('file://'+file_path+'diary.html')
if __name__=="__main__":
main()
显示效果:
更美观了,代码块也正确显示了。
Version 3.0
在Sublime Text 3中编辑输入信息
之前的版本里一直是以命令行里的参数作为日记内容进行输入的,是否能找到方法像使用git commit
时那样
- 激活Sublime Text 3(下简称为ST3)
- 在ST3里输入信息
- 保存关闭ST3之后,编辑的信息输入进diary.py程序用于后继处理
因为ST3进行设置有个命令行命令subl
,可以用$ subl <file>
来用ST3打开文件。
所以我最初的想法是
- 通过
subprocess.call
调用命令行的命令subl
- 用
$ subl temp.txt
打开临时文件 - 写入要记录的信息进
temp.txt
,关闭ST3 - 将
temp.txt
的信息读写进储存信息的diary.md
里 - 删除临时文件
temp.txt
根据这个思路,将原代码中的
message = sys.argv[1]
改为
subprocess.call("subl temp.txt".split())
f = open('temp.txt', 'r')
message = f.read()
f.close()
os.remove('temp.txt')
运行后出错,错误信息为
IOError: [Errno 2] No such file or directory: 'temp.txt'
想了以后发现了
subprocess.call("subl temp.txt".split())
上述语句在打开temp.txt
的同时就已经在执行了后面的命令
f = open('temp.txt', 'r')
因为还没来得及写入信息保存temp.txt
,所以结果会出现"No such file"的错误。
后来用$ subl --help
看看subl
的参数,发现参数说明里
-w or --wait: Wait for the files to be closed before returning
然后将subprocess.call("subl temp.txt".split())
改为subprocess.call("subl -w temp.txt".split())
之后代码运行成功。
此外,还找到python里tempfile
库,可以用它来创立临时文件来代替temp.txt
。
代码
# coding=utf-8
import time, tempfile, webbrowser
from subprocess import call
def main():
#建立初始模板
template = \
'''
## {my_time}
{message}
'''
# 时间信息
my_time = time.strftime('%b %d %Y')
# 用户输入的信息
tempf = tempfile.NamedTemporaryFile(suffix=".tmp") #创立临时文件
call(['subl', '-w', tempf.name]) #打开ST3编辑临时文件
message = tempf.read()
tempf.close()
to_write = template.format(my_time=my_time, message=message)
# 写入信息到diary.md
file_path = "/Users/alan/Desktop/" #设定文件路径,后缀名改为.md
f = open(file_path+'diary.md', 'a+') #建立跟file的连接
f.write(to_write) #写入信息
f.close()
#将diary.md转换为diary.html
call(['pandoc', 'diary.md', '-o', 'diary.html', '-c', 'github.css'])
#浏览器里打开本地的diary.md
webbrowser.open('file://'+file_path+'diary.html')
if __name__=="__main__":
main()
Verision 4.0
使用docopt进行参数解析
- 将
main()
函数拆分为write()和read()两个函数 - 用
docopt
进行参数解析
关于docopt
的更多介绍可以看我写的docopt
教程
代码
# coding=utf-8
# author: "Alan Lai"
# mail: "[email protected]"
"""alan_diary
Usage:
diary.py [options]
diary.py (-h | --help)
diary.py --version
Options:
-r --read Read your diary archive.
-w --write Write a new diary entry.
-h --help Help doc.
--version Show version.
"""
import time, tempfile, webbrowser, os
from subprocess import call
from docopt import docopt
file_path = os.getcwd() + '/'
def write():
#建立初始模板
template = \
'''
## {my_time}
{message}
'''
# 时间信息
my_time = time.strftime('%b %d %Y')
# 用户输入的信息
tempf = tempfile.NamedTemporaryFile(suffix=".tmp") #创立临时文件
call(['subl', '-w', tempf.name]) #打开ST3编辑临时文件
message = tempf.read()
tempf.close()
to_write = template.format(my_time=my_time, message=message)
# 写入信息到diary.md
f = open(file_path+'diary.md', 'a')
f.write(to_write)
f.close()
def read():
#将diary.md转换为diary.html
call(['pandoc', 'diary.md', '-o', 'diary.html', '-c', 'github.css'])
#浏览器里打开本地的diary.md
webbrowser.open('file://'+file_path+'diary.html')
if __name__=="__main__":
arguments = docopt(__doc__, version='alan_diary 4.0')
if arguments['--write'] and arguments['--read']:
write()
read()
elif arguments['--write']:
write()
else:
read()
Version 4.1
添加初始化日记本功能
如果一开始就使用
$ python diary.md -r
会出现'no such file'的错误,因为还没创建日记本,无法进行初次阅读
所以先建立一个初始化的函数init(), 然后在不存在diary.md时调用它。
代码
...
def init():
"""初始化日记本"""
# 创造diary.md
f = open('diary.md', "w")
f.close()
if __name__=="__main__":
# docopt会返回一个涉及参数的字典
arguments = docopt(__doc__, version='alan_diary 4.1')
if not os.path.exists('diary.md'):
init()
if arguments['--write'] and arguments['--read']: #-wr时先写再读
write()
read()
elif arguments['--write']: #-w时写入
write()
else: #-r或不带参数时阅读
read()
Version 4.2
用web库中的markdown模块进行格式转换
Pandoc
是个非常强大的软件,但用在我这里有点大材小用,而且要求用户预先安装了Pandoc
也有点太不人性化了。
除了之前提到的第三方库markdown
之外,我发现web.py
中自带了一个markdown.py
模块,可以进行md到html的格式转换,而且更易用。
但是转换后的html文件只是<body></body>
中的内容,要补足头尾的HTML
信息
代码
# coding=utf-8
# author: "Alan Lai"
# mail: "[email protected]"
# with support of Sublime Text, Pandoc & docopt
# with Thanks to andyferra's github.css
# 给docopt提供帮助文档
"""alan_diary
Usage:
diary.py [options]
diary.py (-h | --help)
diary.py --version
Options:
-r --read 阅读所有日记
-w --write 新添一条日记
-h --help 显示帮助文档
--version 显示版本信息
"""
# 导入所需模块和函数
import time, tempfile, webbrowser, os
from subprocess import call
from docopt import docopt
from markdown import markdown
#获取文件所在路径
file_path = os.getcwd() + '/'
def write():
"""调用ST3编辑输入信息"""
#建立初始模板,"\"是续行符
template = \
'''
## {my_time}
{message}
'''
# 时间信息
my_time = time.strftime('%b %d %Y')
# 用户输入的信息
tempf = tempfile.NamedTemporaryFile(suffix=".tmp") #创立临时文件
call(['subl', '-w', tempf.name]) #打开ST3编辑临时文件
message = tempf.read() #读出到message里
tempf.close()
# 将时间和记录传入模板里
to_write = template.format(my_time=my_time, message=message)
# 写入信息到diary.md
f = open(file_path+'diary.md', 'a')
f.write(to_write)
f.close()
def read():
"""在默认浏览器中浏览日记"""
head = \
'''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
<title>小小日记本</title>
<style type="text/css">code{white-space: pre;}</style>
<link rel="stylesheet" href="github.css" type="text/css" />
</head>
<body>
'''
end = \
'''
</body>
</html>
'''
f = open('diary.md')
body = markdown(f.read())
f.close()
htmlcontent = head + body +end
f = open('diary.html', 'w')
f.write(htmlcontent)
f.close
#浏览器里打开本地的diary.md,打开本地html文件时用file://开头
webbrowser.open('file://'+file_path+'diary.html')
def init():
"""初始化日记本"""
# 创造diary.md
f = open('diary.md', "w")
f.close()
if __name__=="__main__":
# docopt会返回一个涉及参数的字典
arguments = docopt(__doc__, version='alan_diary 4.2')
if not os.path.exists('diary.md'):
init()
if arguments['--write'] and arguments['--read']: #-wr时先写再读
write()
read()
elif arguments['--write']: #-w时写入
write()
else: #-r或不带参数时阅读
read()