May 2010


It is…that…的句型,在英文中非常常见,大家都知道,这表示强调,理解的时候,要把that后面的部分放到前面来,比如:

It is no wonder that she is so ill.
她病得这么厉害,并不奇怪。
It is strange that she should have failed to see her own shortcomings.
她竟然看不到自己的缺点,这真奇怪。
It is arranged that the class meeting will be held next week.
据安排,下周召开班会。

但是,语言也是非常微妙的,没有定规可循,例外的情况很多,中文里“中国队大败美国队”与“中国队大胜美国队”是同一个意思,英文也同样如此,“She is too tired to speak”与“She is too ready to speak”,句型一样,表达的却是完全相反的意思(前者是“她累得走不动了”,后者是“她说话太草率”);再比如,莎士比亚的《威尼斯商人》第2幕第2景第81行有这么一句:

……It is a wise father that knows his own child. ……

老高波:唉,少爷,我是个瞎子;我不认识您。
朗斯洛特:哦,真的,您就是眼睛明亮,也许会不认识我,只有聪明的父亲才会知道自己的儿子。好,老人家,让我告诉您关于您儿子的消息吧。请您给我祝福;真理总会显露出来,杀人的凶手总会给人捉住;儿子虽然会暂时躲过去,事实到最后总是瞒不过的。

以上是朱生豪先生的翻译,而梁实秋先生翻译的是:……聪明的父亲才能认识自己的儿子呢……

初看起来并没有错,都还通顺,但较真起来是,两位大翻译家不幸翻译错了。中国古话虽然有“知子莫若父”,但莎士比亚的意思却是相反的:无论怎样聪明的父亲,也不见得认得出自己的儿子。把这句话放回原文,才真正通顺:

老高波:唉,少爷,我是个瞎子;我不认识您。
朗斯洛特:哦,真的,您就是眼睛明亮,也许会不认识我,再聪明的父亲,也有认不出自己儿子的时候。好,老人家,让我告诉您关于您儿子的消息吧。请您给我祝福;真理总会显露出来,杀人的凶手总会给人捉住;儿子虽然会暂时躲过去,事实到最后总是瞒不过的。

那么,It is…that…,究竟在什么情况下表达这种“否定”的意思呢?据我的经验,如果它作为格言、谚语(Update:网上查到已经有人写过这个问题,总结有三个特征:1.被强调部分是一般的泛指;2.被强调中心词至少有一个形容词作为修饰;3.时态为一般现在时),就要尤其小心,仔细掂量原意,以下再举三个例子:

It is a good workman that never blunders.
再好的工人,也会犯错(智者千虑,必有一失)。
It is a long lane that has no turning.
再长的路,也会遇到转折(世事难料)。
It is a good horse that never stumbles.
再好的马,也有失蹄的时候(凡人总有缺点)。

这两天,我的同事丁宇@felixding,极具艺术气质的设计师,推荐)遇到了一个正则表达式的问题,我琢磨了半天写了个表达式,暂时能用;今天庄表伟@zhuangbiaowei)跟我说,遇到正则表达式的问题,大家一般只能查手册,但具体的问题要怎么思考和解决问题,往往束手无策;恰好我在写作《正则表达式傻瓜书》,也希望多讲讲这方面的内容。尽管目前的写作还没有进展到介绍解题经验的阶段,但可以先在blog上写这方面的内容,希望对大家有所帮助,也希望大家多提意见;如果大家愿意,我可以继续写这类文章。
另:本例解决过程中王晖同学(@cnhacktnt)提供了大量的帮助,他使用正则表达式的熟练程度远在我之上,在此深表感谢。

要想写好、写对正则表达式,第一步就是分析需求,把模糊的应用要求清楚归纳为几条程序性特征;本例中的正则表达式用于验证“密码字符串”,仔细分解应用场景,可以得到四条明确的要求(一般来说,密码字符串对长度都有要求,但本例中,需要验证的密码字符串已经由其它语句保证了是6-12位长的字符串,所以这里不考虑长度):

1.只能由小写字母、数字和横线(-)组成;
2.开头和结尾不允许是横线;
3.不允许全部是数字;
4.不允许有连续(超过一个)的横线。

下面我们一一解析:

1.只能由小写字母、数字和横线(-)组成
这一条很好办,用字符组『[-a-z0-9]』即可解决,注意我们没有用字符组『\w』,因为一般来说『\w』等价于『[a-z0-9_]』,下划线_也可以匹配;在使用正则表达式时准确限定范围、避免错误匹配,是需要谨记的规矩;

2.开头和结尾不容许是横线
这也很好办,我们知道,在正则表达式中,字符串的开头位置用『^』表示,结束位置用『$』表示(关于『\A』和『\Z』的情况暂不讨论,因为密码字符串中不可能出现换行符),这两个锚点(anchor)只匹配位置,不匹配任何字符;开头不容许出现横线,也就是说,从开头位置向后,不容许出现横线字符,我们可以用否定顺序环视(negative lookahead)功能解决。在本例中,它写作『(?!-)』,其中的『(?!…)』是否定顺序环视的标志符,其中的横线,整个结构表示,在当前位置之后(也就是右边一位),不容许出现横线字符,把它和表示字符串开头的『^』连在一起,得到『^(?!-)』,就表示“从字符串的开始位置,向右边看,不容许马上出现横线”;类似的,我们在表达式的末尾使用否定逆序环视,正则表达式『(?<!-)$』就表示“从字符串的末尾位置,向左边看,不容许马上出现横线”;

3.不容许全部是数字
这个要求得动点脑筋,有人一看到“不容许全部是数字”,就想到否定型字符组『[^0-9]*』,这其实是不对的。我们仔细想想,“不容许全部是数字”就是“必须出现至少一个非数字字符”,而第一条要求字符只能是小写字母、数字和横线,那么这个“非数字字符”只能是小写字母,或者横线。这样一来我们就知道了,在这个正则表达式中,必须出现一个『[-a-z]』匹配的字符;

4.不容许有连续(超过一个)的横线
这种“不容许出现某种连续字符”的情况,是正则表达式中最难处理的地方,因为常见的表示“不容许”的功能,就是排除型字符组『[^…]』,于是,遇到“不容许出现两个连续横线”的情况,许多人就想当然地写下『[^–]』,但这其实大错特错——我们需要谨记,字符组的作用只限于单个字符,所以『[^–]』的意思是“在这个位置,不能匹配横线”。那么要怎么办呢?
一般来说有两个办法,我们可以规定,在一个横线字符匹配之后,不容许继续出现横线,还是应用上面说过的否定顺序环视,『-(?!-)』,就保证了匹配了一个横线之后,不容许继续出现横线,如果在每一个可能匹配横线的地方都加上这个限定,“不容许有连续(超过一个)横线”的要求也就满足了;或者我们也可以在整个正则表达式的最开头,使用否定顺序环视『^(?![-a-z0-9]*–)』,因为表达式『[-a-z0-9]*–』会“尽力寻找可能的匹配”,对它加以否定,就保证了整个字符串中绝对不容许出现两个连续的横线。
在这个例子中,我们观察第一条要求对应的表达式,发现横线一般是与小写字母和数字同时出现在一个字符组『[-a-z0-9]』中,如果采取上述第一种办法,因为字符组中只能出现对单个字符的规定(而无法使用类似环视之类的结构),『[-(?!-)a-z0-9]』的意思完全不对,所以整个字符组就要改成括号,以多选结构表示为『(-(?!-)|[a-z0-9])』,显得很累赘,所以优选第二种方法。

好了,四条要求已经分别解决完毕,现在我们把它们组合起来。

首先,是开头的『^(?!-)』,这就表示“开头不容许出现横线”,在结尾用『(?<!-)$』,表示“结尾不容许出现横线”;
其次,之中的内容都只可能是小写字母、数字和横线,所以用字符组『[-a-z0-9]』,因为长度不确定,所以使用量词『*』,变成『[-a-z0-9]*』;
再次,整个正则表达式中必须出现一个非数字字符,也就是必须让『[-a-z]』匹配一个字符,因为这个非数字字符出现的位置不确定,我们不妨把上面的表达式『[-a-z0-9]*』“切开”,把『[-a-z]』塞进去,得到『[-a-z0-9]*[-a-z][-a-z0-9]*』,这样就保证了“在所有由小写字母、数字和横线构成的字符串中,至少出现了一个非数字字符”;
最后,不容许出现两个连续的横线,我们的解决办法是在字符组的最开始位置,添加一个否定顺序环视,也就是『(?![-a-z0-9]*–)』,我们把它与之前的『^(?!-)』合并起来,得到『^(?!(-|[-a-z0-9]*–))』。

所以,整个正则表达式就是这样:

^(?!(-|[-a-z0-9]*--))[-a-z0-9]*[-a-z][-a-z0-9]*(?<!-)$

看起来完全没有问题,但放到Ruby on Rails框架里运行,却报正则表达式错误——原来是Ruby不支持逆序环视,所以最后的『(?<!-)』无法使用;那么要如何解决呢?
这时候又有两个办法,第一是用字符串函数判断最后一个字符是否横线,来“辅助”正则表达式,许多新手往往会陷入思维的误区,或者追求“漂亮”,非要用一个正则表达式解决所有问题,这其实是不必要的;如果非要用正则表达式,可能要动用一些复杂的结构——不过还好,在本例中,我们可以“取巧”,再添加一个否定顺序环视,『(?![-a-z0-9]*-$)』,表示“不容许出现 横线+字符串结尾 的情况”,也就等于“在字符串结尾之前,不能出现横线”。

我们把这个字符组与之前的『^(?!(-|[-a-z0-9]*–))』合并,就得到『^(?!(-|[-a-z0-9]*–| [-a-z0-9]*-$))』;于是,整个正则表达式就成了:

^(?!(-|[-a-z0-9]*--|[-a-z0-9]*-$))[-a-z0-9]*[-a-z][-a-z0-9]*$

输入这个正则表达式,编译不再报错,运行测试,发现完全符合要求。

很多人都对外国的教材颇有微词:一个简单的东西,翻来覆去地讲,生怕不明白,很麻烦。没错,这样确实很罗嗦,但罗嗦并非没有意义,这是因为作者为广大读者(而不是某一个读者)考虑,多走了好几步,这样才能照顾到大部分读者,真正传授知识。

同样的道理也适用于翻译,译者相比读者,也一定要多走一步,这个道理最明显的体现就是典故的翻译。

所谓典故,就是文章中引用的古代故事或者有来历的词语。译者对典故的处理,需要照顾两方面:一方面要能够识别、准确理解原文中的典故,这需要语感、知识积累和运用搜索引擎的能力;另一方面,也要能把典故的意蕴传递给读者,毕竟,原文与译文是不同的语言,存在于不同的文化之中,不能奢求读者理解原作文化中的典故。

(more…)

1.长期的任务,要尽早开始

一般来说,长期任务总是比较烦人,也有难度,而人心里总有逃避困难的趋势,最后的结果或者是最后干脆放弃,或者是剩下一点点时间手忙脚乱地赶工;我自己之前也有这样的教训,自欺欺人地说“要轻松生活,抛开烦扰”,到最后几天才着急办理,搞得狼狈不堪。

后来,我发现这做法其实是事与愿违的,如果调整好心理状态,尽早了解情况并不必然带来的心理压力,反而因为时间充裕,有信心把握进度,即便中间遇到突发的问题,也留有时间解决;更重要的是,尽早着手,可以充分利用边角余料的时间:比如说,接到一份文档,需要在三天后给出意见,我一定会在当天大致浏览一遍,下面的三天里,就能在坐车、走路等等零碎的时间来思考,而且效果不错,如果没有尽早了解,这些时间就浪费了,什么有意义的事情也没干(阿基米德若不是之前遇到了问题,在澡盆里泡一万年也想不出办法检测皇冠的真伪)。

电子邮件的情况也是如此,我常看到有人讨论电子邮件是马上回好还是过一段再回好,我的经验是,收了电子邮件要尽快看,至少了解邮件里说了什么,如果不是着急的,等想清楚了再回。

2.时常想清楚自己正在做的事情

一般来说,我们做的工作总是有一个目的和意义的,但工作的形式又是非常具体的,忙起来往往就钻到死胡同里,忘记了真正的目的和意义,“想不清楚”自己真正要做什么了。前几天,我需要搭建一个演示环境,手上有两套方案A和B,方案A估计要半小时,方案B估计要一小时,于是我选择了方案A,可是动手之后才发现服务器缺乏一个必要的组件,于是先费劲添加好这个组件,再编译自己需要用到的软件,又发现在64位环境下会编译出错(以前我只在32位机器上编译过),上网查发现需要打一个补丁,于是又四处去寻找这个补丁……此时已经用掉一个多小时了,下面还不知道会有多少问题;我忽然想到,自己真正要做的无非是演示程序,解决打补丁、找软件之类的问题虽然很有意思,但其实从任务的角度考虑,是浪费时间,于是果断选择方案B,一小时后就顺利解决了。

据我观察,很多技术人员都热衷解决纯技术问题,温伯格称之为“hacking (神游)”;神游很好玩,容易上瘾,但我们都不是不食人间烟火的神人,要想真正做点事情,就不能放任神游。

关于这一条,还要补充一点:哪怕忙得昏天黑地,也不能没有头绪。工作的压力很大,忙得焦头烂额是常有的事情,许多人就在这种忙碌中失去了方向,往往忙了整天,下班了都不知道自己今天到底干了什么,有什么意义。我的经验是,越是这种时候,越要打起精神想想(虽然这样很难):自己究竟要干什么,目前的安排是不是可以做些调整……持续的思考,才会产生感悟,才能有改观,否则,有可能一直陷入“瞎忙”的境地而不能自拔。

(more…)

生硬地“对接”两种语言,尤其是“条件反射”式地对照,是翻译中的大忌——一方面,译文因此变得僵硬难读;另一方面,在不同场合,词语的意义也有不同,自然也不能用同样的办法来翻译。今天讲的就是常见单词and的翻译。

英文单词and,一般译者都翻译为“和”:

you and me 你和我
China and America 中国和美国
peace and development 和平与发展

(Update:图灵的刘江老师指出,地道中文许多场合不用“和”,确实如此,上述例句的“和”也可去掉,类似的还有“油盐酱醋”、“东南西北”、“男女老幼”等;就我个人的经验,不用“和”时一般使用单字名词,结合多字名词用“和”则显得比较洋气)

在这些场合,如此翻译并没有错:连接两个对等主体的连词,正是中文所说的“和”。

(more…)