0wd4~1wd3 CLI极简日记程序 任务描述

  • 完成一个极简交互式日记系统,需求如下:
    • 一次接收输入一行日记
    • 保存为本地文件
    • 再次运行系统时,能打印出过往的所有日记
  • 时限: 0wd4 ~ 1wd3
  • 发布: 发布到各自仓库的_src/om2py0w/0wex1/目录中
  • 指标:

    • 包含软件使用说明书: README.md
    • 能令其他学员根据说明书, 运行系统, 完成所有功能

目的:用程序方式记录下日记
目标:简单完成个交互式日记记录系统

模块思路拆解:

  • 一次接收输入一行日记

    弹出对话框接收用户输入一行文本信息,存入一string变量名line中

  • 保存为本地文件

    把变量line的内容存入用户用命令行参数方式输入的本地文件(名)中

  • 再次运行系统时,能打印出过往的所有日记

    运行程序时,检测是否存有笔记记录文件:Dairy.txt文件
    a. 有的话,打印笔记文件内容,然后继续执行上面1.部分接收用户输入的一行笔记内容
    b. 没有的话,直接执行1.部分

要点具体实现思考:

  • 接收输入一行日记 -> line = raw_input("Please type your dariy")
  • 保存本地文件 -> file.write()
    [X] 查找相应文档,如何使用file.write() function

  • 打印出过往的所有日记 -> file.read() & print()

交互 101 - 脚本调用

首先,得有一个稳定的代码记录容器: 脚本

  • 以之前上传的_src/om2py0w/0wex1/main.py为例
  • 如何调用之?

    进入文件存放目录,在命令行方式运行:Python main.py 的方式调用运行该程序。

  • 并确保每次调用返回相同的结果?

    采用同样的方式运行,就可返回同样的结果。"电脑是傻蛋!同样的运行条件,只能返回同样的结果。"

Ref: BeginnersGuideChinese - Python Wiki
6. Modules - Python 2.7.10 documentation
Code Style - The Hitchhiker's Guide to Python

交互 101 - 调用参数

然后, 脚本能接收外部数据的输入

  • 还是以_src/om2py0w/0wex1/main.py来操作吧
  • 如何做到调用main.py获得外部的数据的同时,不需要每次都修改代码?

    采用命令行参数传递的方式,程序第一行:import sys,然后调用字符串变量argv,使用argv[1:]
    argv[0]是运行的Python程序名本身(*.py)
    第一参数是 argv[1]
    第二参数是 argv[2]
    ...依此类推

Ref:
10. Brief Tour of the Standard Library - Python 2.7.10 documentation

交互 101 - 输入中文

接着最常用的要求, 脚本如何获得外部中文文本?

  • 还是以_src/om2py0w/0wex1/main.py为例吧
  • 如果给的参数是中文字符, 运行脚本会发生什么事儿?

    中文字符处理采用程序内写上语言编码集申名 # -*- coding: encoding -*-
    通常在第一行申明 # -*- coding: utf-8 -*-

    • 解决之!

Ref:
2. Using the Python Interpreter - Python 2.7.10 documention
docopt - language for description of command-line interfaces

交互 101 - 持续交互

每次记录一行日志, 都要调用一次脚本嘛!? (嫌麻烦不?)

  • 如何令_src/om2py0w/0wex1/main.py 可以一直运行, 等待我们的输入?

    采用while true:无限循环语句,实现程序运行后一直运行,不自动退出。

  • 或是接受其他命令?

    采用if 条件判断,判断输入的值=特定字符串,就执行相应命令

    if line == 'finish':
    break
    
  • 怎么退出脚本?

    提示客户输入特定字符,退出循环语句部分,最后调用文件关闭功能file.close() 安全退出。

Ref:
2. Built-in Functions - Python 2.7.10 documentation

交互 101 - 输出为文件

每次记录的日志如何变成一个本地文件永久保存?

  • 先不管数据结构, 就原样保存到文件(比如txt格式的)中吧!

    采用file.write()方法来写入文件

    • 文本怎么实现换行?

      每行文本末尾加个Newline(换行字符) 代码写作\n ,可用来实现文本换行。

    • 是否要加日期?

      暂时不加,后续翻阅稳定,查找date 函数/方法

    • 中文要用什么编码?

      没看过其他人写中文程序用别的编码,就UTF-8吧。

Ref:
open() 2. Built-in Functions - Python 2.7.10 documentation
5.Built-in Types - Python 2.7.10 documentation

交互 101 - 回读文本数据

那么, 最后一个功能就能串联起来成为一个mini软件了!

  • 每次运行_src/om2py0w/0wex1/main.py
  • 怎么自动将过往的日志都打印出来?
  • 细节:
    • 如何找到日志文件?
    • 如何打开日志文件?
    • 如何读取文件内容?
    • 如何输出日志?
    • ...
    • 中文输出OK嘛?

Ref:
open() 2. Built-in Functions - Python 2.7.10 documentation
5.Built-in Types - Python 2.7.10 documentation

版本调错 迭代- Debugging

v1

argv[1] 参数变量外面加'',变为字符串(string), 并未正确传递日记文件变量名。
file.open('argv[1]', 'a')

更改为:
file.open(argv[1], 'a')

v2

文件写入方法(method)
fname.write('%s \n') line

发现敲错,更改为的:
fname.write('%s \n') % line

还是不行。看来file.write() 方法括号内不支持采用格式化字符方式,替换变量字符+'\n' (newline回车行) ,
尝试更改为:
fname.write(line + '\n')

运行程序系统仍提示错误:

>  File "main.py", line 8  
>    fname = open(argv[1], 'a')  
>                             ^  
>IndentationError: unindent does not match any outer indentation level

看起来命令行提醒是对齐错误,检查前后的括号用法没有错误。
无意中选中全部代码,发现:

  print argv[1]  => 这一行最前端是回车在Sublime Text中自动生成的Tab制表符,要死,如不是全部选中还看不出是空格。
    fname = open(argv[1], 'a')
    print fname.read()

替换全部Tab制表符为空格。全选所有代码,检查是否修改完成。

再运行程序,系统提示file.open() 打开模式不对。

用Google搜索: python file.open mode

第一个链接: Python built-in open function: difference between modes
看过该链接信息后, 确定应该用 file.open(r+) 方式打开。

继续运行程序,系统在用户输入完一行文字按回车键后,继续报错(命真苦呀):

Traceback (most recent call last):
  File "main.py", line 17, in <module>
    fhand.write(line + '\n')
IOError: [Errno 0] Error

前后尝试修改半小时,没有头绪。只有放弃先去洗澡。中间想到既然上面file.open()模式的问题,那file.write() 也可以用同样关键字python file.write mode模式搜索得来. Google后看了前三个文档仍没头绪。

突然一下想到应把报错代码输入Google 当关键字再搜索下: IOError: Errno 0
第二个结果吸引了我。Python - IOError: [Errno 0] Error. What is Triggering this Error

从这篇文档说明,我看到file.read()执行完后,文件指针指向末尾EOF。
其建议在 file.write() 之前添加一句: file.seek(0, 2) #

不明觉厉,只能再搜索下Python官方文档 确认其用法,发现关于file.seek的说明

Bingo! 看来就是它了. 新增进代码中, 运行测试ok, 解决问题收工睡觉.

回想下这8小时编程调试,还是花太多时间困在自己头脑中运行调试除错上。

  • 应该牢记任务目标,以最小成本完成任务。
  • 不断用Google 搜索错误代码/错误线索,搜索官方文档,可以更早解决问题。

v3

在广州现场听过大妈讲解这个编程任务的指导和期望后,明确了该程序的目标功能和采用的核心函数。

  • 目标功能
    • 直接的无参数调用
    • 直觉的默认帮助 ? /h /help
    • 持续输入
    • 直觉退出 q /quit
    • 自动保存
  • 采用的核心函数
    • raw_input()
    • while + break
    • os.path.exists (参考《笨办法学Python》中习题17)
    • Open()
    • for .. in
  • < 50行的代码
  • 测试除错
    • 能否在没有日记文件的情况下,运行程序正确记录在新的日记文件中
    • 能否在日记正文中输入中文正确记录,并正常展示过往中文日记

针对打印过往日记内容,输入本次日记两个模块,分别封装了2个函数:

def print_all_records(f):
    line_count = 1
    print 'Previous dairy records: \n'
    for current_line in f:
        print '%i - %s' % (line_count, current_line[:-1])
        line_count +=1
    print '=' * 20
def lines_inputs(f):
    print 'Diary now... \n'
    while True:
        line = raw_input('Current >>> ')
        if line == '?' or line == 'h' or line == 'H':
            print '^-^ \n \t input ?/h/H for help \n \t input q/bye quit the Dairy progame'
            continue
        if line == 'q' or line == 'bye':
            break
        f.write(line + '\n')

运行测试,发现:

>  File "main.py", line 37  
>    print '%i - %s' % (line_count, current_line)  
>        ^  
>IndentationError: expected an indented block
  • 检查几遍后仍提示该错误,马上想到可能自动对齐时Tab建与空格键混淆。Sublime Text全选全部代码后查看果然,立马转换所有代码对齐采用空格。

接着测试,发现:

>E:\projects\omooc2py\_src\om2py0w\0wex1>python main.py  
>  File "main.py", line 57  
>    if exists(log_name) = True:  
>                        ^  
>SyntaxError: invalid syntax

这个错误没有什么好说,条件判断应该用双等号 ==. 单等号(=) 是赋值用的。立马改。

再测试:

>Diary now...
>
>Current >>> 试验下中文可以输入吗?
>Traceback (most recent call last):
>  File "main.py", line 68, in <module>
>    main()
>  File "main.py", line 60, in main
>    lines_inputs(log_name)
>  File "main.py", line 51, in lines_inputs
>    f.write(line + '\n')
>AttributeError: 'str' object has no attribute 'write'

参数传递错误,应该用current_file传递 给lines_inputs()函数,其他同参数名部分参照更改过来。

Ok, 终于测试通过,最后附加到本开发文档这段曲折。

进展

  • 151027 Penguin 完成v3
  • 151021 Penguin 完成v1, v2
  • 151018 Penguin 写下开发思路