迟钝的编程

从上周一开始,我接到了一个任务,又是把系统里导出来的数据进行统计,输出结果。我用 Ruby 语言编写了一个简单的程序,来处理 Excel 导出的 CSV 文件。由于时间紧急,我没有仔细构思程序应该怎么设计,直接想到什么写什么,最终写出了程序,完成了任务。

之后的几天,我每天开心的用我的小程序来完成任务。尽管我心里想着为它加上直接解析 Excel 的功能,省得我要手动把文件导出成 CSV 格式。不过尝试了一下 parseexcel 包没有成功之后,我暂时就偃旗息鼓了。然后一直到了这周一,领导找到我,说数据不对,我一看果然差的很多,心里一下子紧张起来。然后手动计算,找问题出在哪里。结果发现原来从三天前,信息中心的人不声不响在系统输出的数据表里加了一列,我的程序当然出错了。于是急忙改正。

之后这周三我因为请假,于是就在周二下班后,把当天的结果给弄了出来。我们公司每天下午 4 点就结帐,停止生成新订单了,我觉得到了下班后来弄这个表应该万无一失(过去是次日得出本日的结果)。岂料到了周三下午,领导找到我说数据又不对。我心想怎么回事儿?结果领导指着一个相差比较大的数据给我看。我一看正好有印象,因为我前一天导出数据后,看到这家分部的数据量特别小,只有不到 20 行——这些数据无论如何不能得到领导算出的 80 多的结果。于是我重新导出数据,悲愤的发现原来下班后 1 小时后数据还没有计算完毕,所以发生了这种错误。

然后领导带着我一起手工算了一下,我在这个过程中发现了,原来系统导出的数据有各种各样的坑,一些不适合的数据也被放了进去。另外,我还发现了另外一种情况,也应该被算入结果之中,结果一开始因为它没有出现过,因此我就忽略了它,导致我得到的结果与正确结果有偏差。

于是,从前天下午下班后,我就一个人留在办公室,思考这个问题。因为我的思路还是一开始的那样,所以陷入了一个死胡同中。一开始我是让程序直接模拟我思考的过程,心里怎么想的就让它怎么运行,结果是它完全模拟了一个人类的处理过程。有了不同的情况之后,程序需要判断各种不同的情况,按照这个思路,最简单的方法是程序在读取完一遍数据文件后,再重新读取一遍,用另一种方法来处理数据,最后把两次结果加起来,才得到最终结果。想到这么麻烦,我就感觉一阵绝望——就算我最终写出了程序,我也不敢确保它能正确执行,要调试起来也很麻烦。

到了昨天,我想预期要多次处理数据,不如把这些数据一股脑儿读到内存里去,开始时我觉得这样是一种折衷方法,后来慢慢的发现,这应该是处理问题的正确方法才对。我用两个哈希表,每个元素对应一个列表,来存储从两个视角来分析数据的结果,然后迭代每一个元素对应的列表,进行简单判断,然后就是加法运算计数了,整个解决问题的思路一下子变得如此清晰!

写完了程序之后,我略微反思了一下,其实程序本身没什么复杂的,只是需要把所有的可能情况考虑清楚了,这是最关键的地方。但说来容易,执行起来可不容易。在正常的软件开发中,TDD 是解决这个问题的良药。可我时间紧急,也没有经历来考虑所有可能发生的情况,这其实就给这次开发带来了弯路。另外一个教义,是不要害怕推倒重来。如果我到后来一直坚持一开始的思路,没有变更思路的话,我想就算我最终完成了程序,写的过程应该也非常难受。有趣的是,这样的一个教条,在《人月神话》中已经被提及了。如果正却的开发方法都无法被实行的话,我想最重要的一点,就是要保持冷静,让头脑放空、清晰,是写出正确代码的不二法门。

当然,Ruby 这样的快速开发语言帮了我很大的忙。我想,如果让我自己手工写哈希表,我一定不会这么快的找到解决问题;哪怕是 Java 这样的语言,有自己的数据结构库,使用起迭代来那繁琐的步骤,也很容易把我给绕迷糊了。

重拾 Perl

最近,我开始重新学习 Perl,尝试用 Perl 来完成日常的开发程序。

原因是前几天听第 27 期内核恐慌时,主持人提到了 Perl 6。然后吴涛说起 Perl 时,感情很复杂。我当时心想,我对 Perl 的感情也很复杂咯。首先是对今天的程序员来说,Perl 太原始了,与 Python 和 Ruby 等语言相比,更显古老。回想起来,自从我学习了这两种语言后,我就再没有正儿八经用 Perl 写过程序。而我真正有了属于自己的电脑,是在 2007 年,那个时候我正好开始学习 Python,之后就没有碰过 Perl。

而另一方面,Perl 又是我的启蒙导师。具体的科目不好形容,与 UNIX/Linux、开源等世界有关。在接触 Perl 之前,我对编程,还停留在 BASIC 语言、C/C++ 语言、Pascal 语言上面,对于脚本语言,我的理解只是 .bat。在当时的教科书上,获得的结论是编译型语言比解释型语言在执行上各种优秀,解释型语言只有在学习时才有优势。UNIX 世界繁荣的脚本语言与著名的 KISS 原则,我则一概不知。

记得我大概在高中的时候买过一本小册子,第一次让我接触了 Perl。小册子的内容是 FORTRAN、Pascal、C、Perl、PHP 这五种语言的诞生历史,正是我感兴趣的内容。FORTRAN 那一节看的我比较枯燥,Pascal 是参加 OI 的语言,我比较熟悉。C 我虽然没怎么入门,但也接触过,于是就一章一章的看下来了。Perl 的这一章,是 Larry Wall 写的历史,用女孩子的成长周期来形容每一个版本的 Perl,语言相当幽默风趣,浅显易懂,还有 Larry 在设计 Perl 时的心路历程。虽然我还没有读过一行 Perl 程序,但看完了这一章节,我对 Perl 有了极大的好感。我喜欢的,就是这种不羁的风格。务实、能用的思想,再加上一些宅男一样自娱自乐,不管旁人眼光的态度,让我十分痴迷。

后来,我在新华书店买到了一本《Perl 语言入门》,一本薄薄的小册子。虽说《Perl 编程语言》才是真正权威的教材,但我还是抱着试试水的想法,买了更薄、也更便宜的“小骆驼书”。事实证明,这对我来说是一个极端正确的选择——这本书实在是太棒了!哪怕到了十多年后的今天,我还是认为这本书是编程语言教材中的 No. 1!或许是这本书的作者们都是常年培训 Perl 的老师,他们讲起 Perl 来头头是道、语言风趣、深入浅出。虽然我在阅读的时候,手边没有一台电脑给我来实践,但随着阅读和书中的示例,我很容易的就理解了书中的内容。书中用虽然没有涵盖 Perl 的方方面面,但其中 Perl 的 20% 的内容,就可以解决 80% 的问题。同时,书本的内容引起了我的一些思考,以及计算机世界眼界的变化,带给我的影响是巨大的。

另外,之所以说先看“小骆驼书”是个极端正确的选择,是因为后来我出于对 Perl 的喜爱,还购买了更贵、也更厚重的“大骆驼书”,也就是《Perl 语言编程》。不知道是书的本来风格与我不合的原因,还是翻译水平的原因,我总是无法对这本书产生喜欢的情绪。到最后还是把它当成了 Perl 的 Reference Manual。

出于对于 Perl 的这种复杂的感情,当我听吴涛给没有接触过 Perl 的听众介绍说“Perl 是一种‘只写’的编程语言”的时候,我忽然想到了,我日常工作中要分析的数据,虽说我用 Ruby 和 Java 分别实现了相关版本,但时常又被告知需要其它方面的结果。与其这时候再来该程序,不如尝试用 Perl,来快速的写一些只写的脚本来工作?

于是我这个周末开始了尝试。因为已经很久没有碰 Perl 了,我只好找出了《Perl 语言入门》来,从头看起,然后通过运行示例程序,来恢复过去的知识。有了一点心得后,就马上尝试来写我日常需要的一个功能。现在我发现,Spreadsheet 这个模块很有用,用来分析我们系统导出的 Excel 表中的数据,可以比较方便的提取数据,而且写起来比 Java 上的 jxl 模块要简单方便的多。

只是时间长了,有些东西还是很生疏。比如我需要用到 switch 语句,就去搜索语法,得到的结果居然是 Perl 核心中没有 switch!然后有用其它技术来实现的,还有一些模块。确实是 Perl 的“怪”的体现。不过我还有时间,这个过程也很好玩就是了。

最后,对比起 Python 和 Ruby,我觉得 Perl 有更强烈的“顺手”这个特性。它没有很完美严格的“世界观”,不会执着于把一切都弄的很漂亮,它只是你手头的一件趁手的工具,甚至你用完了随意一丢也不怕摔坏。在 Perl 里,class 关键字可没有其它语言中这么方便。

歧路?

昨天要写一个简单的 Ruby 小程序,来快速的对一些数据做出统计。之前用的 Atom 来写的 Java 程序,缩紧方面的设置还需要重新调整。我觉得有些烦了,就干脆关了,用 Emacs 来写。

我的电脑上一直装着 Emacs,结果不成想,可能是因为近期升级了操作系统版本的缘故,在 Emacs 里的汉字字体都变成了隶书,看起来太别扭了。我于是赶紧搜索字体设定办法,结果要编辑 elisp 了,却发现我竟然需要从 org-mode 的文档里进行调整。后来,我想上 USENET 上看看,发现 Gnus 的设定也没了,再想重新设定,却发现也不算容易。最终,我还是解决了问题,但却让我觉得,之前的一些 fancy 的东西,也许对我来说是一种歧途?

之前我用了 Mac OS X 后,同时也渐渐的从每天写程序的日子里退了下来,渐渐的觉得既然用了 Mac 系统,那再用 Emacs 也许有点不合时宜——毕竟从界面、快捷键上,都与 Mac 系统不大一致。而且,我又看了一些关于 Sublime Text、特别是 Atom 等编辑器的文章,觉得也想试试这些东西,于是在一段时间里,我有意的使用这些编辑器来工作,包括编辑文档还有写程序。

但是,我渐渐的发现,我也许已经过了可以随便学习一个新的编辑器的阶段。没有了学习一个新的编辑器的热情。想起在高中阶段,我对 Emacs 的痴迷劲儿,常常读相关的文章,对着其中的配置来弄自己的 .emacs 文件,每知道了一个新的功能,我就可以兴奋好几天。从那个时候,我一直把 Emacs 当做自己的首要编辑器。这也让我放弃了学习其它编辑器的机会,比如 Vim,我虽然会用,但没法把它用好,真让我用它来写代码,我也真做不到。所以,其它的编辑器,我都只是停留在试试而已。

刚开始是 SublimeText,很多人把它捧得很高,因为它是用 Python 写的,Python 程序员可以自己来扩展。它也同样有一个扩展机制来扩充功能。后来有文章又把 Atom 捧上了天,说是很了不起的创意,用了 web 技术来写编辑器。等我用了下来,我发现除了是用 web 技术来渲染文档之外,其它的跟 Emacs 的差别,从思想上来说,不算太大。不同的是 Emacs 使用 ELisp 语言来扩展功能,而 Atom 用了 Nodejs。不过我想既然 Nodejs 这些年来大火了一把,我跟着这个潮流来学习一二,也挺不错。后来终究因为时间忙、注意力差而没有坚持下来,反而让我更加的思念 Emacs 了。

今天我重新打开了 Emacs,莫名的发现中文字体变样了。过去正常的字体,现在成了隶书。不是不能看,但是在是别扭。当中我升级了操作系统,估计跟这个有关系。找到了设置默认中文字体的方法,把默认字体设置成苹方,能正常显示了,可重启 Emacs 后就又回到了原样。我着手进行修改,可发觉原本觉得真了不起的 Emacs Starter Kit 也不太好用。模仿高德纳的文学式编程,实际进行修改起来,一是文档造成了一定的干扰,不如直接面对代码方便。二是编辑器有了一定的偏差,原本可以轻易的进入一种模式来编辑块中的代码,但现在配置有了偏差,这个功能失效了,代码一下子变得比较难看,最终勉强的才修正了代码。

到了晚上,我因为启动了 Emacs,就想起了 ERC,想上 IRC 上去看看。上了一些过去的讨论组,发现在2012年还非常繁荣的 #gentoo-cn、#ubuntu-cn 等,现在都门可罗雀,没有人上去讨论了。也就是英文群组 #emacs 还算有人发言,让我一阵忧伤。之后又想起了那时上新闻组的日子,于是想上去看看,才发现我的 Gnus 还没有配置。想着重新配置,Emacs Starter Kit 里 Gnus 那一部分又让我挠头了。

我开始觉得用 Emacs Starter Kit 对我来说是一种歧途。原本我自己的 Emacs 配置文件长年不用变,无论怎么升级都可以用的好好的。但 Emacs Starter Kit 给这一切增加了复杂度,有一些我不知道怎么运作的代码,不搞清楚它们,我在修改功能的时候,就非常的受到限制。我决定要重新整理相关配置,弄成之前的那个样子。可之前的配置文件不知道被我备份到什么地方去了。想自己重新写,路径那一关就是个问题,我花费了两天业余时间,才把它配置的跟过去相似。要想恢复过去的样子,真不知道还要多少时间。

我想,我在之前的那条歧路上,走的有些远了。像一些便利的东西,偶尔玩玩可以,但因为它而荒废了自己的根本,在长远上看有些得不偿失。

Java and Ruby

我的日常工作之一是统计非法走私信息,在国家级的行业信息系统上有一些数据,但不是我们要的格式。我们要把每个走私案件统计下来,包括走私的产品、数量、案值金额、还有涉及到哪个下属单位等信息。过去用眼镜跟手再加上 Excel 来整理实在是太麻烦了,于是我就写了一个 Ruby 程序,把导出的数据整理成我们需要的格式,输出成 HTML,大大减轻了工作量。

不过这个程序是在我的 MacBook Pro 上写的,而我们的信息系统只有内网能访问。每次使用的时候我只好导出数据,转换成 CSV 格式,然后用电子邮件发给自己,从 MacBook Pro 上收取邮件,转换编码,转换成 UNIX 格式,然后再运行程序,把生成的 HTML 页面再通过电子邮件来发回去,再进行整理。这样一来很繁琐,而来这样只有我自己才能干这件事,我休假时也没法委托别人去做。

所以前几天我考虑了一些比较适合在 Windows 上使用的编程语言,把我的 Ruby 程序移植过去。最后我选择了 Java,因为它比较容易在 Windows 上安装,而且我之前上学时还做过两个 GUI 程序,我也希望把程序做的友好一点,可以推荐给同事用。

我没有进行别的什么设计,只是简单粗暴的对照着之前的 Ruby 程序,逐行翻译过来。断断续续的调试了两天,目前弄了个大差不差的版本,只是这次我的输入输出直接是 Excel 文件,省下了转换格式、编码的步骤。我对于操作 Excel 还没有了解多少,所以还有些合并单元格、修改字体、宽度高度什么的需要加上,其它数据方面今天总算是调试成功了。

谈谈使用 Java 的感想。

我在 2010 年的时候写过一篇文章,记载着是一次用 Java 语言完成作业的经验,我那时候花了一夜完成了那一周的作业,实现了几个数据结构并且排序。那时候我用 Java 还比较熟练,有些东西不需要像现在一样查文档,结果没怎么调试居然成功了,让我对 Java 好感大增。不过这次跟 Ruby 比起来,Java 就显得太繁琐了。

过去我对 Java 的一些比较“高级”的用法不大了解,因此一些迭代器呀什么的也用的不多。这次因为在 Ruby 程序里用了很多迭代,感觉比自己苦兮兮的写数组来说方便太多了,因此就用了不少的迭代,结果每次新建一个迭代器实例,再用 hasNext()next() 函数来遍历,比起 Ruby 的一个 each 就搞定实在是繁琐。这样的另一个后果就是多了一堆代码,我在 NetBeans 里面看的眼都乱了,调试的时候直接找不到头绪。最终搞定了我是觉得没问题的,但是搞定前费的功夫实在是大。所以近年来,要完成一些任务,我绝对是偏爱脚本语言。

写到这里,不禁感叹,如果我们公司用的是 UNIX 操作系统该有多好,可惜绝对不现实呀。

过去对这方面的感触不深,但通过对比就觉得 UNIX 的编程方式实在是太方便太先进了,比起 Windows 的模模糊糊好了不知道多少倍。可惜 Ruby 在 Windows 下执行不大方便,做成可执行的 EXE 就更不容易了,简直没有头绪。

这个程序目前的输出方面,还有一些功能需要添加,不过这个 Excel API 弄得我写起来还是不顺心,也许有一个更好的用法,反正正常的写程序绝不应该是像现在这样遇到输出就一个单元格一个单元格的写,运算逻辑和输出逻辑都没有分开,之后要加点功能,处理其他格式的数据,肯定远比我过去用 Ruby 输出 HTML 时来的困难。

计划重新配置 Emacs

Emacs 是我最喜欢用的编辑器。最早我是在大约 2005 年从王垠的网站上知道它的,同时听说的还有 VIM,后来经过尝试,我觉得 Emacs 更加符合我的口味,用起来更为自然,因此从很早起我就用 Emacs 来写程序。

我最早的 .emacs 配置文件的内容也是从王垠的文章中获得的,王垠为此写过两篇文章,一篇是基础的配置,另一篇是讲各种好用的 Elisp 包,我从两篇文章中选取了我需要的内容,放进了我自己的配置文件中。这个配置文件我用的非常久,中间也经历了我从 Windows 到 Linux 到 Mac OS X 这个操作系统替换的过程。

后来我得知了有 Emacs Starter Kit 这么一个 Emacs 配置的项目后,立马对其动心了。主要原因是它使用 Org Babel 来把配置文件写成了 Literate Programming 的形式,可以在配置文件中大段的写入对于某一部分代码的想法,而 Org Babel 会在启动时自动提取出所有配置的代码,非常方便。Starter Kit 还有一个好处,就是它把很多相关的配置提取出来,放进了不同的文件中。我过去的 .emacs 是一个大文件,所有的东西都在里面。Starter Kit 则不是,它给了很多模块,需要的时候可以调用。这样的配置文件更加的清晰了。

不过 Starter Kit 也有让我不满意的地方。首先它对我来说有点大,很多配置在我看来有些过了,反而让我一下子有点措手不及。在我看来 Emacs 主要还是一款编辑器,写项目的时候可以有些项目管理的功能,但并不意味着它就要做一个 IDE。我更喜欢比较简单的东西,够用即可,过犹不及。另外就是它的效率问题,它让我的 Emacs 启动速度减慢了许多,今天我又往里面加入了一些 Ruby 相关的配置,速度又一次变慢了。今天我也看到了这篇文章,讲优化 Emacs 配置的,对我很有启发,里面很多的东西在我的配置文件中还没有被用上,于是我计划要整理一下这些配置。

我开始的想法是采用 Starter Kit 的思想,但不要直接用它的代码。首先我很喜欢文学编程,尤其是对于 Emacs Lisp 这种我不能完全掌控的语言来说,有了相关的文字解释可以省去我很大的功夫。另外,这样做的好处是我可以真正的掌控 Emacs 的配置,目前 Starter Kit 中又好多代码我不知道是做什么用的,但又不敢胡乱修改,只好让它们运行。这次借着重写的机会要好好筛选了解以下。

不过 Starter Kit 有一些好的设计,我还没有把握能自己实现出来。比如说它对于第三方插件的管理,我还需要看一下它的代码。另外,Starter Kit 自己也在演进中,我自己配置就意味着要与 Starter Kit 社区断裂,不知道是好是坏。总之目前我有这个想法,但是否能实现出来,我还有些惴惴。

服务器的文件不知不觉的被人动了

今天早上偶尔访问我的服务器上的文件时,发现当我访问一个 HTML 文件 时,Chrome 浏览器提醒我这个页面有不良程序。

Chrome 浏览器发出的警告
Chrome 浏览器发出的警告

我第一个反应是“怎么可能”,这明明是我自己的服务器,文件也是我几年前自己编辑放上去的,有了木马我自己怎么可能不知道。不过为了以防万一,我用 SSH 登陆了服务器检查了一下,没想到这个文件里确实有不明脚本。

不良脚本的内容截图
不良脚本的内容截图

这个文件很老旧了,早在我还在用 Dreamhost 共享空间的时候就已经存在了。中间我的帐号也被黑过,因此有些文件被加了木马也是有可能的。这次这个脚本是用 JavaScript 写的,放在了 </html> 的后面,用 DOM 语法在页面里插入一些内容。具体作用是什么我不清楚,因为脚本里加了一些乱码来变换真实内容,我也没有兴趣去仔细研究它。

我检查了一下其它文件,发现没有问题。

升级了 tweets-page

前几天偶尔上 Twitter 的时候看到别人的一条推,说是 Twitter 可以导出自己过去发布的所有消息了。这是我一直想做的,原因之一是 Twitter 自己限制了它是一个快速更新的消息发布集中地,而已经发布了的消息就不是它所关注的了。所以过去你要是在 Twitter 页面上不断的点“上一页”,能看到的页面是有效的,印象里好像回退到 20 几页就不能再往前看了,不知道现在有没有改进。因此我很早之前就有把我所有的 Twitter 条目都导出来的想法,可惜 Twitter 的 API 也有类似的限制,因此这个想法一直没有实现。

第二个原因是让我在国内的父母可以看到我的推。由于我经常发推,因此从我的 Twitter 记录中可以看到我的生活。这样我父母能更放心一些。

我写 tweets-page 是从 2009 年 7 月开始的。那个时候我刚刚回国,Twitter 已经被封锁了。当时我和家人在北京的饭店住着,父母正好有假期,准备带我在北京转转,看看鸟巢等新建设的地方。我因为时差关系到凌晨三点多就睡不着了。自己住在一间客房里没事干,又不想看电视,于是就想解决从国内上 Twitter 的问题。一开始时我用 ssh -D 给自己开个通道用,一会后嫌麻烦,因为代理的速度不高,一些国内的页面用代理会更慢,而且每次开代理还麻烦(那时我还不会 screen)。所以我就自己写了一个程序,让我可以浏览 Twitter 并发布新推。当时的代码怎么写的我早就忘了,好像也没有备份。浏览了一下当时的推特记录,说是用的 Perl 调用 curl 来用官方的 API 来更新,印象里时一个 CGI 小程序,非常简陋,还有不少的问题,当时仅仅是临时用用。

然后回加拿大后,我用 Ruby 写了一个小程序,通过 twitter4r 来获取我的 tweets,保存在数据库中,然后用程序生成页面浏览。API 在那时已经不支持 since 参数了,我只能从某个时间段开始抓取,之前的内容就只好放弃。程序最终写成了,不过我当时对服务器的知识太少,用的还是 Dreamhost 共享主机,最终用 Crontab 让程序定时运行的时候,发现无论怎么样都找不到我用 gem 安装的 twitter4r。如果程序不能定时运行,我每天要登陆到主机上手动执行一遍程序?非常不现实。所以最后我只好放弃,最后的生成结果部分我也懒得弄了。

印象里之后还试过几次,好像都没有成功。

到了 2012 年 5 月,我重新写了一次,结果弄成了。这次用了 Twitter 库和 Sequel,在服务器上我也不再用 SQLite 了,而是用 MySQL。除了保存到数据库之外,它还能在刷新后立即生成输出页面。输出的页面是一个比较大的 HTML 静态文件,我曾经想写一个分页功能的,后来一直没有兴趣碰它。在解决了 Crontab 的问题后,它就一直在我的服务器上工作着,一直到了现在。

当我看到了 Twitter 可以导出的消息后,我就导出了我的记录。看着一个几兆大小的 CSV 文件,我有了把它们添加进我的数据库的想法。于是就尝试了一下,中间也遇到了一些小问题。

开始时我觉得 CSV 文件没有什么难度的,于是就直接读取一行,用逗号拆分。但输出的时候我发现了几个空行,查看了一下原来 CSV 文件并不是严格的每行是一条记录的。记录中有的值有换行符,体现在 CSV 文件中就是直接的换行,我按行处理就会出错。我当时的想法是不要这些有问题的条目了,不过它们太多、太分散,我没有完成。之后我用了 CSV 库,它没有任何问题处理了换行。

第二个问题是时区。导出的 CSV 文件中的发布时间是用的 UTC 时间,看上去是这样的 2013-03-12 01:35:28 +0000。我要把它转换成我所在的时区,于是就做了各种尝试。或许由于 Rails 太火的原因,我搜索到的很多条都是如何在 Rails 里完成的,而不是 Ruby。最后经过尝试,我找到了解决方法。

第三点是如何插入数据库的问题。我开始的想法是直接在 CSV 文件中把我已经保存了的条目删除,然后运行一遍我的程序。现在想想这种方法其实也行,弄进数据库中再排一下序就可以了。不过我当时是直接新建了一个表,导入到这个表后检查结果正常后,就把表改名,跟过去用的表换过来,然后我就有了一个比较大的数据表了。

这次我不想生成静态页面了,为了练习,我改用 PHP 写了一个动态页面。我过去从来没有写过 PHP 程序。之前有想过要学一下 PHP,当时的想法是读别人写的代码,反正 PHP 的语法也不难。但是我悲剧的选了 WordPress,因为它是最流行的 PHP 软件之一。我现在对 PHP 的了解不深,对于 WordPress 的代码写的好坏没什么发言权。不过我觉得 WordPress 的代码非常非常非常不适合初学者用来学习模仿,非常不清晰,几下子就能把人绕晕了。所以我到现在都没有认真学习过 PHP。不过 PHP 在我印象中不难,当然不包括它面向对象的部分。我从网上找了一些它跟 MySQL 合作的介绍,选择了 PDO 方式来操纵数据库,写出了不到一页的代码,很容易的就完成了任务。

新的页面在:http://liuf.net/tweets/

当然这个页面目前其实是比较糟糕的。动态页面每次都要生成,这么多条目导致了这个页面的速度比较慢,要将近 10 秒才能完成。所以有机会我一定会给它安上分页功能,或许加点 JavaScript 来动态载入。

旧的代码我还没有修改,其实我想把它生成页面的部分去掉,单独的干刷新数据库功能。等有机会再说吧。

生成的代码我还没有往 Github 上传,因为当中我直接把数据库的用户名和密码写进代码里了,估计从 git 里回滚几下就能看到。虽然我的数据库仅限 localhost 访问,但还是再考虑一下吧。不过代码库的链接在这里

生成 Twitter 静态网页

我从 2009 年就想写一个程序,把我的 Twitter 记录生成为静态的网页,放在我的服务器上,可以让墙内的人浏览。这样,在国内的父母也可以像 Twitter 被封锁前那样通过我的 Twitter 来了解我的生活情况。

当时我完成了一个小程序,但仅仅是做实验用的,有相当多的小问题。当时用的是 twitter4r 库,很多东西都不大成熟,用它写程序挺麻烦的。当时也遇到了各种问题,都在上面的文章中描述过。后来的代码总结在这篇文章里

最近我看了一些关于 Rails 的文档,写了一些程序,受到了一些启发,于是觉得可以重新写一写这个程序了。现在很多东西也变了,有了 twitter 库,获得记录的方法也更方便了。我如今也不再执着于把所有的 tweets 都保存下来了。另外,我对于数据库方面的了解更多,所以没有干巴巴的用 sqlite 接口来写 SQL,而是用了 Sequel 库,在本地用 Sqlite 3 调试,在服务器上就用 MySQL。结果程序很快就出来了,在纸上做设计,把要做的步骤考虑清楚了之后,很容易的就完成了代码。当中遇到的困难之一是对 Ruby 的不熟悉,很多东西我都需要查找例子,这是练习不足的原因。困难之二就是在 Sequel 的文档搜索信息费了我很多功夫。很多东西都不能想当然,我需要摸索着试验。比如说查看一条 tweet 是不是已经在数据库中有了,只要找找有没有相同的 id 就可以了,这个操作就让我费了很长时间来试验才知道正确的用法。

我过去从来没有写过 MySQL 程序的经验,因此在服务器方面也遇到了问题。首先是 MySQL 本身的启动问题,这个放在下一篇文章中专门谈,其次是安装 mysql 的 gem 时的问题,系统在哦年时提示:

*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

只是说缺少依赖,但并没有说具体少什么。后来我找到这篇文章,知道了原因,安装了 libmysql-ruby 和 libmysqlclient-dev 库之后就没问题了。

最后是让程序自动运行,我用 crontab 完成。但我平时很少用到这个,因此从网上找了配置的写法,让它每 12 小时运行一次,但现在还不知道我的配置写的是否正确,等到了凌晨的时候看看运行情况。

最后,程序代码在我的 Github 中:link。生成的页面在这里这里这里,我没怎么做美化,毕竟我不擅长这个,等有时间再看看。

OS X 10.6 的 AppleScript 中文 bug

前几天一个偶然的机会我发现了 Shawn Blanc 的 blog。这个 blog 包含了一些时尚科技的评论,更新速度比较高。作者 Shawn Blanc 目前是专职维护这个 blog,blog 本身有收费会员订阅的项目,每月花 3 美元就可以阅读一些更多的内容。但免费的内容已经非常丰富了,作者有这个底气征收付款,在中文 blog 圈子中应该是比较难以想象的吧。

我从这个 blog 中看到一篇 MarsEdit 的评论,因为我自己也在用 MarsEdit,它是我的计算机上的几个花钱购买的商业软件之一,于是我就花了点之间仔细看了一下作者的评论,从中知道了我之前一直以为不存在的字数统计功能,竟然在脚本菜单下面。我知道自己还不是一个比较资深的 Mac 用户,比如像 mac script menu 这样的菜单我从来没有点过。这次知道在 MarsEdit 这个菜单下面,就有对文章的统计功能。于是我点了统计功能后,MarsEdit 的 CPU 占用率马上飙倒了 99%。最后只能把这个进程给杀掉。杀死进程后 MarsEdit 有崩溃报告功能,我就顺便把这个 bug 给报告了。

Apple 本身也有这种功能,不过可能用户量太大,Apple 应该只把它当作一个记录统计的作用。而 MarsEdit 的开发公司 Red Sweater 这种规模不大的公司,收到这种报告的次数可能不多吧,反正我第二天就收到了 Red Sweater 的回信,请我帮助他们 debug。我把造成问题的那篇文章发送了过去,因为我的文章是中文的,我以为 MarsEdit 可能对多语言支持的不够完善,就提了一句是不是 Unicode 方面的问题。

之后对方发来邮件,包含了一个另一个版本的字数统计脚本的附件,让我用这个脚本来试试。我试了一下,仍然有问题。脚本是用 AppleScript 写的,我从来没有学过这门语言,但据说它非常简单,脚本本身也不长,我看了几眼就大体上知道是怎么回事了。稍微研究一下,我想找出来是我的文章的哪个部分让这个程序的哪一行卡住了。于是排除了一下,发现造成问题的原因比较古怪,也不是所有中文都通不过,我发现的一种情况是只有字符串中包含一个汉字,跟上一个空格,再加上一个数字的时候,words of inputString 就会卡住。inputString 就是输入的字符串,脚本中用了 number of words of inputString 来获得字符串中包含的字符或汉字。我的那篇文章中正好有这种情况。实际上,我从几年前写文章就习惯再汉字跟数字及英文字母之间加上空格,这样更清晰一些,所以我的 blog 中一定会出现很多这种情况,我的大部分的文章都没办法用这种办法来统计字数。

我把我的发现发给了 Red Sweater,对方回信告诉我说这个问题在 OS X Lion 中不存在,因此似乎是 Mac 系统本身的问题。出问题的代码仅仅是调用了系统给的函数,因此也不好解决。对方说他们会想别的办法来获得字数统计功能。

另外,我还询问了对方能不能加上一个实时的字数统计功能,在编辑窗口中显示。结果对方告诉我目前 MarsEdit 已经有这个功能了,在状态栏里有显示。只是默认情况下状态栏是隐藏的,但对方担心他们做的统计功能可能不适合中文,因此他们也询问了我对于中文字数统计的要求。我看了对方的说法,他们或许是因为英文统计的是单词而不是字母的关系,所以考虑做中文分词,统计文章中出现的中文词组。我一看这可差老鼻子远了,中文分词本身就发展的不怎么样,还让老外来做这个,猴年马月才能弄出来啊,更何况中国人看字数统计也不管里面用了多少词组啊。所以我赶紧发邮件说别搞这个了,我们关心的仅仅是字数。

之后,不知道什么时候可以解决这个 bug。老外对于中国语言,大概从文化上就和西方语言有差异吧,我用过一些编辑器,里面也有字数统计的功能,但对中文来说都是没法用,因为它们全部是针对英文的单词设计的,字符之前没有空格就认为是一个单词,比如说这篇文章写到这里,MarsEdit 告诉我一共有 173 个 words,这就差的相当远了。不知道是不是所有写软件的老外都有类似的误会,考虑做什么分词,要能统一的给他们纠正过来就好了。

删除数据库中的换行符

早先我的 blog 是用 WordPress 搭建的,后来我想尝试一下 Movable Type,就把所有文章都导入到 Movable Type 里了。之后我对 Movable Type 失望了,就把 blog 系统换回了 WordPress,但在导入过去的文章的时候,发生了一些问题。

其中最主要的就是所有的自然段都被合并到了一起,生成的文章全都成了一大坨,基本上无法阅读。当时我对网站、数据库之类的知识了解的不大透彻,于是想出了一个笨办法,在该换行的地方插入 br 标签,这样算是变相的解决了问题。后来我想想,可能未必是 Movable Type 和 WordPress 格式不兼容的问题,导致了两者互相导出导入会有问题。应该跟我使用了 Markdown 插件有关系,毕竟插件是第三方的,也没办法精确控制。

当时我还写了几行 Ruby 小程序,判断在什么地方改加换行标签,把 Movable Type 导出的文件给扫了一遍,把输出的文件导入到 WordPress 中就可以了。但这也带来了一些其它的问题,比如当一篇文章里有引用的地方,如果在换行处都有换行标签,那么 Markdown 会一直把引用开始时到文章结尾的部分都当作一个引用,这明显不对。为了解决这个问题,我也不愿意在 WordPress 里不断的导出导入,还要删除旧的文章,于是就选择了一个更笨的方法——平日里随机选一篇文章,进入后台的编辑,然后手动去除换行标签。用一个编辑器的查找替换来弄其实还挺快的,顺便也可以修改一下有问题的 tag。时不时的修改几篇,总有一天可以完成吧。

昨天晚上在看一个 WordPress 主题制作的牛人 zww 的 blog 的时候,看到了这么一篇文章:《闲来蛋疼: 把所有文章的more标签去掉了》。在文章里作者提到了用 SQL 操作数据库,把他的文章里的 more 标签给批量的删除掉。里面仅仅用了一行 SQL 语句就把所有文章的标签给删除了。我一看大喜过望:zww 要删除 more 标签,我何尝不想批量删除我的文章里的 br 标签呢?把他给的 SQL 语句略作修改,我很容易的就用下面的这条语句删除了旧文章里的换行标签:

UPDATE wp_posts SET post_content = REPLACE(post_content, '<br />','');

zww 用的是 phpMyAdmin 来执行 SQL 语句。我用了 Nginx 之后就没有在 VPS 上安装这个工具,因此只能通过 MySQL 的命令行客户端来执行这条语句。我用 mysql blog -p 进入了命令行客户端之后,由于太长时间没有在后台设定数据库了,竟然有了一种茫然的感觉。想了半天才渐渐的回忆起了一些命令,真是让我深有感触。看来这些东西还是得靠熟能生巧啊。