邓世龙的自留地

兼济天下则达 独善其身则穷

分类目录归档: 编程

迁移Wordpress到Django


终于买了一台Mac电脑,发现Mac电脑确实不错。许许多多的快捷键,可以提高效率。很方便的一点是,每次开机会启动到上次关机的状态,保证工作的连续性。而笔记本方便携带,即便回老家也可以继续写代码。

最近一个多月,陆陆续续完成了从Wordpress迁移到Django,还是很高兴的。之所以离开Wordpress,一个重要的原因是对PHP不熟悉,想对Wordpress进行修改时总感觉力不从心。虽然对Django也不熟悉,但毕竟Django是用Python写的,而我对于Python的熟悉程度远远超过PHP.

于是开始动手,其实2014年的时候就想迁移过一次,只是后来不了了之。现在重新动手,还是有些基础,而网络上也有许多用Django搭建博客的例子。渐渐的功能一步步完善,也就成了现在这个样子,代码地址https://github.com/dengshilong/dengshilong。以下是一些体会。

  • github和StackOverFlow这两个网站确实不错
  • 对于搜索引擎而言,百度和Bing做的确实太差了,与Google相比确实有天壤之别
  • Virtualenv, Supersivor, Fabric这些模块真不错
  • 最后,感谢Wordpress博客系统,它确实太优秀了。

下面是一些参考链接:

MV小站开发手记


很久很久以前,看《康美之恋》的电视广告时,就被它唯美的风格所吸引,之后也看过一些类似的MV,于是想做一个网站来记录下这些MV。之前学了Django后,尝试用来做一个博客系统,并有了一个雏形。有了这个基础后,顺手把MV小站也做好了。

花了一个上午的时间,就写好了。功能不需要太多,只要能记录MV就行,这都是得益于Django的后台功能,而MV的视频源都是来自优酷。不想写评论功能,因为这样的话,代码就需要更多了,于是用了社会化评论。

MV小站可移步至http://lemonbean.info/

欧拉工程中文站V1.1版完成


真没有想到,距离上次更新已经将近三个月了,看来真的是越来越拖拉了。

记得上次说,将使用MVC来重新设计网站,添加评论功能,RSS功能。后来发现,写一个MVC框架也没有那么容易,还是用Smarty相对容易一些,于是用Smarty来实现数据和显示分离。

对于评论功能,一直纠结是否要实现像Wordpress那种嵌套评论功能,写着写着,发现嵌套功能也没有那么容易,于是只好实现最简单的评论功能。而对于评论,本来还打算实现分页功能的,而在我看来,实现这个功能一共有两种办法,一种是重新刷新整个页面,另一种是使用Ajax。使用Ajax的体验相对好一些,可是我又发现,实现这个Ajax功能也并不容易,后来又考虑到网站那可怜的访问量,评论根本上不去,所以也没有必要实现这么复杂的功能,于是干脆连分页都不做了。而对于RSS,访问量上不去,这功能估计也用不到,先不写了,一切从简吧。

这次改版,一个最大的变化还是左导航栏,有了这个问题题目的导航栏,看题目内容就很快了。这还得多谢颖妍同学。

如果感兴趣的话,可移步欧拉工程中文站

欧拉工程中文站v1.1

欧拉工程中文站第一版完成


知道欧拉工程,是在任晓祎的博客里,之后断断续续也做了一些题目,并被数学之美所折服。其中的许多问题,如果没有一个合理的解决方法,即使高性能的计算机也无能为力。典型的一题是素数的判断,记得微软实习面试时也问过这道题,当时还以为很简单,没想到这后面还隐藏着米勒拉宾算法,并且竟然会与费马小定理搭上边。

但是这个理由还不足以需要自己做一个网站,以及将题目翻译成中文。之所以这么做,出于两个原因。第一个是,想学习怎么翻译,提高英文水平;第二个是,学习如何做网站。

高三的时候,一次英文课上,老师叫我和几个同学扮演一个采访节目,其中我扮演阿迪力,也就是那个走钢丝的特技演员。当时我的口语水平很烂,所以放弃这次扮演,于是老师笑道,阿迪力不懂英语啊。当时很羞愧,学了五年的英语,口语竟然这么烂。但同时,我也觉得这个老师不好,怎么能这么打击学生呢?事实上,我的英语阅读水平一直都很不错,即使读起原版书籍也毫不吃力,只是我这人生性腼腆,不常开口说英语,以至于口语很烂。后来学日语的经历也证明了这一点,日语老师韩凌燕很耐心的教我们从发音开始学起,而当时自己学的很努力,最后日语的发音也还不赖,事实上,在普通话,英语,日语中,我的日语发音是最标准的。而为了证明我还是懂英语的,后来看了许多英文书,英语的阅读水平显著提高。为了学以致用,决定翻译欧拉工程,一方面锻炼英语,另一方面,也可以加深对题目的理解。

事实上,不会做网站一直都是我的一个心病,所以能够自己独立做一个网站一直是我的一个愿望。两年前找实习时,就问过余晟老师,如果要做Web开发要从何做起。余晟老师说,先从最简单的网站开始,从前端到后台,功能,设计全部走一遍,而后台语言的选择最好是PHP。当时还和余老师说,自己喜欢Python多一些,还是不学PHP了,因此还被骂了一顿。工作之后,看志容用Django做的博客,于是决定用Django做网站。可是后来发现并没有那么容易,很多时候都在学习Django如何处理Web开发,无法按照自己的想法做。于是还是听取余老师的意见,用PHP,在看了哈佛大学的《构建动态网站》以及李航用PHP写的博客,更加坚定了学习PHP的想法,其中Malan老师说,PHP就是为Web而生的,它为Web开发提供了许多语言特性。于是开始用PHP做这个网站。

不知道从什么时候开始做了,陆陆续续的用空闲的时间做。先从如何编写一个登录验证的程序,到新建一篇文章,修改一篇文章,增加一篇文章,实现分页,慢慢的,一个基本的网站就形成了,最终也就是现在这个样子。现在这个网站,如果对于一个熟手来说,不到一天就可以完成了,可是我是一个新手,所以只好一步一步来。接下来还有很多工作要做,功能上,如评论功能,RSS等还没有完成。而后台设计上,现在很多地方的HTML输出,还是用echo,没有做到MVC,接下来的机会是先用MVC重构整个网站,之后再添加评论和RSS等功能。

如果想看看现在网站的样子,可移步http://euler.dengshilong.org/

用epoll提供telnet服务(续)


用epoll提供telnet服务中说到,使用epoll提供telnet服务,用telnet登录后,然后输入命令,一切都很正常。只是由于一些原因,只能用nc命令,如echo stats | nc 192.168.168.128 9999,这个命令如果在这台机器上运行,还是会返回正确的结果,然而当这个命令在其它机器上运行时,则有时候会返回结果,有时又不返回。于是用样的命令去测试memcache,不论是在本机上还是其它机器上,都可以返回结果。暂时想不到memcache是如何做到的,于是先用echo stats | nc -i 1 192.168.168.128 9999来应付这个问题。可是这还不是最佳的解决办法,既然memcache能够做到,那么一定有解决的办法。

可喜的是,memcache是开源的,可以去看看它是如何解决这个问题的。打开memcached.c,文件不是一般的长,竟然有5000多行,慢慢找才知道memcache使用libevent,于是去了解libevent相关的知识,按照libevent官网和自带的示例程序,自己写了一个小例子运行,发现还是存在相同的问题。于是自己写了一个非常简单的单连接程序来取代epoll那一部分,可是还是存在相同的问题。问过同学之后,说是通过打印日志和tcpdump来查看网络包就一定可以找到。自己之前也用tcpdump查看过,客户端的确是有发数据的,服务端也收到了,可是为什么不显示收到的命令呢?依然没有解决我的问题,看来只有去《UNIX网络编程》中寻找问题的解答。

说实话,还真没有认认真真的看过《UNIX网络编程》这本书,因为之前对于网络编程总觉得异常繁琐,要调用socket,bind,listen,accept等函数才能建立一个链接,还要去填写sockaddr_in这个结构体,可是现在是工作,而且c就是这么底层,而网络编程也是很底层的,所以只好从头开始看起。此时对于TCP状态转换图才逐渐有些了解,没有想到啊,毕业5个多月后才知道这些,似乎有些晚了。难怪当初面试腾讯时,会被人鄙视说,许多工作的人都还能记得住这些转换图,你一个在校生怎么会不知道这些呢?其实当时很想和他说,这些都是工程实践很强的东西,没经常在这方面打滚,肯定不知道的,工作的人经常要接触这方面的东西,自然会记得住了。也罢,至少现在知道还不算晚。

看过书后,才知道有shutdown这样的东西,虽然以前看《UNIX环境编程》时,也看到过shutdown这个函数,可是没有写过实践代码,看过之后就忘记了。shudown的函数原型如下 #include int shutdown(int sockfd, int hoot); 返回:0--成功, -1--失败 该函数的行为依赖于howto参数的值。其中SHUT_WR选项,这个的意思是关闭连接写的这一半,当前留在套接口发送缓冲区的数据将被发送掉,后跟TCP的正常连接终止序列。用上这个,还是不行。

继续往下看,才知道有SO_LINGER这个选项,它使用linger结构体,如下:

struct linger {
int l_onoff; /* 0 = off, nozero = on <em>/
int l_linger; /</em> linger time */
}lin;

当l_onoff是1,且l_linger是0时,close某个连接时,TCP将会丢弃保留在套接口发送缓冲区的任何数据并发送一个RST给对端。于是我回头看代码,建立连接这一段,我用的是公司封装过的函数,发现代码中将l_onoff设置为1,l_linger设置为0,于是我认为是找到了解决的办法,将l_onoff改为0就可以了。试过之后,发现客户端还是收不到数据,看来还存在其它问题。

书上有一个poll函数的例子,于是认真看看。先胡扯一下,网上都说,当epoll工作在水平触发模式时,则是一个高效的poll,可是他们都不给出一个epoll使用水平触发的例子,只是在那里将epoll工作在边沿触发模式的程序抄来抄去的,非常令人失望。而《UNIX环境编程》也只是给出poll的介绍,没有给出例子,看了之后还是很无厘头。还好俊杰同学留下这本书,真得感谢他。用上这个例子,测试之后,发现客户端是可以收到数据的,将这段代码去代替epoll那部分代码,发现依然可以收到,于是问题的范围就缩小了。

打印日志,终于找到问题的所在。首先说明一点,在边沿触发模式下,程序一直在while循环中read客户端发过来的命令,当返回-1,且errno设置为EAGAIN;而当读到结束符,也就是客户端发送FIN包过来时,read返回0。看代码中的处理,原来在代码中,是检查最后read的结果,而不是检查最后读到的字节总数,如果返回0的话,则关闭socket,不再去处理命令,那这里有什么问题呢?通过打印日志,发现如果将命令在与服务端同一台机器上执行,则会输出

n 6 nread -1

n 0 read 0

因为是在同一台机器上,所以两个包是单独处理的,这样就会给客户端返回命令后再关闭连接。而如果不是在同一台机器上,则输出就会发生变化。有时它会收到

n 6 read 0

此时已经从客户端读到命令的,同时也读到了结束符,在我的程序中就会直接关闭连接,这样就不会去处理命令,发送数据给客户端。而有时它会收到

n 6 nread -1

n 0 read 0

这时它就可以正常处理。这就可以解释为什么之前客户端有时可以收到反射,有时又收不到。而当在nc命令中加上-i 1后,客户端的FIN包将会在命令发送大约一秒钟后再发送,服务端就可以正常处理。

通过解决这个问题,了解许多网络的知识,受益良多。如果对于网络的知识不是很了解,调试的过程总不能得心应手,找不到合适的地方输出日志。如果当初能想到输出n和nread的值,也许就可以很快的找到问题的症结,只是没有想到这里。《UNIX网络编程》果然是本好书,在实践过程中来看这本书,才发现其中闪烁的真知见灼,都是来自于实践,有些怀念Richard Stevens了。另外tcpdump确实是个好东西,对于查看网络包,非常方便。

最终的代码见这里用epoll提供telnet服务的代码续

谈谈编码


记得一年前找工作时,阿里巴巴的面试官问了这么一道题,汉字在C语言中是怎么存储的,或者说C语言是如何处理汉字的,对于那时的我是一脸茫然,因为实战经验太少了。前不久,在处理中文标题时出现了乱码问题,组长只在一段代码中改了一句话就解决了问题,觉得非常神奇,于是看这段代码,原来就是关于编码处理的,于是认真的看了这段代码。

通过努力,算是明白了这段代码,其实也就是用了iconv函数族来进行编码之间的转化,再进行特殊处理。例如如果最终目标语言是GBK,可能还需要进行全角和半角之间的转化,繁体到简体的转换。事实上,我们处理时出现了乱码问题正是在繁体到简体的转换时写错了,导致出现乱码。

这里只想写写我自己的理解,在我看来,任何编码保存的文件最后都是字节流,在读取这些字节流时,你只有采用保存文件时采用的编码才不会出现乱码。这就好像加密与解密一样,例如如果一个文件是用utf-8编码的,但你是用GBK打开,此时就会出现乱码了,因为两种编码的编码方式不同。当然还是有一些例外情况,例如如果你是用GB2312保存的文件,用GBK打开是不会出现乱码,因为GBK是兼容GB2312的,GB2312的所有编码的值在GBK中是一样的,只是GBK还利用了GB2312没有用的一些空间来增加了一些编码。而GB18030则又扩充了GBK,包含了更多的字符。

在C语言中,用字符串来保存文本,这些文本都是字节流,只是每种编码占用的空间不同。例如“我是中国人”这5个字,如果是utf-8编码的,因为每个汉字占用3个字节,则要占用15个字节,用strlen可以测出来。如果是GBK编码的,因为每个汉字占用2个字节,则要占用10个字节。对于这样的回答,不知道那个面试官会不会满意呢?

所以这里有一个问题是,给你一个字符串,如果没告诉你用什么编码的,那么要怎么判断它是什么编码的。

用epoll提供telnet服务


最近实现一个小功能,统计引擎中搜索次数,成功的次数,失败的次数,增加的记录等等,并通过socket连接提供服务,就像memcache一样,可以通过命令查询当前的状态。

于是想到以前写FTP服务器时与这个功能很类似,当时是用的多进程,可是现在不同,只能用线程,于是想到用多线程,来一个连接就开一个线程去处理,可是组长说这还是太麻烦了,可以看看select,poll,epoll等等,说实话,虽然这些都有听过,但都没有用过,这次正好可以试试。看了《UNIX环境高级编程》上的例子,大概知道什么意思,可是书上又没有例子,而且对epoll都没有介绍,只好参考网上的资料,在Epoll在LT和ET模式下的读写方式中有比较详细的介绍,依葫芦画瓢,写了一个,很快就实现了,用telnet做客户端,能够提供服务。

只是还有一些细节需要处理,如果epoll_wait是是否设置timeout,客户端关闭连接怎么处理等等,在另一篇文章中说到当read时,返回0时,就代表客户端关闭连接了,试了一下,可行。 这里本来是用发牢骚的地方,所以不想把代码贴这里,如果想看具体代码,请移步用epoll提供telnet服务的代码

参考资料

Epoll在LT和ET模式下的读写方式

How to use epoll? A complete example in C

manufactoria通关攻略


《空闲时玩的游戏manufactoria》《通关manufactoria》说过在manufactoria上花了很多时间,并借助外力的帮助下通关了,但是一直没有将它记录下来,现在记下通关方法,以免以后又忘记了。这个游戏对于我对程序设计的理解有很大帮助,给了我许多兴趣。本来给出解答是不应该的,这样会剥夺了别人思考的权力,就像以前的《吉米多维奇》习题集,本来非常火爆的,结果有好事者搞出了习题解答,之后这套习题集就冷下来了。可是每个人都有局限性的,看过《三体》之后,让我知道技术爆炸这个词,也就是徒弟本来不会一项技术,师傅教他之后,他比师傅做的还好,并有了创新。秉着这个观点,把解答贴在这里,让更多的人体验技术爆炸。

关卡按照从左到右,从上到下计数。

  1. Move robots from the entrance(top) to the exit (bottom)! 将机器从入口(顶部)移到出口(底部)! 简单,不多说
  2. If a robot's string starts with blue, accept. Otherwise, reject!  如果机器以蓝色字符开头,则接受。否则,丢弃。
  3. ACCEPT:if there are three or more blues! 如果有三个或三个以上的蓝色的,则接受
  4. ACCEPT: if a robot contains NO red! 如果机器不包含红色的,则接受。
  5. OUTPUT:The input,but with the first symbol at the end!  对于输入的字符,将第一个字符移到末尾作为输出
  6. ACCEPT: if the tape has only alternating colors! 如果只存在交替字符,则接受。 意思就是字符如果是交替出现的则接受,如蓝红蓝红蓝,红蓝红蓝等等,而蓝蓝红,红蓝蓝等出现连续相同的字符,所以不接受
  7. OUTPUT:Replace blue with green, and red with yellow! 输出:将蓝色换成绿色,将红色换成黄色。
  8. ACCEPT: if the tape ends with two blues!  如果末尾是两个蓝色,则接受。
  9. OUTPUT: Put a green at the begining,  and a yellow at the end! 输出: 对于输入的字符,在开头中添加一个绿色字符,在末尾中添加一个黄色字符。
  10. ACCEPT: Strings that begin and end with the same color! 如果开始字符和结束字符时相同,则接受。
  11. ACCEPT: With blue as 1 and red as 0, accept odd binary string! 把蓝色当做1,红色当做0, 接受奇数二进制数。 其实就是接受蓝色结尾的字符。
  12. ACCEPT: Some number of blue, then the same number of red! 接受: 一些蓝色的,然后相同数量的红色的。 也就是接受诸如蓝蓝红红,蓝蓝蓝红红红等,当然空字符也要接受,因为空字符代表0个蓝色,0个红色。
  13. OUTPUT: Swap blue for red, and red for blue! 输出: 将蓝色换成红色,红色换成蓝色。 也就是将字符串中的颜色互换。
  14. OUTPUT: All of the blue, but none of the red! 输出字符串中的所有蓝色字符,不输出红色字符。 也就是将字符串中的所有红色字符去掉,留下蓝色的。
  15. OUTPUT: The input, but with the last symbol moved to the front! 输出: 对于输入的字符,将最后一个字符移动最前面。
  16. OUTPUT: With blue as 1 and red as 0, multiply by 8! 输出:把蓝色当做1,红色当做0,将二进制字符串乘以8. 其实也就是在字符串末尾添加3个0,也就是添加三个红色
  17. ACCEPT: With blue as 1 and red as 0, accept binary strings &gt; 15! 接受:把蓝色当做1,红色当做0,接受大于15的字符串。
  18. An equal number of blue and red, in any order! 只要字符串中包含相同个数的蓝色和红色,则接受。
  19. OUTPUT:Put a yellow in the middle of the (even-lenght) string! 输出: 在字符串(偶数个字符串)的中间放置一个黄色字符。
  20. ACCEPT: X blue, then X red, then X more blue, for any X! 接受: X个蓝色,然后X个红色,接着X个蓝色,对于X没有限制。 也就是接受蓝红蓝,蓝蓝红红蓝蓝等字符串,对于空字符也接受,因为它代表0个蓝色,然后0个红色,接着0个蓝色。
  21. OUTPUT: The input, but with all blues moved to the front! 输出:对于输入,将字符串中所有的蓝色移到前面。
  22. OUTPUT: With blue as 1 and red as 0, add 1 to the binary string! 输出: 将蓝色当做1,红色当做0,将二进制字符串加上1.
  23. ACCEPT: With blue as 1 and red as 0,  accept natural powers of four! 接受:把蓝色当做1,红色当做0,接受四的开方
  24. ACCEPT: (Even-length) strings that repeat midway through! 接受:(偶数长度)接受从中间开始重复的字符串 意思就是接受的字符串是偶数长度,前半段和后半段是一样的,如蓝红红蓝红红,
  25. ACCEPT: If there are exactly twice as many blues as red! 接受:如果蓝色的个数是红色个数的两倍,则接受。
  26. OUTPUT: Reverse the input string! 输出:将输入的字符串逆转输出
  27. OUTPUT: Subtract 1 from the binary string!(Input >= 1) 输出:从二进制字符串中减去1(输入字符串>=1)
  28. ACCEPT: Perfectly symmetrical strings! 接受:完美对称字符串!意思就是回文串,也就是从左读到右和从右读到左是一样的。
  29. ACCEPT: Two identical strings, separated by a green! 接受:两个相同的字符串,由绿色隔开。
  30. ACCEPT: Read the tape as two numbers, A and B, split by a green: accept if A &gt; B! 接受:读入的字符串当做两个二进制数,A和B,由一个绿色隔开,如果A>B则接受。
  31. OUTPUT: Read the tape as two numbers, A and B, split by a green: output A + B! 输出:读入的字符串当做两个二进制数,A和B,由一个绿色隔开,输出A+B的和!

遗传算法和busy-beaver


问题描述:设计一个只有五个状态(外加一个终止状态)和两个字符集的图灵机,要求能够停机,并且状态转移后要求向左走或向右走,使图灵机走的步数尽可能的多。在这里看到busy beaver问题,看到博主用遗传算法,于是也想自己动手写程序,生成一个图灵机,因为之前没有学过遗传算法,于是上网找了一下资料,找到一篇介绍遗传算法的很好例子,遗传算法:内存中的进化 .写的挺久的,因为老是出现错误。

  • 第一步是生成合法的初始解,因为生成的规则会进入死循环,所以我设定超过5000步的都丢弃,这里我只生成了500个初始解。
  • 第二步是执行杂交和变异,这里引进500个外来物种进行繁殖。随机选取上一代中的一个规则与外来物种中的一个规则杂交,变异。杂交是交换两个规则的转移函数,变异是对其中一个转移函数进行改变或者添加一个转移函数。之后排序,选取最优的500个为下一代。

跑了三个多小时,得到一些结果,361,1004步。用Python写还是挺好的,就是程序运行的慢了一点。遗传算法很有意思,简单的规则,经过不断演化,能解决问题。

上一次说被Python的缩进弄的头痛,现在终于知道原因了。原来只要将Tab设置为4个空格宽度,并且将Tab改为空格就可以了。之前说要写一个博客系统放在SAE上,现在想想还是放弃了,因为意义不大,现在这个博客挺好的,界面很清爽。

学习Python


最近一段时间,不知道要做什么了,但总不能闲着,于是学习Python。之前也接触过Python,但真正用Python写程序,还是最近的事。

以前一直想写一个小工具,用来统计自己写过的C/C++程序的行数,但是一想到要自己去看VC++的目录操作,就放弃了。最近学了Python之后,很容易的就写了一个,只有十行左右。源程序放在这里源程序行数统计工具,此前还写了一个人人网日志下载工具,输入人人网用户名和密码就可以使用。使用正则表达式去提取链接,因为人人网的页面变动后,程序现在不能用了。

现在尝试用Tornado写一个个人博客系统,然后放到SAE中去,现在还在写,以及有雏形了。感觉写Python还是很舒服的,但是缩进问题有时挺麻烦的,因为习惯了用Tab键缩进,而很多人的程序都是用四个空格,看来还是自己的习惯不好,得改。

发现Python真是一门不错的语言,写起来方便,不会令自己纠结于细节,调试很方便,因为有错误跟踪,不像以前写C程序时,一个指针问题就要调很久。