交互日记系统

fork this on github

任务要求

  • 完成一个极简交互式日记系统,需求如下:
    • 一次接收输入一行日记
    • 保存为本地文件
    • 再次运行系统时,能打印出过往的所有日记

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()

使用效果如下

terminal_markdown_viewer

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()

results matching ""

    No results matching ""