20161220

由 excel 转换为 markdown,及收获




1. 问题
构建之法(现代软件工程)东北师大站[http://www.cnblogs.com/younggift/]的每周学生作业成绩,执行教学团队[https://home.cnblogs.com/u/xinz]要求,发布在 cnblogs 上。作业中包括每位同学在作业单项中取得的分数、累加、按比例分配、线性映射等数据,本学期约17次作业成绩或排序统计,产生约70个表格。成绩公布后学生申诉教师修改成绩时,这些表格需要重新计算,再次发布。每次作业申诉数量大约5次左右。

Emacs是我平时的主要工作环境,所以优选熟悉的工具。上学期第一次成绩发布使用了 org-mode 中的表格,发布为html源代码,粘贴到 cnblogs上。成绩累加、变更后的重新计算非常麻烦,org-mode主要是大纲写作工具,不是电子表格。使用的感觉类似于在word的表格中计算。
Excel适合记录、计算、数据变更后再计算,cnblogs使用纯文本、html 或markdown格式。其中 markdown 格式语法简洁,支持大纲式写作和表格,所以适合成绩发布和变更后再次发布。因此,上学期除最初一两次,以及本学期大部分作业成绩发布,先在excel下记录和计算,然后转换为 markdown 格式。
本文回顾我使用的三种方法,由 excel 文件转为 markdown,及收获。
2. 方案1,FFL 的 exceltk.exe
推荐使用此方案。在本学期大多发布中,我都使用了这一工具。没有使用的几次是在等待升级,采用了临时方案。
exceltk 最初是小牛同学拷给我的,说这个非常方便。后来 FLL 老师做过升级,其中对公式的支持、支持移动设备上查看cnblogs上的表头不变形、小数点保留位数这几次升级都很有帮助。
FFL 老师对 exceltk.exe 的介绍在
[http://www.cnblogs.com/math/p/exceltk.html]。
源码:https://github.com/fanfeilong/exceltk
下载:http://files.cnblogs.com/files/math/exceltk0.0.9.7
我的使用方法类似
exceltk.exe -t md -p 2 -xls 构建之法作业成绩debug.xls
把excel中的每个 sheet 导出成 markdown,小数点保留两位精度。
3. 方案2,sed
exceltk有一段时间不支持 excel 公式计算结果,我换用了临时方案,等待exceltk升级后切换回来。
3.1 为什么需要公式
我的excel中使用了 vlookup, match 等函数,以方便学生申诉以后的成绩变更。
比如个人作业单项变更,需要因此变更的字段有 个人作业总和、个人作业映射到占本周总成绩20%、本周成绩总和、数周累积、数周累积排序、数周累积去除负分同学排序、数周累积映射到[50,100];再如团队成绩单项变更,需要因此变更的字段有 该团队总分、该团队总分映射到本周总成绩的30%、该团队所有成员的团队成绩、该团队所有成员的本周总成绩、该团队所有成员的数周累积以及排序和映射到[50,100]。诸如此类。由每周作业的单项数目不同,所以公式不宜用固定列的序号,比如”=SUM(B4:L4)”,而采用了vlookup & match 函数对字段寻址。
vlookup & match 函数类似这样:
=VLOOKUP(F4,小组!$A:$W,MATCH("合计",小组!$1:$1,0)+1,FALSE)
含义是
(1) F4单元格所在列是”所属小组”,每行一人,随行变化。此例中的值为
“=VLOOKUP(A4,组员归属!A:B,2,FALSE)”,求值结果 “飞天小女警”。
(2) 取”飞天小女警”的”合计。”取 名为”小组”的工作表 中,表头 (第一行)写
作”合计”的那列的数据,要求 A列的值为 F4的那行,即”飞天小女警”。
(3) 总结,姓名 -> 组员归属 -> 小组成绩.
这样,当小组成绩变更以后,该团队所有成员的小组成绩、本周总成绩、数据累积等都会自动变化。我只要修改变更的单项,然后再把excel导出成 markdown发布就行了。不使用公式,每次变更需要顺序修改、复制粘贴若干次,时间长工作量大,每个单项都要消耗30分钟左右,还担心出错。使用公式后成绩变更一次几分钟。

3.2 excel -> csv -> markdown
sed 是 perl 的灵感来源之一,另一个是 awk。它们专门辅助 shell 脚本,awk做计算,sed做文本替换。
我用的临时方案脚本,在这里[https://coding.net/u/younggift/p/xls2md/]。
3.2.1 excel -> csv, vba
我把 excel 导出为 csv 格式,这样完成了公式的计算,也成为了文本格式,sed才能处理。
使用了 stackoverflow 上的 vbs 脚本,稍作修改,按数据表名导出。
----脚本开始
 
if WScript.Arguments.Count < 2 Then
 WScript.Echo "Please specify the source and the destination files. Usage: ExcelToCsv <xls/xlsx source file> "
 Wscript.Quit
 End If
 
csv_format = 6
 
Set objFSO = CreateObject("Scripting.FileSystemObject")
 
src_file = objFSO.GetAbsolutePathName(Wscript.Arguments.Item(0))
 dest_file = objFSO.GetAbsolutePathName(WScript.Arguments.Item(1))
 
rem msgbox(dest_file)
 
Dim oExcel
Set oExcel = CreateObject("Excel.Application")
 
Dim oBook
 Set oBook = oExcel.Workbooks.Open(src_file)
 
oBook.Worksheets(1).ActivateoBook.SaveAs dest_file+"\个人", csv_format
 
oBook.Worksheets(2).ActivateoBook.SaveAs dest_file+"\结对", csv_format
 
oBook.Worksheets(3).ActivateoBook.SaveAs dest_file+"\小组", csv_format
 
oBook.Worksheets(5).ActivateoBook.SaveAs dest_file+"\本周", csv_format
 
oBook.Worksheets(8).ActivateoBook.SaveAs dest_file+"\数周排序-去除负分", csv_format
 
oBook.Worksheets(9).ActivateoBook.SaveAs dest_file+"\数周累积负分", csv_format
 
oBook.Close False
oExcel.Quit
 
----脚本结束
调用的时候,在bat中,如下。
----bat片段开始
 chcp 936
 
set filename=构建之法作业成绩beta-review.xls
 xls2csv.vbs %filename% .
 ----bat片段结束
3.2.2 cvs -> mark down, sed
根据不同数据表的格式不同,我写了不同的 sed 脚本。”应该”把某些 sed 脚本抽象合并到同一个文件中,不过考虑到复用次数不多、可预见的复用增长不大、以及懒,所以就复制粘贴,然后分别修改了。
所以 shell 脚本看起来这样,里面的 c1_head 与 c1, c2_head 与 c2 长得很像,抽象优化强迫症患者可能感觉不好。
----shell脚本片段开始
sed -f c1_head.sed 本周.csv | sed -f c2_head.sed &gt; 本周.md
sed -f c1.sed 数周排序-去除负分.csv | sed -f c2.sed &gt; 数周排序-去除负分.md
----shell脚本片段开始
每行分成两个sed执行,用管道连接,重定向到指定名称的md即markdown文件中。分成两个sed执行是必要的,因为 sed 不支持对刚刚粘贴来的行通过引用行号编辑。或者是因为我没有做出足够好的正则表达式 (@典同学,@marverick@柳园bbs) ,考虑sed/正则表达式的处理能力,此处应该不涉及类似括号匹配的上下文无关文法。
3.3 sed解读
3.3.1 测试用例
(1) cvs的前几行
列之间用”,”分隔。在我的临时sed脚本中,没有处理转义”,”的情况,解决的方案是在xls中避免使用半角逗号。
,20160901,20160908,20160922,20160929,20161013,20161020,20161027,20161103,20161110,累积,映射至[100,60],映射至[100,50]
 ,,,pre-α,α-1,α-2,α-review,β-1,β-2,β-review,,,
 [黄兴](http://www.cnblogs.com/huangxman),72.00 ,80.00 ,68.60 ,5.15 ,41.06 ,63.93 ,60.60 ,69.60 ,78.03 ,538.97 ,100.00 ,100.00
 [李俞寰](http://www.cnblogs.com/li-yuhuan/),85.00 ,86.00 ,69.53 ,35.75 ,44.35 ,64.53 ,41.20 ,31.20 ,78.63 ,536.19 ,99.79 ,99.73
 [张金生](https://www.cnblogs.com/jx8zjs/),93.00 ,94.00 ,72.47 ,-1.00 ,66.06 ,-1.53 ,39.60 ,63.88 ,88.41 ,514.89 ,98.14 ,97.67
 [程媛媛](https://www.cnblogs.com/yuanyuancheng/),61.00 ,76.00 ,-7.60 ,13.27 ,41.23 ,94.67 ,69.40 ,75.20 ,86.51 ,509.68 ,97.73 ,97.17

(2) markdown的前几行
形如”|:–|”的文字,用于分隔出表头。
||20160901|20160908|20160922|20160929|20161013|20161020|20161027|20161103|20161110|累积|映射至[100,60]|映射至[100,50]|
 |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
 |[黄兴](http://www.cnblogs.com/huangxman)|72.00 |80.00 |68.60 |5.15 |41.06 |63.93 |60.60 |69.60 |78.03 |538.97 |100.00 |100.00 |
 |[李俞寰](http://www.cnblogs.com/li-yuhuan/)|85.00 |86.00 |69.53 |35.75 |44.35 |64.53 |41.20 |31.20 |78.63 |536.19 |99.79 |99.73 |
 |[张金生](https://www.cnblogs.com/jx8zjs/)|93.00 |94.00 |72.47 |-1.00 |66.06 |-1.53 |39.60 |63.88 |88.41 |514.89 |98.14 |97.67 |
 |[程媛媛](https://www.cnblogs.com/yuanyuancheng/)|61.00 |76.00 |-7.60 |13.27 |41.23 |94.67 |69.40 |75.20 |86.51 |509.68 |97.73 |97.17 |
 |[张政](https://www.cnblogs.com/regretless/)|90.00 |98.00 |71.27 |5.07 |65.48 |-2.73 |20.00 |68.38 |92.91 |508.38 |97.63 |97.04 |
3.3.2 把 , 转成 |
# , => |
 s/,/|/g;
 s/^/|/g;
 s/$/|/g;
(1) s是substitute.
(2) s / 原来的文字 / 替换成的文字 / 全局
(3) ^表示行首,$表示行尾。
总的效果是,把所有逗号换成竖线,行首行尾各加一条竖线。
3.3.3 表头 |:–|
数据流是这样的 (cvs) -> c1_head -> c2_head -> (md),其中括号里的是产物,没括号的是加工。
在 c1_head.sed 中:
 # table head, copy & paste
 1h
 1G
在 c2_head.sed 中:
 2s/[^|]//g
 2s/|/|:--/g
 2s/|:--$/|/g
(1) 1h 复制第1行,1G粘贴在当前位置。得到
||20160901|20160908|20160922|20160929|20161013|20161020|20161027|20161103|20161110|累积|映射至[100,60]|映射至[100,50]|
 ||20160901|20160908|20160922|20160929|20161013|20161020|20161027|20161103|20161110|累积|映射至[100,60]|映射至[100,50]|
(2) c2_head.sed中几行的作用,是对只转换第2行,不是对全局影响。
(3) 2s/[^|]//g,除了竖线以外,去除所有字符。
||20160901|20160908|20160922|20160929|20161013|20161020|20161027|20161103|20161110|累积|映射至[100,60]|映射至[100,50]|
 ||||||||||||||
(4) 2s/|/|:–/g,把第2行的所有竖线,转换为 |:–
||20160901|20160908|20160922|20160929|20161013|20161020|20161027|20161103|20161110|累积|映射至[100,60]|映射至[100,50]|
 |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--
(5) 2s/|:–$/|/g,把第2行行尾前的 :– 转换为 竖线。
||20160901|20160908|20160922|20160929|20161013|20161020|20161027|20161103|20161110|累积|映射至[100,60]|映射至[100,50]|
 |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
之所以采用复制粘贴-替换的方法,是因为sed不会计数。

3.3.4 空行 ||||||||||||||
原始 cvs 形如:
---cvs片段开始
 姓名,继续迭代,PSP,进度条,代码堆积图,博客字数堆积图,beta发布评论,加分事项,加分分值,合计,占比20%
 满分分值,,5,5,5,5,5,,,25,20.00
 
[程媛媛](https://www.cnblogs.com/yuanyuancheng/),,5,5,5,5,5,,,25,20.00
 [杜桥](http://www.cnblogs.com/duq11/),,5,5,5,5,5,,,25,20.00
 ---cvs片段结束
期待修改为形如下面的样子。”||||||||||||||”一行,用于建立空行,目的是造成两行表头的效果。
||个人作业|占比20%|结对|占比20%|所属小组|小组成绩|占比30%|贡献系数(4人分配4*20)|占比30%|特别加分事由|特别加分数值|本周得分|
 |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
 |满分分值|25.00 |20.00 |10|20||35.00 |30.00 |5*N|30.00 |||100.00 |
 ||||||||||||||
 |[程媛媛](https://www.cnblogs.com/yuanyuancheng/)|25.00 |20.00 |0|0|飞天小女警|37.00 |31.71 |5.80 |34.80 |||86.51 |
 |[杜桥](http://www.cnblogs.com/duq11/)|25.00 |20.00 |0|0|奋斗吧兄弟|32.00 |27.43 |5.00 |30.00 |||77.43 |
 |[杜月](http://www.cnblogs.com/qianhuihui/)|24.00 |19.20 |0|0|金州勇士|48.00 |41.14 |5.12 |30.72 |||91.06 |
 |[宫成荣](http://www.cnblogs.com/gongcr/)|25.00 |20.00 |0|0|新蜂|19.00 |16.29 |6.00 |36.00 |||72.29 |
在 c1_head.sed 中:
# table head, copy & paste
1h
 1G
# blank line, copy & paste
3G
在 c2_head.sed 中:
4d
 5s/[^|]//g
(1) 在 c1_head中 复制第1行,另粘贴到第3行一份。此时文本文件仍维持原有
的行号,新粘贴的文字不能使用行号引用,因此不能进一步编辑。
||个人作业|占比20%|结对|占比20%|所属小组|小组成绩|占比30%|贡献系数(4人分配4*20)|占比30%|特别加分事由|特别加分数值|本周得分|
 |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
 |满分分值|25.00 |20.00 |10|20||35.00 |30.00 |5*N|30.00 |||100.00 |
 ||个人作业|占比20%|结对|占比20%|所属小组|小组成绩|占比30%|贡献系数(4人分配4*20)|占比30%|特别加分事由|特别加分数值|本周得分|
 
[程媛媛](https://www.cnblogs.com/yuanyuancheng/),,5,5,5,5,5,,,25,20.00
 [杜桥](http://www.cnblogs.com/duq11/),,5,5,5,5,5,,,25,20.00
(2) c2_head.sed中的4d,删除第4行空白行 (在c1_head中的第3行) 。
||个人作业|占比20%|结对|占比20%|所属小组|小组成绩|占比30%|贡献系数(4人分配4*20)|占比30%|特别加分事由|特别加分数值|本周得分|
 |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
 |满分分值|25.00 |20.00 |10|20||35.00 |30.00 |5*N|30.00 |||100.00 |
 ||个人作业|占比20%|结对|占比20%|所属小组|小组成绩|占比30%|贡献系数(4人分配4*20)|占比30%|特别加分事由|特别加分数值|本周得分|
 [程媛媛](https://www.cnblogs.com/yuanyuancheng/),,5,5,5,5,5,,,25,20.00
 [杜桥](http://www.cnblogs.com/duq11/),,5,5,5,5,5,,,25,20.00
(3) 5s/[^|]//g,把原第5行 (删除第4行后显示为第4行,仍计数第5行)改为 ||||||||||||||
||个人作业|占比20%|结对|占比20%|所属小组|小组成绩|占比30%|贡献系数(4人分配4*20)|占比30%|特别加分事由|特别加分数值|本周得分|
 |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
 |满分分值|25.00 |20.00 |10|20||35.00 |30.00 |5*N|30.00 |||100.00 |
 ||||||||||||||
 |[程媛媛](https://www.cnblogs.com/yuanyuancheng/)|25.00 |20.00 |0|0|飞天小女警|37.00 |31.71 |5.80 |34.80 |||86.51 |
 |[杜桥](http://www.cnblogs.com/duq11/)|25.00 |20.00 |0|0|奋斗吧兄弟|32.00 |27.43 |5.00 |30.00 |||77.43 |
 |[杜月](http://www.cnblogs.com/qianhuihui/)|24.00 |19.20 |0|0|金州勇士|48.00 |41.14 |5.12 |30.72 |||91.06 |
 |[宫成荣](http://www.cnblogs.com/gongcr/)|25.00 |20.00 |0|0|新蜂|19.00 |16.29 |6.00 |36.00 |||72.29 |


4. 方案3,emacs elisp
Emacs是我平时使用的工具,所以本学期最初的转换,当需要公式,因此由 cvs转成 markdow 还没有被 ffl 支持时,自然地想到用 elisp 作为临时方案。
elisp是上下文无关文法 (或者更强?)的语言,因此可以计数,得以避免使用复制粘贴-修改这样的手段生成表头行。col-count用于存储列的数量。
(defun cvs2md-table ()
   "replace cvs format to markdown talbe."
  (interactive)
  ; , -> |
  (goto-char (point-min)) 
  (replace-string "," "|")
  (goto-char (point-min))
  (replace-regexp "^" "|")
  (goto-char (point-min))
  (replace-regexp "$" "|")
  ; table head
  (setq col-count 0)
  (goto-char (point-min))
  (setq col-count (count-matches "|" (line-beginning-position) (line-end-position)))
   (goto-line 2)
  (setq head-count 0)
  (while (&lt; head-count col-count)
     (insert "|:--")
    (setq head-count (1+ head-count))
    )
  (insert "|")
  (open-line 1)
  ; delete "||" in the last line
  (goto-char (point-max))
  (beginning-of-line)
  (kill-line)
  )
5. 收获
一个技术方案是否能被别人采用,因此具有更帮助更多的人而不仅是自己,取决于多个方面。比如,emacs这类相对小众之下开发的代码,sed这种需要运行环境和只能命令行操作的脚本,对于很多人不算友好。ffl的工具可以复制粘贴,也支持命令行,基于.net运行环境在当今不再是个问题,友好得多。
语言的能力越强,越接近于图灵机,实现通用功能,比如计数、插入某个特定数量的字符,就越容易。所以在DSL中要小心配置文件容易迅速成长为上下文无关文法,然后图灵等价,成为新的语言,专用特性的优势就消失了。能力弱一些的语言,也不见得不能实现,比如把插入特定数量的字符等价为 (事实上这才是原始的需求)复制粘贴再修改某一行。还是要确定自己的问题到底是什么,计算模型和数学模型的选择,然后才是代码。
要清楚–尽可能第一时间发现–语言或工具的限制,比如sed不为刚插入和文本编排行号,因此不能基于行编辑。

————————————————————
博客会手工同步到以下地址:
[http://zhuanlan.zhihu.com/younggift]
[https://younggift.net/]
[http://blog.csdn.net/younggift]
[http://giftdotyoung.blogspot.com]

唐诗宋词与我


题图临摹果壳日历2017年1月1日。
唐诗宋词以后,当数元曲,而不是”我”。题目所以在唐诗宋词后面继之以我,是因为元曲里,除了课本上背过的那些,我就只知道郭靖背着黄蓉的那段情话并兴亡之论。课本上的那几篇,又都油腔滑调的,颇不符合我当时的小资情怀。对了,还有前几年编程序吵架的间隙,关同学为我们科普过一段汤显祖,兼论越 (粤?)剧。就是那位一个人相当于一支军队的关同学,主业是程序员,她的故事参见[https://zhuanlan.zhihu.com/p/19966058]。
1. 第一首唐诗
小时候有一段住在我姥家,大约一年时间吧。我姨家的弟弟有次去玩儿,他没有托管在我姥家,而是去幼儿园。大人们问在幼儿园学什么,然后让他背一段唐诗。他立定站稳,声音洪亮,背了一首《望庐山瀑布》。
我现在还能记住当时他带给我的沉重打击。他比我小三四岁吧,就能背唐诗了。而我,应该还啥也不会背,或者也许能背”更上一层楼”,楼字的发音还是大舌头的。那个时候还不知道,又过大约两年之后,我才开始能写字,我爸启蒙的,是”岳飞”二字,繁体,然后才是我本人的名字。
日照香炉生紫烟,一道瀑布挂前川。
我完全听不懂,仅仅声音里的气势就把我震慑住了。
我接触的第一首唐诗,由一个比我小三四岁的小孩背出来的。那个时候,我还不能表达”我也要背”,会被大人批评”别人想干啥你也想干啥”,且原文是东北方言,比这阴损得多。
2. 我也能写
终于上了小学,顺理成章地就可以”也背”了,不用额外恳求。每个人都拥有这样的权利,背诗。所以说,教育平等非常好。
小学一年级班主任杨老师讲到《咏鹅》的时候,就是那首”鹅,鹅,鹅”,感叹着说,”你看人家骆宾王,才七岁就能写诗”,然后殷切地望着我们。其实我已经忘记这首诗的作者是谁了,刚刚还搜了一下,到底是骆宾王还是王洛宾。但是我现在还记得的是,老师话音刚落,我就举手。
老师: 咋的?
杨贵福: 老师我也能写。
老师: 真的啊,那你写一个。
杨贵福立定站稳,声音洪亮,写道: “江上往来人,但爱鲈鱼美。君看一叶舟,出没风波里。”
老师说: 你这不是写,是背。不错,我还以为你们都不会背诗呢,还有哪位小朋友会背……
众: 我,我,我。
这首诗其实我背得艰难,尤其第三句,忘记很多次。”这一句不押韵,背起来确实难”,我的诗歌启蒙老师是我哥,他非常理解地说,”你多背几遍吧。”
3. 我真的能写
转眼就是小学二三年级了,认字不多的我读了能接触到的全部书。全部书的范围,应该是三四本民间故事,人参娃娃啦,人参姑娘啦,人参老大爷啦,诸如此类。一个令我感动至深的故事,是老把头 (抓人参的人)孙某某从山东远道而来,在路上跟一个人参精变成的青年结拜,后来跟人参精走散,满山寻找。最后他弹尽粮绝,饿得不行,在古河之中抓到三只 la3 gu,因为只有精细蛋白质补充而脂肪和糖类不足,饿死在河边,死后也成了精。死前他写了一首诗:
家住山东我姓孙,飘洋过海来挖参。半路丢了亲兄弟,沿着古河往上寻。三天吃了个 la3 la3 gu,找不到兄弟不甘心。
看了这个故事不久,有天我妈下班非常晚,我放学回家没找到饭吃,炉子也落了(阴燃的煤烧尽,需要用柴火重新生火,而柴火是宝贵资源;生炉子还需要特殊技能,当时还没有解锁),冻得够呛。后来知道那天我妈他们加班,可能是绑钢筋或者搭架子,或者自愿义务献工两小时,做什么不重要。
饿死前,我也赋诗一首,如下。
家住通化我姓杨,回到家中不见娘。找啊找啊没找到,炉子落了屁股凉。这事儿不怨赵工长,只是妈妈工作忙。
第一首诗。留在这里,姑且作为纪念,一个小资青年的童年。最后两句,其实我没有那么高的觉悟。那么写,一个是当时我看到的都是这么高的格调,另一个是,除了”不怨”,我又有什么别的办法。我妈说,”你以为钱是大风刮来的啊,那是我和你爸汗珠子掉地上摔八瓣挣来的。”
不少同学的父母从事的不是体力劳动,虽然你觉得非常辛苦--到了一定年龄,就都学会了承认父母是天下最辛苦的人--但是体力劳动还是非常不同的。真正做过一段时间,你就会明白,为什么有的体力工人会酗酒,因为肌肉太疼。
我对父母的理解,只是口头的表达而已。到了初中,我迅速成长为一个小资少年,写了好几厚本诗。每页一首,字非常大,内容全是(不是大多)悲秋伤春,虽然整个初中也就三个春天三个秋天,还有感叹我班教室门口那棵大杨树要死了没人管。诗写在我妈从工地要回来的建筑施工的记录单背面。正面是我的大作,背面是铅字印刷,我们都力透纸背。记录单里偶尔有人手写的东西,鬼画符一样,看不出来是什么。封皮是洋灰袋纸那一类装订,当然上面并无水泥。我会用线装订书籍和修书,就是那时候练下来的功力,每本都像电视里线装的《九阴真经》一样。上面用钢笔描出来的毛笔字,”何以语出惊风雨,诗成泣鬼神”。
初中班主任黄老师有一次看到我写的诗,评论说,”你这随感写得不错啊。”
什么,随感?我全部的生活和感悟,只是诗歌里我觉得最不起眼的随感吗?难道不应该是诗言志,立意甚高,笔法娴熟。我很失望,对老师很失望。
当是时也,正是198X年代末。我想,如果以后有英国作家为我写自传,可能会这样回顾那一打击: 他年少时自许颇有诗才,甚至曾经尝试写出一些长短不齐并且押韵的文字。没有发现有文学家或者诗人对他作品的评论,可以找到的唯一资料是他自己引用教语文课的黄老师在他初二时给予的中肯甚至带有鼓励性质的虚假正面评论”不错”,这一评论的肯定部分极可能建立在他当时身为中二学生的这一身份之上。他对于”不错”二字并无觉悟,反而对语文老师把他的作品归结为”随感”耿耿于怀,事实上对于一个中二少年来说,如果他除了随感之类的无病呻吟,生活竟然就没有别的,当然不可能成长为一个诗人。

4. 补课
初二初三,看到同学读 李璟李煜词,她还看 李清照选集 (书也许不是这个名字,李清照是没错)。书非常薄,几十页,因为他们的作品也就那么多,书皮印成线装书的样子,当然是假的,远不如我的手工。当时对借书的全部认识,就是毛主席抢了肖三的书,一把夺过,以表达对书的热爱,然后在上面写满批注,再还回去,甚至就此匿下。好在我没有那么做,而是礼貌地借,虽然并不认识人家。
不过,你能想借我的书看 (虽然现在我既不乐意,也不会借),是对我的品味的直接褒奖。大学毕业以后,躺在床上读宋词赏析词典,被严同学借走。我对他的品味就此评价高出许多,虽然依然认为他在生物本专业和逻辑上甚是不严谨。栾同学则大段背诵原文,他背的不是短小而押韵的诗歌,而是余华的小说,还有电影《秋日传奇》的情节,事无巨细,连标点符号我看都不会错。大哥那个时候则基本不读书,后来我在他办公室里看到半柜子哈耶克汪丁丁,很是感叹了一番。他热情地推荐给我看汪丁丁,我翻翻评论道,”写得什么破玩意,话都说不明白。”他推荐我看哈耶克,说,这个真不一样。我很勉强地借走了《科学的反革命》,心想,等我看完回来批你一头狗屎。也是一本小册子,百十来页的样子,我去北京机场接Lars他们一行十多个人的时候随身带着,躺在靠椅上想催眠,结果就翻了大半本。唯心主义给我的第一次沉重打击,太有道理了。
能够在非常薄的篇幅内包含巨量内容,只有诗歌、哲学,还有数学。流落荒岛的必备读物,及《野外生存》一本。
说回初二三的那两本宋词,人家早我多少年就读过了,而我才巴巴地借来看。然后全抄下来,好在并不长。所以你看,有正确地爱好多么重要。
高中,在满是灰尘的图书室靠着窗户,看《全唐诗》。一段段写得无聊,觉得要么我水平低,要么古人写得烂,但是也不敢说。直到最近在douban看到有人发出同样的抱怨,知音啊。哪能每首写得都那么好,李白的烂诗多了去了,翻来覆去就是酒好喝啊喝好酒,我愿意天天喝啊天天喝。原文忘记了,大意如此。我都怀疑那是不是后人瞎编的。也是铅字印刷,力透纸背,两面都力透纸背,所以读起来挺费劲的,不过这成功地阻止了我继续怀疑。
5. 还是学理工吧
大学一年级,我们有征文。对,物理系也有征文,征文学类作品,不是公式推演。我应征的作品之一是在黑板上推敲出来的一段,你可以想像旁边就是勒让德公式,还新鲜热辣。
如果有一天,我不再能翱翔,你是否还会赞美我的翅膀。
P同学为代表的一干人等当面问我,当我面不是当评委面,是不是抄的,怎么记得在哪里见过。我答,林黛玉也觉得他见过贾宝玉,写得好,你读起来感觉就是这样。不是抄的,是我从高中就开始写的。
大学几年级忘记了,有选修课。绝大多数课都是必选的,并且我们必须亲自选。大家跑到机房,围成一圈的机器,只有数字的小键盘,按纸上的课表把自己必须也只能那么选的课录进去。只有个别课可选,我选了《唐宋诗词》。
老师是个高大而柔软的胖子,脸上略有胡茬。我一度担心他的胡茬会导致漏气,并没有。
他上课的时候非常有魅力,后来我发现,尤其是女同学们更觉如此。师范大学本来女生就多,喜欢唐宋诗词的男生更是稀少。你是否能体会到他讲到精彩处,全班的”啊~~哦,这样喔”这样的感觉。其实诗歌里是不太用感叹词的,除了李白,他一下子就用了好几个,”噫吁兮”。
老师上课的时候还会把古诗唱出来,他唱的是《将近酒》。比后来网上传的那段差得很多,甚至我都能听出来跟跑调似的。不过他说,这不是唱,而是吟,并且声称古人就是这样吟诗的。我想提问,一方面据说古诗的格律暗合音律,所以应该是有谱子的,每次都类似的旋律,而不应该像我一样每次另谱一曲,他给我的感觉也是随便瞎吟的,并无定规。另外,我对将近酒中醉的程度与他的理解不同,我觉得那应该是喝得正high,可以打架可以交友可以吹牛的程度,他吟的状态似乎快要说不出话要睡着的感觉。写到这里,刚刚又想起来,他还吟了”但愿人长久”,与王菲版差整整一个梅艳芳外加半拉屠洪刚。
还有个重大问题,他怎么知道古人就是那么吟的。我非常想讨教,但是课后老师被全班女生包围着问,我想并没有性别平等的提问时间,就赶去编程序了。
匆匆,是离别的生椒。
考试的时候,我非常认真地答了,题目也还简单,不必复习。但是!居然没有我的成绩。我费劲找到老师,要知道那时候既没有手机也没有微信。老师说,没有你的卷子啊,没有办法。好吧,只好认了,怎么会没有呢,我挠着头走掉,像是一个吃掉卷子的坏学生。直到我自己做了教师很多年以后,我才意识到,这是教学事故啊,你怎么能把个一心向学热爱诗歌的少年的卷子整没了,还很有理的样子。
他还在课上教我们,”腹有诗书气自华”,看着女同学们这样说。我当时还心有戚戚焉地附和,恩,男生也可以腹有诗书。
老师之不靠谱和敷衍,让我最后终于绝了写诗的念头。引用小于同学的话,看你们都在那里,我肯定不能加入啊。不屑与你为伍的感觉。所以,我坚定了信念,从此走上一条成为理工学生的道路。

6. 补记
放弃诗歌很久,初中的本子再也不动,生了霉斑。宋词词典被压在UML用户手册下面,连积灰的机会也没有。有天有人提到《唐诗三百首》第一篇是什么,他说错了,我脱口而出,不是“孤鸿海上来,池潢不敢顾。侧见双翠鸟,巢在三珠树”么。然后我悲哀地发现,后面四句,我想不起来了。
有一天,我写了一段八句律,发在朋友圈,还配照片什么。关同学在下面回,老师,好工整啊。如果有人夸你写得有感觉有才华有情怀,就跟某些人夸女生有气质差不多,意思是实在难看,而又希望评论保持礼貌。而关同学说”工整”,说明她也有此道中人,真的明白,相当于评论你唱歌”不跑调”,而不是”好听””用灵魂在歌唱”。我突然意识到,不仅仅是我,还有人也在骨子里潜藏着诗的种子。只是,我们竟日在键盘上敲下来的都是用分号结束的句子。
看人家喝酒的时候,徒夫Y老师回顾了几段他为了逼迫儿子而作的诗,每首都是观景,然后立马可取,墨迹未干。多为格律,其中竟然还有六言。我说,六言,你一定写过词吧。他说,我背过平水韵。我喝可乐敬酒时说,如果徒夫Y老师只是学问好,那么我就会觉得他是一位好学者,不过他同时可能是个乏味的人。但是他居然还能做诗,居然还能在喝酒的时候读给我们,那非常之不一样。
我看到《构建之法--现代软件工程》的作者邹欣老师出现在一张照片里,板着脸抿着嘴,挽起袖子,大马金刀地坐在中央,专注地看着眼前的书或者纸张,态度就像review代码。那张照片是人文学者杨早老师的读书会。
我看到导师在抱怨的时候在朋友圈里发了两首诗,文彩熠然。我击手机称颂,原来老师还有这个本事,平时都看不出来,以为他顶多会拉二胡呢,还没有亲见。
我看到你们隐藏在骨子里的诗的灵魂。请不要让它湮没,不要放弃,偶尔让它出来透口气。世事尘嚣,唯超然可以安静。
我们不请你喝酒道别离,而是还在追随。老师加油。有两三员大将横刀立马,当可以定中原。

————————————————————
博客会手工同步到以下地址:
[http://zhuanlan.zhihu.com/younggift]
[https://younggift.net/]
[http://blog.csdn.net/younggift]
[http://giftdotyoung.blogspot.com]

全国教师管理信息系统填报 照片处理,使用 imagemagick






1. 通知正文截选
各位老师大家好!根据教育部要求,我校全国教师管理信息系统填报工作以及开展,请大家登陆http://bwyxjs.emis.edu.cn:8082/selfservice/index。 相关要求,首次填报将于12月10日完成。时间紧,任务重,希望大家支持和谅解,按时完成任务。
2. 照片要求
26mm*32mm, >=150dpi, <60k jpg="" p="">

<60k jpg="" p="">
3. 操作流程
如果看不懂下述操作流程,把下面的内容发给您的学生,请他帮助。如果他说“不会”,请他提高信息化素养,或者使用PS。
3.1 下载 imagemagick
[http://www.imagemagick.org/script/binary-releases.php]

[https://www.imagemagick.org/download/binaries/ImageMagick-7.0.3-8-Q16-x64-dll.exe]
3.2 剪切
准备原始照片。
使用windows画图工具(win+r, mspaint)把原始照片切成 宽:高 为 26:32,另存为 crop.png
3.3 转换
使用imagemagick转换照片为指定规格。
(1) 进入命令行

Win+r, cmd
(2) 进入照片所在目录
f:;cd \20161201
(3)转换
convert -density 150 -units pixelsperinch -resize 154x189 -quality 100% crop.png output1.jpg
4.4 结束
output1.jpg就是符合 全国教师管理信息系统 要求的照片。
4. 解释
如果看不懂上述操作流程,把上面的内容发给您的学生,请他帮助。如果他说“不会”,请他提高信息化素养,或者使用PS。
如果看不懂上述操作流程,本节也不会看懂的。

剪切这一步很难自动处理,需要能AI找到人脸。


4.1 size
厘米->像素 换算
宽度 =26/25.4*150
高度 =150/25.4*32
precise_image_width_in_mm=$( echo \
"$image_width_px / $resolution * 25.4" \
| bc ‐l )
[http://stackoverflow.com/questions/11773688/how-to-change-the-print-size-of-an-image-in-mm-on-command-line]
4.2 dpi
-density 150 -units
4.3 quality
如果文件尺寸过大,可以再压缩。
4.4 unit
可以转换为以公制厘米为单位。此步骤可选,并非必须。
convert output1.jpg -units pixelspercentimeter output2.jpg
4.5 identify
检验转换结果。此步骤可选,并非必须。
4.5.1 英制
F:\20161201>identify -verbose out
 
put.jpg | head
 
Image: output.jpg
 
  Format: JPEG (Joint Photographic Experts Group JFIF format)
 
  Class: DirectClass
 
  Geometry: 154x189+0+0
 
  Resolution: 150x150
 
  Print size: 1.02667x1.26
 
  Units: PixelsPerInch
 
  Type: TrueColor
 
  Endianess: Undefined
 
  Colorspace: RGB
4.5.2 公制
F:\20161201>identify -verbose output2.jpg | head
 
Image: output2.jpg
 
  Format: JPEG (Joint Photographic Experts Group JFIF format)
 
  Class: DirectClass
 
  Geometry: 154x189+0+0
 
  Resolution: 59x59
 
  Print size: 2.61017x3.20339
 
  Units: PixelsPerCentimeter
 
  Type: TrueColor
 
  Endianess: Undefined
 
  Colorspace: RGB
4.5.3 mspaint检验

4.5.4 资源管理器检验
150dpi下,宽度154像素,合26毫米;高度189像素,合32毫米。

5. 后记
据说照片是可选的,不是必填项 😀
PDB老师说acdsee就行。
LXL老师说Photoshop可以。
李记者说用 gimp可以。
————————————————————
博客会手工同步到以下地址:
[http://zhuanlan.zhihu.com/younggift]
[https://younggift.net/]
[http://blog.csdn.net/younggift]
[http://giftdotyoung.blogspot.com]