Category: 未分类

  • 通宵游戏

    我们 Team 好久没有过团队活动了,上周老大提议我们通宵打游戏,找回大学宿舍的感觉。于是,昨天晚上我们真的实行了这一计划,到凌晨一两点的时候,大概还有二十人左右,一拨玩桌游,一拨打电脑游戏。不过到三四点的时候,打桌游的就作鸟兽散了……我们玩的电脑游戏包括 CS,红警、星际、魔兽、帝国、文明,不过这其中我只会一点魔兽和 CS.

    我第一次通宵是有一年寒假没有回家在学校待着,农历二十九晚上一夜没睡,第二天(除夕,三十)早上倒不觉得困了。不过到了晚上跟同学在宿舍看春晚,看到九点左右突然不行了,于是睡觉去。一夜的爆竹声一点都没有听见,大年初一醒来的时候,已经是下午五点钟了,正好避免了感受到外面欢庆的气氛而勾起思乡的情绪。不过以后再怎么熬夜,也不能一下睡二十个小时了 🙂

    大学宿舍的生活总是越来越堕落,后来通宵也不是什么稀罕事了,多数是在打游戏。我最喜欢一堆人玩的还是 CS,简单粗暴,并且乐趣多多。尤其是在 03 年左右的时候,CS 特别流行,学校里一栋楼就是一个大局域网,一打开 CS 在局域网里就有好多服务器。因为菜鸟一堆一堆的,像我这种稍微好点的菜鸟就很开心,记得可以在 iceworld 里悄悄跳到他们头顶上然后用刀杀掉。等到05年以后,玩 CS 的就只剩下高手了,局域网里很少能找到服务器,进入外面的服务器只能被虐了。

    昨夜到了凌晨四五点,好多同志就坚持不住了,有的找个地方睡觉,有的看综艺节目,还有的竟然说打不动游戏却在那儿看书!不过可以理解,年纪都大了!

    不管什么名义,熬夜这种事还是少做为好,毕竟伤身体啊。

  • Ruby 解析 HTML (Nokogiri)

    很多时候我们需要爬取网页并且获取页面上的特定内容,不一定是做坏事比如爬取竞争对手的数据。也许我想定期爬自己的网站,找到页面上的链接,并且看它们指向的网页是不是都还可以访问。爬到一个网页,为了获得上面所有的链接,我以前首先想到的是用正则表达式。但是 HTML 并不一定是良构的 XML (如果每个人都用 XML 的标准写 HTML,那我们直接用标准的 XML parser 就好了),写正则表达式的时候你需要考虑大小写、换行、单引号/双引号/没有引号、某些地方的空格,太头疼了。

    更好的办法当然是像在浏览器中使用 JavaScript 一样,在 DOM 树上找东西。我比较习惯用 jQuery,通过 CSS selector 来找页面上的节点非常舒服。

    其实大部分的语言都有人实现了 HTML parser. 拿当下比较火的 Ruby 来说,随便一搜就找到两个:HpricotNokogiri.看名字好像都是日本人写的──不奇怪,Ruby 就是日本人发明的。

    我在 Mac 上先试用了 Hpricot, 很奇怪我测试的一个文档不能正常处理。于是尝试 Nokogiri,很不错,没有发现问题。如果懂 XPath,Nokogiri 提供了 XPath 方式来寻找文档里的节点。或者你习惯了 CSS selector,在 Nokogiri 中也可以用类似 jQuery 的方法:

    require 'nokogiri'
    
    html = '...'
    doc = Nokogiri::HTML(html)
    
    # 获取页面上所有的链接
    doc.css('a').each do |link|
      puts "#{link.content}, #{link['href']}"
    end
    
    # 打出 meta-keywords
    puts doc.css('meta[name="keywords"'])[0].content
    

    使用 Nokogiri 相对于正则表达式的优点当然是更简单直观,而且更安全。缺点呢?如果你只是要页面上很少的数据,Nokogiri 可能比正则表达式稍微慢点,因为不管你要的数据有多么少,它都需要分析整个 HTML 文档(不过 Nokogiri 也提供了 SAX 方式的解析)。

    有意思的是 Nokogiri 一出来就号称比老牌的 Hpricot 快,然后 Hpricot 不服,很快就开始反击,我也不知道现在到底是什么情况了。有兴趣的可以看它们提供的 benchmark. 不过我想解析的速度再慢,这种过程的瓶颈还是在爬取网页这个环节上,尤其在中国这种网速超慢的条件下 🙂

  • 说说凡客诚品(VANCL)的商品评论

    首先写写促使我写本文的事情。

    今天看见某 blogger 的一篇文章广泛流传:我为什么越来越不乐意写博客了。文章内容其实是讲凡客诚品在最近推出的 T-shirt 中的抄袭行为,而现在打开凡客诚品的网站,发现它使用了如下的广告词来推广新产品:

    500 款图案 设计师原创

    让人想起当初 facebook 的新版还在测试,校内网的抄袭过来的“新版”已经上线,并且有大幅标语“校内改版,因你而变”。

    另外在网上搜“凡客诚品 抄袭”,排在前面的页面就有凡客诚品自己的文章:VANCL或成行业“公敌” 加价率只有1.2-2倍。值得关注的是在该文中,凡客诚品使用了大段文字批评其它企业的模仿和抄袭行为,称“其抄袭手法十分低劣”,“已经对部分网站侵权的行为进行了取证”。

    也许这一切只不过是凡客诚品的商业炒作,也许过几天到了一定火候,凡客诚品官方会发个文章说 Flying Mouse 已经加入凡客诚品设计师团队,Shirt.Woot 也已经和凡客诚品合作。但是我觉得这个可能性不是太大。一边抄袭别人,一边在官网的 Press release 批评别人用低劣的手法抄袭自己,这真的很有讽刺意义。看到同为创作者的设计师的作品被抄袭,不知道韩寒是什么感觉。而抄袭者就是他代言的产品的公司。


    扯了半天跟标题无关的,接下来切入正题,讲我的经历──先申明我及我的利益跟凡客诚品没有一点竞争关系。其实我很早就开始在凡客诚品买东西了,并且几个月之前还是非常忠实的拥护者,逢人即推荐,也为凡客诚品拉了不少到现在比我还忠实的客户。

    有一天老婆送我一双鞋,从凡客诚品买的(链接)。我看见了还比较喜欢,因为比较信赖它的品牌。可是第一天穿就感觉后跟非常磨脚,我上班几乎不用走路,穿了厚袜子,到下午就非常疼,晚上回来一看,双脚都破了。仔细查看鞋子,发现后跟的缝合处没有一丝柔软劲儿,坚硬得像石头,好像真的把一块石头缝进去了一样。花 300 块买一双鞋,还要买创可贴,准备让它磨破的袜子。后来发现鞋底的材料很不耐磨,没怎么穿(因为疼),后跟就磨掉一小块了──我在想,为什么不把磨破脚的那一块放在鞋底呢?

    其实偶尔买到一件次品也没关系,谁都是会犯错误的。我让老婆上去写个评论,给以后想买的人参考。真正让人生气的事情发生了──凡客诚品不让差评发表……我后来给凡客诚品客服打了电话,问为什么不让差评发表,他先是问我老婆的手机号码(因为鞋是老婆买给我的),后来确认身份之后,说您的意见很重要,评论会审核一段时间,过几天就会发表──其时我已经看见更晚时间的评论了。

    你懂得这种心情的──你一直被洗脑,等到你醒悟的时候,你会比常人更加憎恨现实。以前每次收到衣服都感到惊喜,是因为好的包装和印象让人下意识地往好处想。这时再回头看之前买到的商品,也不感觉有什么好了──好品质、好设计全是官方的观点,经过过滤的民意。现在看商品评论上方的一行小字:“所有打分与评论均来自已购买本商品用户”,多么巧妙的文案,它让客户觉得这些打分与评论是完全公正和有可信度的!没错,只有购买过的用户才可以发表评论,但这并不是充分必要条件,不是所有购买过该商品的用户都可以发表评论。我感觉鞋子很差的第一反应是自己去写评论,发现不可以才让老婆去写的。现在进入她的帐户,还可以看到订单,但是“我的评论”栏目是空的,写过的评论不见踪影,从订单点击进入商品页面再点写评论,被告知“只有买过商品的客户才可以写评论”。

    这就好像一个恶霸卖东西,把说好话的留下来,觉得不好的人统统赶走。让人不解的是,凡客诚品已经严格限制,必须买过东西的人才有权说话,为什么还要加一道审核的程序呢?很显然,这也是凡客诚品 Marketing 的一部分,他们不放过每一个角落,不止要各种铺天盖地的广告,觉得值得为此付出人力和物力,更何况,这是自家的地皮,不管怎么行!比起淘宝店铺的 100% 好评,这个可操作性高多了。

    平心而论,凡客诚品除了市场做得好,产品在同类企业中也是做得最好的一家之一。但是有些事情,就是让人不可容忍的。

    Update 2010-06-26

    凡客诚品似乎已经承认抄袭,但是差评不让发表的事,要比抄袭更恶心。发现了新料:

    现在看这个搜索结果,发现对于这问题,凡客诚品已经有了官方回答,所谓官方回答,就是客服们遇到此问题很棘手,所以凡客诚品就制定个千篇一律的答复来敷衍客户。为什么我们的评论都不见了?凡客诚品答曰

    您好,很抱歉,由于网站上的商品评论因显示条数数量有限,我们会截取一些具有代表性的客户评论显示到网页中,例如关于商品的面料以及穿着后的舒适度等信息,以引导客户正确的选购商品,即使您的评论没有显示到网页中,并不代表vancl不重视您的评论,如您有任何疑问请直接告知客服人员,我们会及时为您处理。在此请您给予谅解,欢迎您继续关注,谢谢!

    对阉割民意最好的辩解,就是“引导客户正确地选购商品”,凡客诚品果然深谙此道。其实,凡客诚品认为客户没有最基本的判断能力,所以才需要他们“引导客户正确地选购商品”,这是对客户智商的污蔑。以片面的信息来诱导(“引导”)客户,这种做法真的很无耻。摘一段 Animal Farm 中猪 Squealer 给猪 Napoleon 辩解的话:

    “…No one believes more firmly than Comrade Napoleon that all animals are equal. He would be only too happy to let you make your decisions for yourselves. But sometimes you might make the wrong decisions, comrades, and then where should we be?…”

    没错,pigs are generally recognized as being the cleverest of the animals.

  • lighttpd, web.py, spawning fcgi failed

    基于 web.py 的程序开发起来还是非常简单的,但是我没想到在服务器上部署的时候却遇到了不少麻烦。我用的 web server 是 lighttpd,不能正常启动,查看错误日志,发现如下几行:

    2009-12-15 19:48:04: (server.c.1503) server stopped by UID = 0 PID = 25128 2009-12-15 19:48:30: (log.c.166) server started
    2009-12-15 19:48:30: (mod_fastcgi.c.1104) the fastcgi-backend /var/www/code.py failed to start:
    2009-12-15 19:48:30: (mod_fastcgi.c.1108) child exited with status 1 /var/www/code.py
    2009-12-15 19:48:30: (mod_fastcgi.c.1111) If you're trying to run your app as a FastCGI backend, make sure you're using the FastCGI-enabled version.If this is PHP on Gentoo, add 'fastcgi' to the USE flags.
    2009-12-15 19:48:30: (mod_fastcgi.c.1399) [ERROR]: spawning fcgi failed. 2009-12-15 19:48:30: (server.c.931) Configuration of plugins failed. Going down.
    

    经历了许多周折之后,问题终于解决掉──实际上不是仅仅一个问题。在这儿把经验分享一下,如果有人遇到同样的问题,至少可以少走一些弯路。请按照一下几点检查错误:

    code.py 可以执行吗?

    当然你可能没有用 code.py 这个名字,我给可执行文件的名字是 root.py

    检查一下文件的权限,code.py 必须是可执行的。如果没有执行权限,那么给它加上:

    # chmod 755 code.py
    

    同时,要保证文件头部有这样的指令:

    #!/usr/bin/env python
    

    hello world 的例子可以正常运行吗?

    把你的 code.py 的内容用 web.py 首页的例子替换(别忘了加上第一行的指令):

    #!/usr/bin/env python
    import web
    
    urls = (
        '/(.*)', 'hello'
    )
    app = web.application(urls, globals())
    
    class hello:
        def GET(self, name):
            if not name:
                name = 'world'
            return 'Hello, ' + name + '!'
    
    if __name__ == "__main__":
        app.run()
    

    一般情况下这个例子是可以正常运行的。这说明可能是我们自己的程序比 hello world 多引用的库出了问题。

    检查一下 egg cache 的权限

    我的程序要链接 MySQL 数据库,所以用到了 MySQL-python,启动失败肯定有它的份。现在试试在 hello world 里加上 import MySQLdb,果然不能启动了!为了看到原因,我们用 try-except 来捕获 import MySQLdb 引发的异常,并且把它输出到网页上。把上面 hello 类的 GET 函数改成:

    try:
        import MySQLdb
    except Exception, e:
        return str(e);
    return 'hello'
    

    打开 localhost:8080, 我看到了如下的错误信息:

    Can’t extract file(s) to egg cache The following error occurred while trying to extract file(s) to the Python egg cache: [Errno 13] Permission denied: ‘/sbin/.python-eggs’ The Python egg cache directory is currently set to: /sbin/.python-eggs Perhaps your account does not have write access to this directory? You can change the cache directory by setting the PYTHON_EGG_CACHE environment variable to point to an accessible directory.

    又是权限问题,解决办法有很多种,我就把它的 owner 改成了运行 lighttpd 的用户(我配置文件里写的是 daemon):

    chown -R daemon.daemon /sbin/.python-eggs
    

    lighttpd 使用的是哪个 python?

    lighttpd 执行环境的环境变量可能和你在 shell 里使用的是不一样的!我有两个 python,一个是 CentOS 自带的老旧 python 2.4,另一个是我后来自己编译的 python 2.6,在我的 $PATH 里,python 2.6 所在的目录是优先的,但是后来发现 lighttpd 使用的竟然是旧的 python 2.4!如果是这样,比较简单的办法就是在 code.py 的头部写上 python 2.6 的完整路径,比如我的:

    #!/usr/local/bin/python
    

    还是没有解决?暂时我也想不到了……

  • Mac 软件更新不了 – 解决方案

    今天打开 iTunes,提示我有更新,是否下载,点了下载之后,Mac 的软件更新程序开始运行。但是过了一会它告诉我,你的软件已经是最新的,刚才明明 iTunes 自己说有更新的!于是我突然想起前几天在 twitter 上好像有人说 Mac 软件更新不了。我第一反应就是软件更新需要连接的 apple 服务器被封掉了 (事实证明不是),决定花点时间弄清楚这个问题。

    懒得往下看的直接使用解决办法:

    • 在 /etc/hosts 文件添加一行: 80.67.74.160 swcdn.apple.com

    有兴趣的请继续……

    首先 Google 这个问题,确实已经有不少用户开始在各个论坛上抱怨了,包括苹果的官方论坛,但是似乎没有让人满意的答复,大多数人都说把几个 com.apple.SoftwareUpdate.plist 文件删掉然后重启 Mac 试试 – 你不用试了,没用的。

    从苹果官方网站的文章看到,Mac 软件更新需要连接的服务器有:

    • http://swscan.apple.com
    • http://swquery.apple.com
    • http://swdownload.apple.com
    • http://swcdn.apple.com

    Google swscan.apple.com, 第一条结果即是重要线索,该页面可以正常打开,说明此域名没有问题。页面上有好多 URL,域名都是 swcdn.apple.com,随便复制一个打开,都会首先显示 page not found, 然后跳到该域名的首页。这是一个 domain parking page,顶部还显示“您的域名已经到期,请联系您的服务商续费”。

    dig 的结果很有趣:

    $ dig swcdn.apple.com
    
    ; <<>> DiG 9.6.0-APPLE-P2 <<>> swcdn.apple.com
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18722
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 2, ADDITIONAL: 2
    
    ;; QUESTION SECTION:
    ;swcdn.apple.com.       IN  A
    
    ;; ANSWER SECTION:
    swcdn.apple.com.    1178    IN  CNAME   swcdn.apple.com.akadns.net.
    swcdn.apple.com.akadns.net. 279 IN  CNAME   swcdn.apple.com.edgesuite.net.
    swcdn.apple.com.edgesuite.net. 19178 IN CNAME   swcdn.apple.com.edgesuite.net.globalredir.akadns.net.
    swcdn.apple.com.edgesuite.net.**globalredir**.akadns.net. 279 IN CNAME a1562.ce.w.ytcdn.net.
    a1562.ce.w.ytcdn.net.   1179    IN  A   74.63.75.122
    
    ;; AUTHORITY SECTION:
    ytcdn.net.      59910   IN  NS  **dns6.expirenotification.com**.
    ytcdn.net.      59910   IN  NS  **dns5.expirenotification.com**.
    
    ;; ADDITIONAL SECTION:
    dns5.expirenotification.com. 2803 IN    A   67.159.44.159
    dns6.expirenotification.com. 377 IN A   67.159.44.159
    
    ;; Query time: 6 msec
    ;; SERVER: 124.207.160.106#53(124.207.160.106)
    ;; WHEN: Sat May 15 23:33:33 2010
    ;; MSG SIZE  rcvd: 305
    

    看来应该不是常见的 DNS 投毒。我没搞清楚 ytcdn.net 到底是谁的,可能是属于 akamai 的,也有可能是 Akamai 在中国的合作伙伴。在域名解析之后,我们的请求被分配给 ytcdn.net 的服务器了。注意 dig 结果加粗的部分,然后查 ytcdn.net 的 whois 信息,哇,XIN NET 出现了:

    Domain Name: ytcdn.net
    
    Registrar: XIN NET TECHNOLOGY CORPORATION
    Whois Server: whois.paycenter.com.cn
    Referral URL: http://www.xinnet.com
    Status: clientUpdateProhibited
    
    Expiration Date: 2011-04-30
    Creation Date: 2008-04-30
    Last Update Date: **2010-05-01**
    
    Name Servers:
        dns5.expirenotification.com
        dns6.expirenotification.com
    See ytcdn.net DNS Records
    
    Information Updated: Sat, 15 May 2010 15:40:22 UTC
    

    注意加粗的 Last Update Date,回头看网上 Mac 用户发帖求助的日期,都是 5 月 2 日以后。大家请猜测去吧,ytcdn.net 到底属于谁, expirenotification.com 到底属于谁?我是懒得想了,反正 DNS 解析结果指向 ytcdn.net 就完蛋了,因为最终的 IP 并不是真正的 CDN 服务器。

    登录到美国的一台机器(使用的 DNS 服务器跟我的 Mac 不一样),尝试访问在 swscan.apple.com 页面上找到的 swcdn.apple.com 域名的 URL,可以正常下载文件。再看解析 swcdn.apple.com 的结果:

    $ dig swcdn.apple.com
    
    ; <<>> DiG 9.3.6-P1-RedHat-9.3.6-4.P1.el5 <<>> swcdn.apple.com
    ;; global options:  printcmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62285
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;swcdn.apple.com.       IN  A
    
    ;; ANSWER SECTION:
    swcdn.apple.com.    3600    IN  CNAME   swcdn.apple.com.akadns.net.
    swcdn.apple.com.akadns.net. 300 IN  CNAME   swcdn.apple.com.edgesuite.net.
    swcdn.apple.com.edgesuite.net. 21600 IN CNAME   swcdn.apple.com.edgesuite.net.globalredir.akadns.net.
    swcdn.apple.com.edgesuite.net.globalredir.akadns.net. 300 IN CNAME a950.gi3.akamai.net.
    a950.gi3.akamai.net.    20  IN  A   80.67.74.176
    a950.gi3.akamai.net.    20  IN  A   80.67.74.160
    
    ;; Query time: 146 msec
    ;; SERVER: 74.207.241.5#53(74.207.241.5)
    ;; WHEN: Sat May 15 23:35:05 2010
    ;; MSG SIZE  rcvd: 231
    

    现在修改 Mac 上的 hosts 文件让 swcdn.apple.com 指向这个 80.67.74.176,重新开始检查软件更新,一切正常了。

    Apple, 后果很严重,请尽快联系你们的 CDN 服务商解决此问题。其实我看到有一些用户其实已经亲自到 apple store 尝试让 apple 员工帮忙解决了,但是他们也不知道原因。

    Update: 后来发现这里有网友说改 DNS 服务器也可以解决,应该是没问题,不过我自己没有测试。改 hosts 文件是短期内绝对保险的做法,除非 akamai 经常变动它的 IP. 从苹果官方论坛那个帖子看,受影响的用户在中国的居多,但是其它国家的似乎也有。

  • Symbian Series 60 程序开发 (2) ListBox

    上一篇:Symbian Series 60 程序开发(1) Hello World

    继为 S60 成功编译 hello world 程序之后,我又尝试了一下跟实用程序更接近一步的开发——使用列表控件即 ListBox,这是在 S60 手机上比较常见的控件。大概花了5个小时的时间,终于知道最基本的用法,然而我已经开始对 Symbian 开发失去兴趣,因为 ROI 太低了。花的时间多,费的力气多,这还只是刚刚开始。我后来看了一眼怎么开发利用 API 如 flickr、evernote 等的应用,发现了由 Nokia 自己写的,网上广为流传的 CClientEngine 类,很复杂,用起来也很复杂。所以本来打算写一系列文章的,可是到这个第二篇,也许就是最后一篇了,目前不太想在这个事上浪费时间。

    最后的代码很简单,时间都浪费在文档查找与尝试的过程中。大致过程如下:

    1. 在 AppView 类的头文件中包含必要的头文件,添加成员变量定义
    2. 在 AppView 类的 ConstructL 函数里添加创建 ListBox 的代码
    3. 给 AppView 类实现 CountComponentControls,提供接口告诉外层容器该 view 包含多少个控件(本例是1)——我感觉这是设计缺陷
    4. 给 AppView 类实现 ComponentControl(TInt aIndex), 提供接口给外层容器,使其可以根据 index 取得相应的控件——感觉同上,如果你没有实现这两步,程序就是个白板,ListBox won’t be rendered.
    5. 给 AppView 类实现 OfferKeyEventL 函数,将按键事件传递给 ListBox 控件
    6. 千万不要忘记在 CHelloS60AppUi::ConstructL 中 AppView 被创建后,将它加入 Control Stack,否则按键事件不会传递过去
    7. 编辑 group 文件夹下面的 mmp 文件,添加需要链接的 lib 文件
    8. 编译方法照旧

    我懒得每一步都贴出代码了,不过我在 Google code 上创建了一个 project: hello-s60. 它基于 svn 的 browse source 功能比在这里贴代码清楚多了。Revision 3 即是本文中所介绍的代码修改,可以非常清楚地看到具体在哪些文件,哪个地方。

    这里给出一些稍微有点价值的文档:

    最后抱怨一下,基于 Frame 的文档用户体验相当不好,浏览器地址栏的 URL 从来不会改变,因此不便于分享。

    这程序安装到我的 E71 上,我每天就靠按上下键打发时间……哈哈

    上一篇:Symbian Series 60 程序开发(1) Hello World

  • SEO 重复内容问题

    几天前无聊在 Google 看对我这个站点的收录情况 (site:qingbo.net),结果让我很意外:

    site:qingbo.net

    看截图,严重的重复内容!SEO 之大忌,这里有 Google 关于重复内容的解释。重复内容事实上是说不同的页面(即链接,URL)有相同或过度相似的内容。当然这对用户来说很不友好,而搜索引擎作为方便用户查找信息的工具,也当然不喜欢这样的站点。

    不过我的站点被 Google 收录的所谓重复内容,实际上都是同一个页面即首页,只不过 Google 给首页加了参数 (?s=xxx) 之后来爬取,我在写程序的时候并没有考虑到。这确实不是爬虫普通的行为,出现这样的情况是因为我改了域名并且在 Google Webmaster Tools 里面使用了 Change of address 功能,而我又没有沿用原来的程序。Google 将原来存在的 url 替换了域名之后来爬取,就出了这样的问题。

    解决的办法有多种,比如 canonicalization, 301 redirect, Parameter handling tool 等等。我修改了一下自己的程序判断 URL,如果不是正规的就 301 跳转一下,并且加了 canonical 标签,希望可以解决重复内容。

  • Symbian Series 60 程序开发 (1) Hello World

    下一篇:Symbian Series 60 程序开发 (2) ListBox

    Symbian 平台的程序开发很困难,似乎是众所周知的问题。我目前还不了解是什么造成这种困难,开发工具、开发环境?相对 iPhone 平台来说,众多的屏幕尺寸也是一个问题。另外可能由于 Symbian 平台长期以来主要只是一些大企业在开发应用程序,导致对之有兴趣的个人程序员比较少,而且盈利的机会也不怎么多。如果 iPhone 不是把利益分配给大批的个人程序员和小团队,app store 也不可能这么繁荣吧。开发者少就直接导致社区规模小,开发资料少。

    我基本上还没有接触 Symbian 开发,所以上面的仅仅是个人猜想。不过我这几天倒是初步试了一下 s60 的开发,至少发现工具多而乱,文档多而乱,这里就把写一个 Hello World 程序的过程记下来。本文标题里有个编号,所以我还会继续探索,并继续记录。

    开发工具选择

    我在使用的手机是 E71, 系统是 S60 3rd Feature Pack 1. 开发语言不必仅限于 C/C++,但是我比较在意性能问题,并且似乎在官方文档里看到过用 Symbian C++ 可以有最大限度的灵活性。我很理解苹果为什么修改 SDK license agreement 禁止代码转化器或者生成器Qt 似乎有更好的开发工具,可是虽然它被诺基亚收购了,商业开发仍需支付不菲的费用。我很奇怪诺基亚为什么不为开发人员降低用 Qt 开发移动应用的成本。

    开发环境搭建

    安装之前注意,所有这些工具都必须安装到同一个磁盘分区,包括打开 IDE 时需要选择的 workspace 路径,以省去不必要的麻烦。

    1. 安装 Carbide.c++,这是一个基于 Eclipse 的 IDE
    2. Carbide.c++ 安装完成后,应该会弹出一个网页,要求你安装 ActivePerl-5.6.1.635 – 必须是这个特定的版本
    3. 接下来选择要安装的 SDK,我的 E71 就是 S60 3rd FP1, 列表里默认的就是,直接下载安装

    Hello World

    我是参考 Carbide.c++ 文档中的例子做的 Hello World,不需要写代码即可生成一个 S60 的应用,当然了,和所有的 hello world 一样,和所有 IDE 生成的程序一样,这个应用很没用。我这里就不再复制文档里的内容了。

    debug 过后,肯定想知道如何在手机上运行这个程序吧,虽然它很没用。请到这里看:Building a SIS File in Carbide.c++. 没错,刚才的文档是在 nokia.com 上,现在你得跑到 symbian.org 的 wiki 里了,这就是我为什么说 Symbian 平台的文档多而乱了。Symbian 不缺文档,只是太乱了,我很奇怪为什么他们不提供一条清晰的线索供初学者起步。

    按照该链接中提到的方法创建一个 sis builder之后,右键点击该 Project -> Build Configurations -> Set Active,选择 Phone Release (GCCE) [S60_3rd_FP1],否则生成的 sis 文件很大并且无法安装。然后右键点击该 Project,选择 build project,就会在 sis 文件夹里生成一个 sis 文件,通过蓝牙或 PC Suite 安装到手机上,就可以运行了!

    本人初次接触 S60 开发,如走了歪路敬请专业人员指正 🙂 我还会继续探索,但是还没想好要做一个什么应用好。想法很多了,比如 Evernote 的 S60 客户端,或者 todoist/rememberthemilk 的客户端,但是我会挑最简单的尝试。尝试过程中有价值的东西我会记录下来,作为该文章的后续。

    下一篇:Symbian Series 60 程序开发 (2) ListBox

  • Ruby 逗号带来的问题

    几个月前写了一篇 “Ruby 的一个陷阱“,这几天又因为大意得到了另一个教训。

    程序有一段代码本来是 Hash 的,大约如同:

    a = {
      :x = 1,
      :y = 2,
      :z = 3
    }
    

    程序结构变化后,我需要给改成变量的赋值,但是忘记删除每行后面的逗号了,于是变成下面的样子:

    x = 1,
    y = 2,
    z = 3
    

    我觉得学校里那一类闲得没事干的老师肯定会在考试的时候或者课本的习题中出这样的题:请写出变量 x 现在的值。你猜是什么?是一个数组:

    [1, 2, 3]

    看到答案就明白为什么了,是吧……看来 Ruby 语法很灵活,解释器为你发现错误的机会就少了,写程序的时候千万要注意。

  • Mac OS X 中的怪事

    Mac 一直用得好好的,不过最近发现两件比较奇怪的事情。

    有一天离开办公桌一会,习惯性地锁屏。回来之后一动鼠标,奇怪怎么没锁屏呢?仔细一看,不对,确实出来锁屏状态请求输入密码的对话框了,但是它后面并不是以往的黑屏,而是所有的窗口都可以看到,并且所有操作都可以正常进行,只是密码对话框永远居于所有窗口的前面。我本来想多玩一会再截屏的,不过玩了一小下,一切恢复正常,屏幕变黑,必须输入密码了……

    没过几天,又发现一个 bug,但这个我觉得可能是 Firefox 的问题。两个显示器上分别有一个 Firefox 的窗口,其中一个是正常的,但另一个在地址栏输入的时候,出来的提示竟然跑到另一个显示器上一个奇怪的地方!应该是这个窗口创建那个输入提示的 UI Control 的时候,找错显示器了。将出问题的 Firefox 窗口稍微移动一下位置,问题就没了。

    对了,我的系统还是老旧的 10.5.8.