Category Archives: 计算机
很多语言或协议选择使用 ASCII 字符 “\”(backslash,0x5c) 作为字符串的转义符,包括 JSON 中的字符串。一般来说,使用 Python 中的 JSON 模块编码英文,不会存在转义符的问题。但如果使用 JSON 模块编解码中文,就可能面临着中文字符包含转义符带来的 bug。本篇文章给出了一个 badcase。
中文解码错误
测试用例文件里面包含繁体的“運動”二字,使用 GB18030 编码。使用 json 解码的错误如下:
$ cat decode.dat
{"a":"運動"}
$ python
>>> import json
>>> fp=open('decode.dat', 'r')
>>> json.load(fp, encoding='gb18030')
Traceback (most recent call last):
File "", line 1, in
File "/home/yangwb/local/lib/python2.7/json/__init__.py", line 278, in load
**kw)
File "/home/yangwb/local/lib/python2.7/json/__init__.py", line 339, in loads
return cls(encoding=encoding, **kw).decode(s)
File "/home/yangwb/local/lib/python2.7/json/decoder.py", line 360, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/home/yangwb/local/lib/python2.7/json/decoder.py", line 376, in raw_decode
obj, end = self.scan_once(s, idx)
UnicodeDecodeError: 'gb18030' codec can't decode byte 0xdf in position 0: incomplete
multibyte sequence
发生这个问题的原因,就存在于“運”字的编码之中。“運”的 GB18030 编码是 0xdf5c,由于第二个字符与转义符 “\” 编码相同,所以剩下的这个 0xdf 就被认为是一个 incomplete multibyte sequence。
我本来认为,既然已经提供了编码,json 模块就能够区分汉字与转义符(所以我觉得这应该是 json 的一个 bug)。但从实验来看,并非如此。对于一些不需提供字符编码的 JSON 解码器来说,我们倒可以用一种比较 tricky 的方法绕过上面这个问题,即在“運”字后面加一个额外的转义符:
{"a":"運\動"}
遗憾的是,这种方法对 Python 的 json 模块不适用。我仍不知道该如何解决这个解码问题。
中文编码——没错误!
对于相同的 case,Python 倒是能够编码成功:
$ cat in.dat
運動
$ python
>>> import json
>>> in_str = open('in.dat', 'r').read()
>>> out_f = open('out.dat', 'w', 0)
>>> dump_str = json.dumps({'a': in_str}, ensure_ascii=False, encoding='gb18030')
>>> out_f.write(dump_str.encode('gb18030'))
$ cat out.dat
{"a": "運動"}
所以这件事情就把我给搞糊涂了,Python 的 json 模块不能解码自己编码的 json 串。所以我觉得这可能是一个 bug,或者至少是 2.7.1 版本的 bug。
PS: 要仔细看文档
20120516:经网友 TreapDB 提醒,加载字符串时自己做 Unicode 转换,貌似能够解决这个问题。
$ cat decode.dat
{"a":"運動"}
$ python
>>> import json
>>> in_str = open('decode.dat', 'r').read().decode('gb18030')
>>> json.loads(in_str)
回头仔细看了一下 json 的文档,其中有这么一段:
Encodings that are not ASCII based (such as UCS-2) are not allowed, and should be wrapped with codecs.getreader(encoding)(fp), or simply decoded to a unicode object and passed to loads().
已经注明了 encoding 不支持非 ASCII-based 编码的参数,所以应该使用 getreader 进行转码,而不是让 json 模块去转码。看来是我没读懂文档,大惊小怪了,回家面壁去!
>>> json.load(codecs.getreader('gb18030')(fp))
做后台系统比做客户端软件的辛苦的地方,就是不能让程序轻易地挂掉。因为在生产环境中无法容易地复现或调试 bug,很多时候需要程序日志提供足够的信息,所以一个后台系统的程序员必须要明白该如何打日志(logging)。
很多语言都有自己现成的 logging 库,比如 Python 标准库中的 logging 模块,Apache 的 log4cxx(C++), log4j(Java)。如果你愿意找,很容易能找到基本满足自己需求的日志程序库。当然,自己实现一个也不是很困难。难点不在于写这些库,而是如何去使用它们。
大部分情况下,我们关注的都是日志的级别和内容。即哪些情况下,该打哪个级别的日志,日志语句中,该怎么写。
在程序开发的过程中,我们需要很多的日志协助分析程序问题;但在生产环境中,我们没有那么多的空间存储丰富的日志,而且日志量太大对于问题排查反而是累赘。有些人使用预处理解决这个问题,在 debug 版本和 release 版本中编译进不同的日志语句。这样能够解决一些问题,但却使得在生产环境中无法轻易地打印更多的日志。大部分人更接受的做法是,使用配置(参数)控制日志的打印级别,在需要更多日志的时候,可以随时打开它们。为了实现日志“少但是足够”的目标,开发人员必须明白日志信息的价值,即哪些日志应该属于哪个级别。
日志的作用是提供信息,但不同的日志语句,提供的信息量却是不一样的。有的日志里会写“Failed to get sth..”,但却忘记加上失败调用的返回值。同程序一样,日志语句中有的是变量(某个变量内容),有的是常量(提示信息)。常量你总能从程序源代码中获得,但变量不行。所以在一条日志中,信息量最大的是变量,是函数返回值/字符串内容/错误码,因而变量应该尽量放在靠前的位置。常量也不是一点价值没有,写得好的提示语句,会使问题一目了然,可以免去你到代码中 grep,然后重读代码的麻烦。
上面这两点,几乎所有知道 logging 重要性的同学都会了解。但关于 logging 对性能的影响,很多人没有足够的警惕心。例如有人会在一个按行解析文件的函数中写下这样的日志:
int parseline(...)
{
log_trace("Enter parseline with ...");
DO_SOMETHING;
log_trace("Exit parseline with ...");
return 0;
}
乍一看,由于 log_trace 级别不高,在生产环境中肯定会关闭,那么这样做看起来对性能没太大影响。但实际上 log_trace 可能是这样实现的:
#define log_trace(fmt, arg...) \
xx_log(LVL_TRACE, "[%s:%d][time:%uus]" fmt, __FILE__, __LINE__,\
log_getussecond(), ## arg)
#endif
可以看到 log_trace 宏中自动添加了很多信息,值得注意的是时间参数 log_getussecond()。大家都知道统计时间需要系统调用,那么无论 log_getussecond() 函数是如何实现的,它的代价肯定是高于一般的简单函数。
我们本以为 log_trace 在 LVL_TRACE 级别被关闭的情况下,消耗的代价仅仅是一个函数调用和分支判断,却没有发现宏参数中还隐藏着一个需要调用系统调用的函数。当文件不大是还算能够忍受,但当这个文件是一个数据库,扫描每一行都要执行两次 log_trace() 时,它对系统性能的影响就绝不可忽视了。
所以,最佳的做法还是,在性能攸关的代码中,使用可被预处理掉的 logging 语句,仅仅在 debug 发布中才能见到这些日志,release 版本中不把它们编译进来。
此外,上面这个 log_trace,是一个糟糕的设计。logging 模块只应该干 logging 的事情,开发人员需要时间统计时会自己完成。
exVim 是一个非常优秀的 Vim 环境,通过它能够省去很多 Vim 插件的配置工作。自从使用上 exVim 后,我基本没有再自定义 Vim 插件,完全依赖 exVim 打包的辅助功能。
最近让我略有不爽的使用问题是:exVim 默认的 file filter 和 dir filter 都是匹配通过的,即“匹配 filter 过滤条件的目录和文件被通过,列入项目目录、文件列表中”。
对于文件来说,设置匹配通过毫无问题。因为我也想要项目中仅包含 “.cpp,.c,.h,.py” 这样的源代码文件,选出来匹配这些模式的文件就是我希望的结果。
但是对于目录来说,设置匹配通过就与我通常的需求相悖了。一般情况下,项目目录下的所有目录都是程序需要的。但是一些专门存放测试程序、测试框架、输出文件的目录,我其实不希望显示在我的项目中。而且 exVim 中的目录过滤貌似仅限在项目顶层目录中,过滤的意义不大。
所以我修改了一下 exVim 的代码,将默认的 dir filter 含义修改为匹配拒绝,即:“匹配 dir filter 的目录被拒绝(被过滤掉),无论它在哪一级。"例如,我将 dir filter 设置为 “test,output”,那么我项目目录下所有叫做 test 或者 output 的子目录都不会显示到项目目录列表中,而不妨碍其它名称目录的通过。
可以想见两个 filter 采用不同的通过逻辑并不是 exVim 开发者希望看到的,所以我想这个修改也没必要提交给开发者。不过我仍然觉得这是很有用的一个修改,所以拿出来分享一下。修改的补丁文件见:http://share.solrex.org/ibuild/exvim-dir_filter-8.05_b2.patch。
PS: patch 文件中还有一个改动是将 quick_gen_project_PROJECT_autogen.sh 文件从项目目录下,移动到项目目录下的 .vimfiles.PROJECT/ 目录中,原因是看起来碍眼 :)
预先声明:这不是一篇软文,我真的是出于对 MIUI 系统的喜爱才写的。 我的手机型号是华为 U8800,联通定制机,自带的 ROM 里太多乱七八糟的软件,后来就刷了华为官版的海外 ROM。这个 … Continue reading
一直在特定领域的分布式系统一线摸爬滚打,曾取得一些微不足道的成绩,也犯过一些相当低级的错误。回头一看,每一个成绩和错误都是醉人的一课,让我在兴奋和懊恼的沉迷中成长。自己是个幸运儿,作为一个 freshman 就能够有机会承担许多 old guy 才能够有的职责。战战兢兢、如履薄冰的同时,在一线的实作和思考也让我获得了一些珍贵的经验,却直至今日才够胆量写出来一晒。这篇文章标题前面是“妄谈”两字,所持观点未必被所有人认可,我姑妄言之,有心之人姑听之。若有些友好的讨论,亦我所愿也。 我做的虽然也是分布式系统,却不够胆去讨论通用分布式系统的设计原则。因而这篇文章的主题限定到一个特定领域的分布式系统设计,这样即使别人有疑惑,我也可以把 TA … Continue reading
我一直羞于去崇拜某个人,死去的或者正在死去的。我总害怕他做了或者将要做一件我并不认同的事情,这就好比冬至吃饺子时吃到一块脏兮兮的硬币,不是每个人都能享受这种运气。所幸的是《史蒂夫·乔布斯传》的作者所持角度还算客观。读完这本书之后你不免去崇拜乔布斯,但又会有所保留。你会想:我真希望能达到他那样的成就,却不想成为他那样的人。 每个人做事都有自己的方法,但乔布斯采用的那种,恐怕要被归到异类中去。只有他那种天生的特质,结合独特的人生经历,还有过早成功的光环,才能造就那样强大的现实扭曲力场。跋扈如他那样,做一个打工仔的话,恐怕不出半月就要卷铺盖走人了。 在他的传奇一生中,有一股认真劲儿让人不能不佩服。包括他对事件策划、产品外观、字体、广告、包装,甚至于名片美学上的执念,无不让你觉得这家伙真倔,真难办。但当你转了一圈儿,看到那么多平庸的玩意儿时,你就会想:哦,这家伙还是有一定的道理的。 除了认真之外,他还有一点让我非常羡慕的,就是不屈于现实。软驱、吸入式光驱、玻璃屏幕、拉丝铝外框,到处都透着不妥协。但奇迹的是,那些硅谷工程师们居然办到了!这一点也被我归结为我们信息技术落后于硅谷的原因,奇思妙想得不到尊重,创新的阻力远大于推动力,有批判性,无建设性。 如果在这里赞扬他,能够列出的点还有很多。看完这本传记后,我甚至有去买苹果产品的冲动。但就像前面所说,我,一个平庸的家伙,对乔布斯的革命性的设计和产品,一直没有足够到转化为购买力的欣赏。创新是一样高投入的工作,必须有高回报的支持,而这依赖于掏腰包的人的欣赏和价值认可。对于某个产品来说是这样,对于一个公司来说,同样如此。可惜的是,很多公司的老板更像我,而不是乔布斯。
因为习惯了 qsort 的函数指针参数,以前用 std::sort 的时候一般也是传函数指针而不是仿函数(functor)。从很多示例程序来看,貌似没有什么大的不同。但是直到今天我才醒悟,原来是示例太简单了啊! 具体来说,我今天遇到了一个问题:要对一个表进行排序,每个字段可能是升序,可能是降序,也有不同的类型,所以排序的时候需要根据这些信息进行比较。比较函数不能是类成员函数,但我又的确要用到类成员的信息,函数接口又不能变,着实发愁。愁了就只能 Google,发现原来仿函数可以轻松地搞定这件事情。 // … Continue reading
Google 将在今年 12 月的 ACM CoNEXT 会议上发表他们在改善 Web … Continue reading
自动分裂是分布式系统中的一项重要技术,通常与自动迁移和负载均衡一起考虑,提供了系统的可扩展性和良好的性能。例如 Google 的 BigTable 和 Yahoo 的 PNUTS … Continue reading
在编译 Levedb 时,我遇到了这个错误: g++ -c -I. -I./include -fno-builtin-memcmp … Continue reading

