2006年12月18日


以下是转贴。

What Business Can Learn from Open Source
by Paul Graham 2005.8(这篇短文来源于 2005 年在Oscon的一次谈话。)

公司最近对开源给予了更多的关注。十年前似乎真有这样的危险:微软会把它的触角伸到对服务器的垄断。现在可以说是开源阻止了这种情况的发生。一次的最近调查发现,52%的公司把windows服务器转换成了Linux 服务器。

我想更为重要的是,弄清楚这52%的来由。此时此刻,那些想在服务器上运行windows的人,应该先弄懂为什么Google、Yahoo和Amazon服务器不使用windows。

但“商业向开源学习”最重要的方面并不是关注Linux或Firefox,而是关注导致它们的力量究竟是什么。这些最终将比你使用什么软件影响更大。

我们也许能从开源和书写blog之间的稳固关系中,衡量出这些根本的力量。如同你大概注意到的,它们有很多共同点。

就 象开源,书写blog是人们为自己所做的事情,它是免费的,是因为人们喜欢这么做。象开源的黑客、bloggers与那些为钱工作的人竞争,并且常常获 胜。保证质量的方法是相同的:达尔文主义的。公司通过制定规则来防止雇员把事情搞得一团糟,但你不能把职员与职员间的通信也制止了。人们只是生产他们想要 的东西;只传播好的材料,把坏的忽略掉。并且在以上两个例子中,人们从反馈中改进工作,使之变得更好。

blog和开源的另一个共同之处就是Web。人们总是愿意不计报酬地完成伟大的工作,但是在Web诞生之前,它很难抵达观众的面前,也很难在项目中合作。

业余爱好者

我认为商业中不得不学习的、最重要的新法则就是:人们在喜欢的事务上工作得太辛苦了。是的,这对每个人来说都不新鲜了。但为什么我还要宣称商业不得不学习它呢?当我说,商业还没有认识到这一点,其实我是指商业结构没有对此进行反省。

商业仍然反映出一种旧的模式,举一个法语中形容工作的词语:travailler。它在英语中接近于“辛苦”这个词,也意味着“痛苦”。

可 这还不是形容工作的最终词语。当社会变得越来越富有,人们认识工作就非常像认识饮食一样。现在我们知道最健康的饮食习惯,是我们的农业祖先因为贫穷,饱尝 万物中形成的。而当丰富的食物摆在面前,即使你没有完全吃掉它,这种懒散也只是看起来很惬意。我想我们是被设计去工作的,就像我们被设计好了去吃一定数量 的纤维,如果我们不这么做,就会感觉有点糟糕。

人们用一个词语来形容喜爱某项工作的人:业余爱好者。现在这个词语被赋予了贬义,虽然它在凝视我们的面孔,但我们仍旧忘记了这个词语的来源。“业余爱好者”原本更接近于一个称赞的词语。而在20世纪“专业”变得重要起来,而“业余爱好者”恰恰相反。

实 际上,至少在传统媒体上使用“blog”这个词是一种时尚。他们把Blogger弄混淆了,认为blogger并不光是表现以weblog格式写作的人, 而是任何在网络上发表观点的人。这渐渐成了一个问题,就好像web成为了出版的首要媒体。我更愿意用一个词来替代在网上写作的人——“作家”,这个词如 何?

那些在传统媒体上搞混了网上写作的人,因为他们认为平均质量很低是网上写作的重要特征:没有人愿意读质量平平的blog。过去的观点 总是谈论平均质量,因为那是你是否喜欢的标准;但是现在,你可以读到你想要读的任何一个作家的作品。所以,网上写作的平均质量不再成为和传统媒体对比的一 项指标。它们和网上最好的“作品”对比,比如和微软竞争,它们常常是失利者。

我知道自己作为读者的经历。虽然大多数印刷出版物都有网络版,每当我阅读网上报刊和杂志的一篇文章,我就要阅读个人站点上的两三
篇文章。

当 我阅读的时候,比方说,《纽约时报》的故事,我从来不是从《纽约时报》首页开始读的。常常我是从Google News 或Slashdot或者Delicious的聚合进入阅读。聚合器展现出来比频道(channel)更好的阅读方式。《纽约时报》的首页是文章列表,它们 是由时报的工作人员组织排列的。Delicious是一个人们对感兴趣的文章的列表。而且,恰恰你能注意到,两者之间的交叉是很少的。

传 统印刷媒体的大多数文章很令人厌烦。例如,总统注意到大多数选民认为侵略伊拉克是错误的,因此,他为了得到支持,而向国民演讲。在那里“人咬狗” (译注:俗话说狗咬人不是新闻)是为了什么?我没有听到演讲,但是我能告诉你,他确切地讲了什么。更多是在文字意义上,像那样的演讲并不是新闻:因为在其 中并没有什么新的东西。

除了名字和地点的更改,其中并没有新的东西了,大多数事件的“新闻”正走向歧途。孩子被拐骗、龙卷风来了、轮船倾 覆了、有人被鲨鱼咬了、小型飞机失事了……你能从世界的这些故事中得到什么吗?什么也没有!它们正远离数据点(date point);这些引人注目的话题也往往和它们毫不相干。

和在软件业一样,当专家们生产者这样的垃圾时,如果业余爱好者可以做的更好就不 足为奇了。通过频道而生,通过频道而死去(Live by the channel, die by the channel)。如果你依赖于垄断,当你突然面临竞争的时候,就会很难摆脱坏习惯。

工作场所

blog 和开源的另一个共同点是:人们都可以在家里完成工作。这似乎并不奇怪,但它理应令人惊叹。这相当于一架自制的飞行器击落了一架F-18。公司为个人花费数 百万美元建立办公楼的目的就是:它是工作的地方。而人们依然在家工作,那些并不是为工作设计的场所中工作时,得到的结果却是效率提高了。

这证明过去很多事情都值得怀疑。在普通办公室中完成工作是很凄惨的一件事情。很多使办公室变坏的东西,恰恰是我们和职业化相挂钩的性质。办公室的效率低下往往被期望给予更有效的建议,但是“更有效的建议”和“实际上更有效”完全是两码事。

普通工作场所的氛围,被描绘成加速汽车两侧的火焰,看似生产效率很高。但这种方式,不是解决办公事看起来荒凉的唯一方式。实际上人们的行为就是这样的差劲。

情 况在开端有了不同。常常在公寓中就有了不同的开端。为了匹配米色的卧室,人们买了很多不同种类的家具。他们不用按照固定的时间工作,穿着最普通的衣服。他 们在网上看看想要的东西,而不必担心什么“工作环境”。淘气的幽默替代了办公室中愉快而冷漠的微笑。你还想知道什么?公司在这种情况下,可能比以前运营中 任何时候更有效率。

可能这不是巧合。可能一些专业化的方面实际上是网络的损失。

对于我来说,传统办公室最令人丧气的方面是:确切的时间你应该在那里。很多公司中的员工确实这样做,大多数员工在固定的时间工作仅仅是因为:公司无法评估他们的工作效率。

办公时间背后的理念是:如果你不能让员工工作,至少可以阻止他们获得乐趣。理论上,如果员工一天不得不在楼里呆上几个小时,禁止做与工作无关的事情,他们只能继续工作了。而实际上,他们把时间浪费在无人理睬的尴尬境地(no-man’s land),既不工作也不找乐子。

如果你能估量出人们做了多少工作,许多公司就不需要任何固定的工作日了。你也许会说:这是你不得不做的。在任何你喜欢的时间、任何你喜欢的地点做事情吧。如果你的工作需要你在公司中和同事交流,你可以在必要的时候去几次。其他方面我们就不用考虑了。

那听起来有些乌托邦,但是它能告诉我们谁是来公司做事情的。没有固定的工作时间,我从没在早晨11点之前露面。但我没有说这是仁慈的。我们说的是:如果你在那里工作,我们期望你能做得更多。不要假装做了很多,从而唬弄我们。

facetime (针对时长?)模式的问题不仅仅是因为它令人沮丧,而且许多假装工作的员工妨碍了真正工作的员工。我深信facetime模式是庞大组织总有无数会议的主 要原因。庞大组织中人均的完成量很少。但是仍然所有的人都需要在自己岗位上,每天呆上8个小时。就好比一个管道,一头进去的时间很多;另一头产生的绩效却 很小。并且会议是保持松懈的主要机制。

曾经有一年,我朝九晚五地按规律工作。我记得在一次会议中,那种奇怪而安逸的感觉涌现出来。我因为 新鲜,记忆非常深刻,那时我正在为编程而辛劳。它听起来有点疯狂,就好象在我桌上有一架机器,不管我做什么,每两分钟吐出一个美元。甚至当我在卫生间的时 候也有这种感觉!但是因为虚构的机器总在不停的运转,我感觉自己一直在工作。所以那次会议我感觉非常放松。当工作时他们计算着,就像在编程,但是做起来简 单多了。你只需要坐着,并且看起来很勤奋。

和网络的效果类似,开会就像鸦片。Email在更小的范围内也是如此。除去即时的直接费用,还有分段的费用——把人们的每天都弄得支离破碎,以至于没法用了。

你 可以看到,通过把它立即移除,你变得多么具有依赖性。所以对于大的公司,我提出以下的这个实验。尝试一天中禁止了开会,在那里人们不得不整天坐着,不能和 别人谈话,工作中不受到打扰。在大多数工作中,相互交流是必要的,但是我敢肯定,每个人单独做自己的工作,能够充分利用了8小时的工作时间。你可以叫它 “工作日”。

另一个问题是,假装工作总比实际工作看起来更真实。当我涂涂写写的时候,我所花的时间和打字时思考的时间一样多。一半的时间 我坐着喝咖啡,或者在邻里间散步。这是一个重要的阶段——想法从中诞生——在大多数办公室里我这样做就会感觉很惭愧了,尤其是别人似乎都在忙碌的时候。

除非你把它和其他事情做对比,否则很难看出来如此的实验是多么的糟糕。这是开源甚至blog显得如此重要的一个原因。它使我们看到了真实的工作情况。

我 们当时启动了八个新计划。一个朋友问,员工他们在办公室里做什么,当我告诉他我们期望员工能把工作间布置得更适宜居住时,他惊讶极了。但是我不想在此节约 金钱。我们如此做,是因为我想让他们编写的软件更好。在如此无拘无束的环境中工作,正是八个启动项目之一。当你就进入办公室,工作和生活就自然而然分开 了。

那是专业化的关键原则之一。工作和生活应该是分开的。但是我深信,这是错误的观点。

由底向上

我们可以从开源和书写blog中学到的第三课,就是我们的想法应该从底向上地冒上来。而不是以命令的形式从上至下布置下去。开源和书写blog都是从底向上的:人们制造他们想要的,所以最好的东西才会流行。

这听起来很熟悉,不是吗?这就是市场经济的原则。但是具有讽刺意味的是,遵循这条原则的,正是没有商业目的的开源和书写blog。而大多数嘴上谈论市场经济的公司们,内部的运行机制就像是共产主义。

在背后有两股驱动力来推动设计:一是关于下一步做什么的想法;二是质量监督。在渠道时代,这两股推动力的流向都是自顶向下。例如,报纸的编辑给记者指派报道的任务,然后编辑们就会编辑记者所写的稿子。

开源和书写blog告诉我们大可不必如此。有关于下一步的想法甚至是质量监督的流向都是自底向上的。在这两种情况下,产生的结果不仅仅是可以接受这么简单,而且好的多。例如,开源的软件更加可信,因为它是开源的,任何人都可以在其中找出错误。

在 写作上同样如此。当就要文章就要发表的时候,我总是对那些还没有在Hackers & Painters发表的文章担心。如果一个文章已经上线并且有了数千的浏览量,我就会对文章有相当的信心。如果没有这些审查的程序,感觉就像在没有测试软 件的情况下发布它一样。

过去所有的出版都是像这样的,如果有十个人去读你的手稿,你就够幸运了。但是我现在十分习惯把文章放在网上,所以老式的方法看起来就不那么可信了。就像你习惯了GPS导航系统以后,再让你用天文观测法来确定你的位置一样。

我 喜欢把文章放在网上的另外一个原因就是,你可以写任何你愿意写的东西,在任何一个时间发表。在今年的早些时候我写了一篇看起来还挺适合一个杂志的文章,我 把它寄给了我认识的编辑。在我等待他们回音的时候,我突然发现我其实更愿意他们拒绝这篇文章,因为这样我就可以把这篇文章发表在网上了。如果他们接受了这 篇文章,在数月之内就不会有任何人阅读到这篇文章。在这同时我还得小心地保存我的文章,让它们不被有二十五年历史的编辑器弄坏。

许多的雇 员想要为他们的公司制造一些伟大的东西,但是管理层经常不会让他们这样做。你有几次听到这样的故事:雇员对管理层说,让我们这样干吧,这一定会赚大钱的 ——然后管理层说”不”呢?最著名的例子就是Steve Wozniak,他最早为HP工作,那时他想为他的雇主制造微型计算机,但是HP拒绝了。相反,在blunderometer方面, IBM演绎了一段趣话,它接受一个非独占的DOS许可证。我们并不是经常听到这样的故事,因为如果你想要证明你是对的,你必须要辞职并且开一家你自己的公 司,就像Wozniak那样。

创业

我认为这些就是开源和书写blog教给商业的:(1)人们遇到他们喜爱的东西总会工作得更努力。(2)标准的办公室的工作环境很没有效率,还有(3)自底向上总是比自顶向下要好。

我可以想像这个时候经理们会说:这个家伙究竟在谈些什么东西?如果我知道我的程序员在家里工作会更有效率,这对我会有什么好处?我需要他们在这里为我们的3.2版本的软件工作,要不我们永远也完不成任务。

对,这是事实。经理们从讲的这些中能得到的利益基本是零。当我说商业可以从开源中学到什么的时候,我并没有说某一个特定的商业。我只是说商业可以像基因一样从新环境中学习。我并没有宣称公司会变得更聪明,只是那些聋子公司将会倒闭。

如 果公司从开源和书写blog中间学到了这些,会有什么改变呢?我想,阻止我们看到商业未来最大的阻碍,就是这个思想:所有为你工作的人都必须是你的雇员。 想想这个想法的背后是什么吧:公司有一些钱,然后他们给雇员工资,只是希望这些雇员可以为他们赚到比工资更多的钱。但是,有更好的可以组织这种关系的办 法。我们为什么不把投资代替工资,付给为我们工作的人呢?这样你就不用到办公室为你的项目工作了,他会在任何地点为他自己的项目工作。

因为几乎没有人知道雇员-雇主关系的替代品,所以我们不知道如何才能做的更好。这样的习惯以极慢的速度进化,到现在还有很多的主仆关系的基因。

两 个极端我都不喜欢:我讨厌为一个顾客工作,同样我也讨厌被我的老板叫去做这做那。自己做老板同样也是令人沮丧的。有一半的时间,你自己做某些事情要比支使 别人去完成工作要更容易。我总是愿意自己完成所有的工作,而不愿意别人对我的工作作出评价,或者是自己对别人的工作作出评价。

由于本身的 原因,雇用关系积累了大量的cruft。 (译注:随着软件的发展,以及经历了修改bug 和更新的若干周期,它的部分代码已不再使用但仍然保留在源码中。这种代码称为cruft 。 cruft 的尺寸范围可由一两行无用代码到整个源文件模块。由于很难识别cruft ,去除cruft 往往很困难。)在工作的面试中有许多你不能询问的东西,我想这些东西的列表应该是无限长的。在办公室中,你做事必须如履薄冰,以免别人说出或作出让公司上 法庭的事情。如果你想要开除某人,最好祈求上帝的祝福吧。

如果公司总是由于解雇员工被起诉的话,我从中看不到任何原本的经济关系。在一个 完全的经济关系下,你可以做任何你想要做的事情。如果你想停止从一个供应商这儿买钢管,而从别处买,你不用解释为什么。没有人可以因为你不公正的变换钢管 供应商而起诉你。公正意味着着某种像父亲般的义务,而这种义务在两个平等个体的交易之间是不存在的。

几乎所有有关雇主的法律规定都是倾向于保护雇员的。但是你不能作出对等的或者是相反的反应。你不能想象雇主对雇员有父亲般的义务,而不把雇员放在孩子的位置上。而且这样看起来也并不会有什么好结果。

下 次你到一个大城市在他们的邮局的时候,可以好好的观察一下里面工作人员的身体语言。还记得小孩子被逼去做自己不愿意做的事情时候的表情吗?他们都是这样的 怨恨的表情。他们的工会成功了提高了他们的工资,并且得到了让邮局的上一辈的工作人员羡慕的工作条件。但是他们看起来并不开心。接受这种”父亲般的”关系 看起来让人士气低落。无论这个词语看起来多么的舒服。你只要去问问你的小孩就可以了。

我看到了雇主雇员关系的的劣处,因为我找到了更好的 关系,那就是投资者-创始人关系。我并不像说这是很容易的事情。当我创业的时候,我们的投资人的想法总是让我在夜里睡不着觉。现在,我变成了投资人,我的 创业者的想法也让我晚上睡不好了。所有你试图去解决的难题仍然会让你很为难,但是如果没有和怨恨的感情混合在一起的话,痛苦会小的多。

我曾经不幸的参加过一个证明这个结论是正确的对照实验。在Yahoo买下我们之后,我们就为他们工作了。除了老板之外,我的工作没有任何变化。但是让我恐惧的是,我开始表现得像一个小孩子了。总是出现莫名其妙的错误。

正如书写blog和开源告诉我们的,投资关系与雇佣关系相比最大的好处就是人们为他们自己的项目工作。而这个时候的人们的效率总是惊人的高的。创业是一个在两个方面属于个人的项目:在经济上和想法上都是属于自己管理的。

Google就是这样一个被我所描述的力量驱动的公司,这样的例子是很少的。他们努力让自己的办公室比一般的那种布满小格子的房间更加有效率。他们给工作出色的雇员大量的股票,以模拟创业成功带来的感觉。他们甚至同意让雇员用20%的时间作自己的事情。

那为什么不让人们把100%的时间都花在他们自己的项目上呢?而不是试图去计算他们创造的价值,然后给予他们实际的市场价值。不可能?这实际上就是风险投资上正在做的事情。

为 什么我宣称以后将不再会有雇员这个概念?每个人都应该去创业吗?当然不是。但是以后会有更多的人这样做。现在,即使是最聪明的学生在离开学校的时候,满脑 子都是找工作的念头。实际上他们要做的是要创造价值。找到一个工作是创造价值的一种方法。但是更有雄心的人应该从投资人而不是雇主那里拿到钱,而且这样往 往他们也做的更好。

另一个使人们不敢创业的就是风险。如果你有孩子还有贷款要还,最好还是三思而后行。但是几乎所有的年轻人都没有这两样东西。

就像开源和写blog告诉我们的一样,即使你失败了,你也会更加的享受过程。因为你在做自己的项目,而不是到办公室去,等着别人给你下命令。在你自己的公司,你可能会有更多的麻烦事,但是痛苦绝对会少得多。

长期看来,这就是开源和书写blog背后的驱动力量带来得最好的效果:让我们忘记那种老式的如父子关系的雇佣关系,而以两个平等个体之间的,完全纯粹的经济关系替代之。

2006年12月07日

如果备份的数据库有2个文件,分别是.LDF 和 .MDF,打开企业管理器,在实例上右击---所有任务--附加数据库,然后选择那个.MDF文件,就可以了。
或者在查询分析器中输入:

sp_attach_db "数据库名称","路径\文件名.ldf","路径\文件名.MDF"

SQL Server数据库备份有两种方式,一种是使用BACKUP DATABASE将数据库文件备份出去,另外一种就是直接拷贝数据库文件mdf和日志文件ldf的方式。下面将主要讨论一下后者的备份与恢复。本文假定您 能熟练使用SQL Server Enterprise Manager(SQL Server企业管理器)和SQL Server Quwey Analyser(SQL Server查询分析器)

1、正常的备份、恢复方式
正常方式下,我们要备份一个数据库,首先要先将该数据库从运行的数据服务器中断开,或者停掉整个数据库服务器,然后复制文件。
卸下数据库的命令:Sp_detach_db 数据库名
连接数据库的命令:Sp_attach_db或者sp_attach_single_file_db
s_attach_db [@dbname =] ‘dbname’, [@filename1 =] ‘filename_n’ [,...16]
sp_attach_single_file_db [@dbname =] ‘dbname’, [@physname =] ‘physical_name’
使用此方法可以正确恢复SQL Sever7.0和SQL Server 2000的数据库文件,要点是备份的时候一定要将mdf和ldf两个文件都备份下来,mdf文件是数据库数据文件,ldf是数据库日志文件。
例子:
假设数据库为test,其数据文件为test_data.mdf,日志文件为test_log.ldf。下面我们讨论一下如何备份、恢复该数据库。
卸下数据库:sp_detach_db ‘test’
连 接数据库:sp_attach_db ‘test’,'C:\Program Files\Microsoft SQL Server\MSSQL\Data\test_data.mdf’,'C:\Program Files\Microsoft SQL Server\MSSQL\Data\test_log.ldf’
sp_attach_single_file_db ‘test’,'C:\Program Files\Microsoft SQL Server\MSSQL\Data\test_data.mdf’
// 本文转自 C++Builder 研究 – http://www.ccrun.com/article.asp?i=986&d=oxit07

2、只有mdf文件的恢复技术
由于种种原因,我们如果当时仅仅备份了mdf文件,那么恢复起来就是一件很麻烦的事情了。
如果您的mdf文件是当前数据库产生的,那么很侥幸,也许你使用sp_attach_db或者sp_attach_single_file_db可以恢复数据库,但是会出现类似下面的提示信息
设备激活错误。物理文件名 ‘C:\Program Files\Microsoft SQL Server\MSSQL\data\test_Log.LDF’ 可能有误。
已创建名为 ‘C:\Program Files\Microsoft SQL Server\MSSQL\Data\test_log.LDF’ 的新日志文件。
但是,如果您的数据库文件是从其他计算机上复制过来的,那么很不幸,也许上述办法就行不通了。你也许会得到类似下面的错误信息
服务器: 消息 1813,级别 16,状态 2,行 1
未能打开新数据库 ‘test’。CREATE DATABASE 将终止。
设备激活错误。物理文件名 ‘d:\test_log.LDF’ 可能有误。
怎么办呢?别着急,下面我们举例说明恢复办法。
A.我们使用默认方式建立一个供恢复使用的数据库(如test)。可以在SQL Server Enterprise Manager里面建立。
B.停掉数据库服务器。
C.将刚才生成的数据库的日志文件test_log.ldf删除,用要恢复的数据库mdf文件覆盖刚才生成的数据库数据文件test_data.mdf。
D.启动数据库服务器。此时会看到数据库test的状态为“置疑”。这时候不能对此数据库进行任何操作。
E.设置数据库允许直接操作系统表。此操作可以在SQL Server Enterprise Manager里面选择数据库服务器,按右键,选择“属性”,在“服务器设置”页面中将“允许对系统目录直接修改”一项选中。也可以使用如下语句来实现。
/*

*/
use master
go
sp_configure ‘allow updates’,1
go
reconfigure with override
go
F.设置test为紧急修复模式
update sysdatabases set status=-32768 where dbid=DB_ID(‘test’)
此时可以在SQL Server Enterprise Manager里面看到该数据库处于“只读\置疑\脱机\紧急模式”可以看到数据库里面的表,但是仅仅有系统表
G.下面执行真正的恢复操作,重建数据库日志文件
dbcc rebuild_log(‘test’,'C:\Program Files\Microsoft SQL Server\MSSQL\Data\test_log.ldf’)
执行过程中,如果遇到下列提示信息:
服务器: 消息 5030,级别 16,状态 1,行 1
未能排它地锁定数据库以执行该操作。
DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
说明您的其他程序正在使用该数据库,如果刚才您在F步骤中使用SQL Server Enterprise Manager打开了test库的系统表,那么退出SQL Server Enterprise Manager就可以了。
正确执行完成的提示应该类似于:
警告: 数据库 ‘test’ 的日志已重建。已失去事务的一致性。应运行 DBCC CHECKDB 以验证物理一致性。将必须重置数据库选项,并且可能需要删除多余的日志文件。
DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
此时打开在SQL Server Enterprise Manager里面会看到数据库的状态为“只供DBO使用”。此时可以访问数据库里面的用户表了。
H.验证数据库一致性(可省略)
dbcc checkdb(‘test’)
一般执行结果如下:
CHECKDB 发现了 0 个分配错误和 0 个一致性错误(在数据库 ‘test’ 中)。
DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
I.设置数据库为正常状态
sp_dboption ‘test’,'dbo use only’,'false’
如果没有出错,那么恭喜,现在就可以正常的使用恢复后的数据库啦。
J.最后一步,我们要将步骤E中设置的“允许对系统目录直接修改”一项恢复。因为平时直接操作系统表是一件比较危险的事情。当然,我们可以在SQL Server Enterprise Manager里面恢复,也可以使用如下语句完成
sp_configure ‘allow updates’,0
go
reconfigure with override
go
 

DBCC results for ‘x’.
DBCC results for ’sysobjects’.
There are 152 rows in 5 pages for object ’sysobjects’.
DBCC results for ’sysindexes’.
There are 165 rows in 14 pages for object ’sysindexes’.
DBCC results for ’syscolumns’.
There are 502 rows in 12 pages for object ’syscolumns’.
DBCC results for ’systypes’.
There are 26 rows in 1 pages for object ’systypes’.
DBCC results for ’syscomments’.
There are 182 rows in 14 pages for object ’syscomments’.
DBCC results for ’sysfiles1′.
There are 2 rows in 1 pages for object ’sysfiles1′.
DBCC results for ’syspermissions’.
There are 50 rows in 1 pages for object ’syspermissions’.
DBCC results for ’sysusers’.
There are 12 rows in 1 pages for object ’sysusers’.
DBCC results for ’sysproperties’.
There are 1 rows in 1 pages for object ’sysproperties’.
DBCC results for ’sysdepends’.
There are 509 rows in 3 pages for object ’sysdepends’.
DBCC results for ’sysreferences’.
There are 6 rows in 1 pages for object ’sysreferences’.
DBCC results for ’sysfulltextcatalogs’.
There are 0 rows in 1 pages for object ’sysfulltextcatalogs’.
DBCC results for ’sysfulltextnotify’.
There are 0 rows in 0 pages for object ’sysfulltextnotify’.
DBCC results for ’sysfilegroups’.
There are 1 rows in 1 pages for object ’sysfilegroups’.
DBCC results for ‘LogForUserSystem’.
There are 197355 rows in 2038 pages for object ‘LogForUserSystem’.
DBCC results for ‘ox_pics_cates’.
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:110) with values (BlockLayerID = 2 and BlockID = 447316 and owner_board_name = ‘emotion.programmer’ and MessageID = 447194) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:111) with values (BlockLayerID = 2 and BlockID = 447316 and owner_board_name = ‘emotion.programmer’ and MessageID = 447202) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:112) with values (BlockLayerID = 2 and BlockID = 447316 and owner_board_name = ‘emotion.programmer’ and MessageID = 447324) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:113) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447162) points to the data row identified by ().
There are 1 rows in 1 pages for object ‘ox_pics_cates’.
DBCC results for ‘res_faces’.
There are 81 rows in 1 pages for object ‘res_faces’.
DBCC results for ‘dtproperties’.
There are 14 rows in 1 pages for object ‘dtproperties’.
DBCC results for ‘accounts’.
There are 5962 rows in 173 pages for object ‘accounts’.
DBCC results for ‘ox_pics’.
There are 7522 rows in 129 pages for object ‘ox_pics’.
DBCC results for ‘forum_msgs’.
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:114) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447164) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:115) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447167) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:116) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447169) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:117) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447171) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:118) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447179) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:119) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447181) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:120) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447185) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:121) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447192) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:122) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447204) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:123) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447215) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:124) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447218) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:125) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447251) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:126) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447298) points to the data row identified by ().
Server: Msg 8952, Level 16, State 1, Line 1
Table error: Database ‘x’, index ‘forum_msgs.IX_forum_msgs_for_0_layer_list’ (ID 768721791) (index ID 6). Extra or invalid key for the keys:
Server: Msg 8956, Level 16, State 1, Line 1
Index row (1:12894:127) with values (BlockLayerID = 2 and BlockID = 447318 and owner_board_name = ‘emotion.programmer’ and MessageID = 447327) points to the data row identified by ().
There are 446359 rows in 12198 pages for object ‘forum_msgs’.
CHECKDB found 0 allocation errors and 18 consistency errors in table ‘forum_msgs’ (object ID 768721791).
DBCC results for ‘ox_pics_catepics’.
There are 1 rows in 1 pages for object ‘ox_pics_catepics’.
DBCC results for ‘ox_pics_accounts’.
There are 1 rows in 1 pages for object ‘ox_pics_accounts’.
DBCC results for ‘ox_bookmarks_byaccount’.
There are 2626 rows in 21 pages for object ‘ox_bookmarks_byaccount’.
DBCC results for ‘link_tables_users’.
There are 1 rows in 1 pages for object ‘link_tables_users’.
DBCC results for ‘account_usergroups’.
There are 1 rows in 1 pages for object ‘account_usergroups’.
DBCC results for ‘account_onlines’.
There are 60 rows in 11 pages for object ‘account_onlines’.
DBCC results for ’search_actions’.
There are 2 rows in 1 pages for object ’search_actions’.
DBCC results for ‘ox_webmsg_msgs’.
There are 18791 rows in 220 pages for object ‘ox_webmsg_msgs’.
DBCC results for ‘event_logs’.
There are 546155 rows in 6897 pages for object ‘event_logs’.
DBCC results for ‘forum_boards’.
There are 28 rows in 1 pages for object ‘forum_boards’.
CHECKDB found 0 allocation errors and 18 consistency errors in database ‘x’.
repair_fast is the minimum repair level for the errors found by DBCC CHECKDB (x ).
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

2006年11月30日

In October 1178 Baldwin set out to construct a castle which would destabilise Saladin’s nascent empire and shift the balance of power in his own favour – the fortress of Jacob’s Ford.

 

He began fortifying a strip of raised ground on the west bank of the River Jordan, beside an ancient ford north of the Sea of Galilee. With swamps upstream and rapids to the south, this ford was the only crossing of the Jordan for 50 miles and, as such, acted as a gateway between Latin Palestine and Muslim Syria.

‘It stood in a frontier zone contested by both Baldwin and Saladin – a kind of no-man’s-land between their respective realms’

But Jacob’s Ford did not lie on the Crusader’s side of a literal border line. Instead it stood in a frontier zone contested by both Baldwin and Saladin – a kind of no-man’s-land between their respective realms. Add to this the fact that Jacob’s Ford was just one day’s march from Damascus, and it becomes clear that Baldwin was, in 1178, adopting an audacious, even visionary, strategy.

His new castle was designed to be a defensive tool as well as an offensive weapon, to severely inhibit Saladin’s ability to invade the Latin kingdom while simultaneously undermining the sultan’s security in Damascus. If completed, this fortress could thwart Saladin’s ambitions for an empire stretching into northern Syria and Mesopotamia.

Baldwin took his new project at Jacob’s Ford exceptionally seriously, committing practically the entire resources of his realm to its construction. Between October 1178 and April 1179 he actually moved his seat of government to the building site to be on hand as supervisor and protector. He also enlisted the aid of the Templars, a military order that combined the ideals of knighthood and monasticism in the sacred pursuit of the Holy Land’s defence

At dawn on Thursday 29 August 1179, the great Muslim sultan Saladin launched a deadly assault on the Crusader castle of Jacob’s Ford in the Holy Land. As his troops poured through a burning breach in the walls, the Christian garrison of elite Templar knights made a bloody, but ultimately futile, last stand.

 

In a final act of bravery the Templar commander mounted his warhorse and charged into the fray. One of Saladin’s lieutenants later described how ‘he threw himself into a hole full of fire without fear of the intense heat and, from this brazier, he was immediately thrown into another – that of Hell’.

 

On that day 800 of the garrison were butchered, and a further 700 taken captive. With the stronghold overrun, Saladin set about razing it to the ground, later claiming that he ripped the foundation stones out with his own hands. The site was then abandoned and for eight centuries it lay untouched, its story all but forgotten.

The true significance of Jacob’s Ford, around 50 miles north-west of Jerusalem, is only now becoming apparent. With its location rediscovered and archaeological excavation underway, it now appears that the fall of this seemingly obscure fortress was actually a pivotal moment in the history of the Crusades as well as the wider struggle between Islam and the West.

2006年11月29日

    395年,原先统一的罗马帝国终于分裂为东西两部分,即以君士坦丁堡为首都的东罗马帝国和以罗马为首都的西罗马帝国。君士坦丁堡是古希腊移民城市拜占庭的旧址,所以东罗马帝国又习称拜占庭帝国,君士坦丁堡习称拜占庭。到了15世纪中叶,东罗马帝国面临内外交困的局面,绝大部分领土被兴起的奥斯曼帝国占领,实际上只剩下首都君士坦丁堡这座四面受围的城市了。国内政治纷争不断,连年混战,从而经济凋敝,税收锐减,不但完全失去了作为地中海上一支商业劲旅的地位,而且被迫听任热那亚和威尼斯的商人在帝国境内建立许多商业据点,享有种种特权。东罗马帝国已处于风雨飘摇之中。1453年5月29日,君士坦丁堡终于被奥斯曼土耳其人攻占,随后奥斯曼帝国迁都于此,更名伊斯坦布尔。君士坦丁堡的陷落,标志着在西罗马帝国灭亡后继续存在将近一千年的东罗马帝国的灭亡。欧洲历史从此揭开新的一页。

    奥斯曼土耳其是突厥人的军队,如今的土耳其也对全世界突厥人开放国籍。但是,当年奥斯曼土耳其人占领小亚西亚后,历经数百年演化;与当地大量的希腊,亚美尼亚等白种民族融合(当初奥斯曼人习惯于占领一地后,将当地男子杀光,女人则编入为士兵家眷)。从纯血统看,部分西部土耳其人比希腊人更接近古希腊人种,后果是导致现今土耳其人血统极为混杂;号称突厥人领袖的他们,一直很忌讳谈论祖宗到底是谁。后期的奥斯曼苏丹基本上就是希腊或法国人血统,身上的突厥血统基本可以忽略不计,这也是后宫妃子全部为希腊或法国人的后果。

    奥斯曼帝国是一个民族的大杂烩,有突厥人、希腊人、犹太人、亚美尼亚人、巴尔干的诸多民族构成。至于现在的土耳其,犹太人和希腊人都只占一小部分,绝大部分是信仰伊斯兰教的突厥化的各民族。

2006年11月28日

记者:你好像看过很多的文学作品和听过很多的音乐?

黄健翔:任何一个知识分子,一个读书人都这样吧。

记者:你是知识分子吗?

黄健翔:我本科毕业,勉强算吧(笑)。我们家世代是读书人。你还问我是不是知识分子!余华的《兄弟》,上部是只有他写出来的,但是下部别人也能写出来。我喜欢《活着》。

记者:那你还挺知识分子的。

 

以上是引发黄健翔发怒的记者吴虹飞写的一段采访稿。很多人不屑于黄健翔自称知识分子,我却很支持黄健翔的这种自我认知。

什么是知识分子?难道非要具有研究生以上学历或者在高校工作的,才叫知识分子?那些本科毕业,发现自己一无所知,找不到工作,只能靠进一步获得学历而去骗取工作的无能之辈,就是知识分子吗?或者是倚老卖老,靠攫取手下的研究生的成果为生的“老专家”,就是知识分子吗?

莫尔索(加缪《局外人》),这位世界文学中真正具有知识分子意识的著名人物,只是一个大学预科毕业的学生。五四时期梁漱溟之类的大学问家,也未见得有什么“学历”。

黄健翔可以看作典型的江南读书人,有一份正常的工作,但自己还是爱读书,爱音乐。读书,音乐是自己的事情,不需要向人汇报,外人更不必因为这位“体育解说员”读过余华,有音乐鉴赏能力而唧唧歪歪。那帮无知的记者们,闭嘴吧。

2006年11月10日

就会战艺术和出色的战阵控制而言,汉尼拔在东西方的历史都可以说少有匹敌。只有拜占庭的Belisarius才可以和他相提并论。在这方面,无论是项羽还是韩信恐怕难忘其项背。这和东西方军事思想体系的侧重点不同有关。所以说汉尼拔在战术领域处于世界军事史上的顶尖地位,应无可争议。

那么汉尼拔在战略领域的成就究竟如何呢?我们不妨根据第二次布匿战争的前后过程对此作一简单的分析。我们可以参考杜普伊的《战略之父汉尼拔的军事生涯》,考虑到杜普伊的陈述不一定完全符合真实,我尽量采用一些最基本的情节。

战略这一概念,如果要认真地作一精确定义,恐怕是极其庞大的工程。对于战略这一概念,不同的军事理论的理解其实都有些许微妙的差别。即使是克劳塞维茨的定义也很难说没有可修正之处。不过这里想回避这一讨论,而采用另一方法,即选取战略这一概念的共同核心以便大致使得战略这一概念能够和其他概念区分开来。

就我们下面将要用到的战略概念而言,战略不外乎两个要素:第一个核心要素当然是“计划”。一个完全由战场的偶然因素推动的战争进程,显然不能说事先有一个战略。第二个核心是必须对战争进程中的一些可能的变化要有预见,并对这些变化准备一定的应变措施。如果没有这一点,所谓计划不过是僵硬的条条框框而已。当然这仅仅是战略这一概念的必备要素,而不是全部要素。其实这两个要素也是(战役)战术概念的必备要素。所不同的是,战略和(战役)战术所考察的范围和层次不同,它们对于作战进程中各种变化的预测也有各自完全不同的规律和理论。

汉尼拔在决定远征意大利的时候,显然是有战略计划的。单单将这支庞大的军队运到意大利本身,没有一定的计划就不可能成功。从他远征出发以前的一系列准备活动看,汉尼拔显然考虑了很长时间。

那么当汉尼拔还身处伊比利亚的时候,他对于进军意大利的远征行动究竟看到了什么呢?斯人已逝,要想获得完全真实的真相已不可能。但我们仍可从后来的战争进程推测一二。

对于汉尼拔来说,行动的第一步是把远征军以尽可能小的代价运到意大利。对于他来说只有两种选择:海运或者采取史无前例的翻越阿尔卑斯山的路线。汉尼拔显然对此作了相当全面的考虑而选择了后者。从后来的进程来看,选取这一路线大军的损失仍然超过了预计,还好仍在可控制的范围。汉尼拔依靠自己的卓越能力从土著那里获得的补充弥补了这一损失。所以说,这一战略步骤仍属成功。

在进入意大利以后,可以预见远征军或早或晚会和北上迎击的罗马军团遭遇(实际进程中,这一遭遇发生得比预计还要早)。对此,汉尼拔的对策可以是非常简单,那就是伺机消灭之。如何消灭,则可以交给战术系统去解决。这里,我们可以看到,如果战术系统足够强大,那么战略系统可以大大简化。实际进程中,汉尼拔在从波河流域到特拉西梅诺湖的一系列战斗非常漂亮,可以说大大超出预计目标。

解决了罗马外围军事力量以后(实际进程中,罗马当时的主力已被消灭),汉尼拔可以直接进逼罗马了。这时,汉尼拔的选择突然多了起来。以我们事后的分析,汉尼拔大致可以有以下几种选择:

1. 直接进军罗马
直接进军罗马的好处是可以有效隔绝罗马和其属地之间的联系。但在目前这一阶段,时机尚未成熟。这倒并非汉尼拔没有攻城器械的缘故。只要建立一个小小的根据地,不消几个月就可以建造出各种攻城器械。此时进攻罗马的危险在于,罗马目前仅仅是受到了重创,但距离致命还有很大距离。罗马和同盟国之间的关系还完好无损。这样剩余的罗马力量如果将同盟国的力量纠合起来,可以对攻城部队构成致命威胁,并可能随时扭转战局。因此,对于汉尼拔来说,进一步打击罗马的残余力量,并瓦解罗马和同盟国之间的关系显然要比进攻罗马优先。

2. 经营北方以对罗马构成持续的威胁
这一策略未必会为西方学者看重,但从东方的战略思想看,这一策略有其独到之处。具体设想是在特拉西梅诺湖之战后,将战线推进到罗马附近,然后停止并经营北方。这一策略可以对罗马保持一种持续的压力。但这一压力仍属于战略性质的,而非战术性质的直接压力。并且罗马同盟国主要在南方,这一策略并不能起到分化瓦解罗马同盟势力的作用。如果远征军在深入重地后,已经师老兵疲,那么这一做法也许比较适合。但这一策略对于当时自波河以来连战皆捷、气势正盛的远征军来说却并不合适。尤其是汉尼拔在战术上非常强大,可以采取更为进取的方法向罗马施加更直接的压力。从实际进程中,汉尼拔迂回翻过亚平宁山脉、穿越沼泽直插敌后的举动看,汉尼拔显然对此未予考虑。

3. 在野外进一步消灭罗马的有生力量,并最终达到进逼罗马的目的
对于汉尼拔来说,这是一种更为直接,也是更能发挥汉尼拔所长的策略。具体来说,它包括这样几个步骤:在野外寻机进一步消灭罗马的有生力量,并瓦解罗马和同盟国之间的关系。在确立了远征军在野外的主宰权以后(这是一个类似于制空权的概念),迫使罗马残余势力退缩回城内,从而为最后围攻罗马作好准备。从汉尼拔在伊比利亚的一些攻伐行动来看,这应该是汉尼拔在伊比利亚时就已经看到的东西。这一策略简单易行,后来迦太基的陷落大致也是类似的过程。一切看来都不错,至少汉尼拔在伊比利亚出发以前可能是这么估计的。不过,我们这位不世出的军事天才也许根本没有料到,他后来将要遇到的对手之一选择了一种貌似愚笨而实质充满睿智的策略,彻底打乱了汉尼拔的精心设计。

4. 打击罗马的战争基础,削弱罗马,并最终击败罗马
无法攻城,同时又无法找到野战的机会,汉尼拔剩下的选择就是打击罗马的战争基础。在汉尼拔的时代,这是通过对乡村的蹂躏实现的。在实际的战争进程中,汉尼拔或多或少地执行着这一策略。但我们有理由相信,这并非汉尼拔在伊比利亚的设想,选择这一策略在很大程度上是因为前述策略未能得以成功实现的前提下的被迫之举。表面上看来,这一策略对于汉尼拔是无所损失的。但实际上这一策略隐含着巨大的危险:因为,除非汉尼拔的这种打击最后造成罗马在兵员补充和后勤补给上造成严重困难并直接影响到罗马的战争能力,否则,这种打击非但不能削弱罗马,反而会使整个罗马社会被逐步锻造成一个具有极高效率的战争机器。如果对于乡村的蹂躏并不能导致罗马人口的锐减或粮食生产的中断,而只是造成了罗马人生活水平的下降,那么势必导致整个罗马社会成为一个高度团结的整体,并愿意为了赢得战争的最后胜利付出巨大的代价和牺牲。从后来的历史进程看,很难说第二次布匿战争对整个罗马社会的这种锻造没有对后来的罗马的崛起产生了促进作用。

在我们今天看来,我们很难说汉尼拔曾经在这样的层次上意识到这种打击手段的结果。事实上,如果汉尼拔有意识地使用这一手段的话,他的打击对象应该是罗马附近地区,而不应是同盟国地区。

现在我们来看看汉尼拔前期的对手法比乌斯(Fabius)的情况。法比乌斯在接掌罗马军事力量的指挥权的时候,正是罗马处于最危急的时刻。法比乌斯在此时采取的措施,在杜普伊的著作中有非常准确地描述:

   法比乌斯的兵员比汉尼拔多,但他们大部分是缺乏经验的新兵,其对手却是迦太基久经沙场的老战士。可是他已经确立起一个军需补给系统,因此他不必象汉尼拔那样不得不派遣部下去乡村收集粮草。但是法比乌斯的骑兵力量薄弱,而他知道汉尼拔在这方面特别强。此外,汉尼拔已经由于善施计谋、精于用兵、所向无敌而树立威名,因此法比乌斯决心不拿全军的伤亡去冒险,而是利用手中这支军队把迦太基人拖得失去耐心,同时防止罗马的同盟者倒戈投向迦太基人。就这两个目标而言,他是完全成功的。

   在此后的几个月中,汉尼拔试用了他所能用的一切手段力图引法比乌斯出战。他频繁转移营地,当着罗马军团的面肆意蹂躏沿途地区。他一次又一次地设下圈套,可是都毫无用处。法比乌斯始终使其部下对汉尼拔处于居高临下的位置,远远地尾随着他,但就是不上汉尼拔的当,不和他进行较量。然而他们却特别注意迦太基军队的落伍士兵,经常袭击汉尼拔正在进行劫掠的那些小股部队。因此,小冲突时有发生,而罗马人往往因在人数上占上风而得胜。
…….
   两军都据守在冬营里,没有什么重大军事行动,但是小冲突却数不胜数,这就使大部分罗马士兵获得了一些可贵的经验。

法比乌斯并非一味避战,但战斗必须是在他所选择的地点进行。在坎帕尼亚的隘口法比乌斯找到了一次战机,但是天才的汉尼拔耍了一个小小的计谋,就使法比乌斯的打算完全落空了。但这并不妨碍法比乌斯继续其一贯的策略,避战但始终处于监视的位置。这在事实上有效阻止了汉尼拔获得野外的绝对控制权的计划。

罗马军团就在这样一种策略之下,慢慢地成长,然而这种成长的后面隐藏着这样一个诉求:有一天罗马会在战场上击败迦太基。

可惜法比乌斯的执政期很快届满,几个轮次后的新执政官已经忘却了从波河直到特拉西梅诺湖建立起来的血的教训。瓦罗在坎尼的拙劣指挥几乎使法比乌斯好不容易建立起来的基础毁于一旦。

好在罗马迅速回归谨慎的路线,不过此时罗马执政官们的避战,与其说是出于一种谨慎的策略,毋宁说是出于对这位天神般的军事天才莫名的敬畏。但这总算可以使罗马避免再次遭到致命打击了。此后,罗马的力量在逐渐恢复中,开始对倒向汉尼拔的那些同盟国施以惩罚。并多次成功阻止了汉尼拔几个兄弟与其会合。但始终没有人胆敢向汉尼拔本人挑战。

然而,正确的战略只是为了最后的决战作准备。但罗马却一时无法找到合适的领军人选。罗马只好等待……

这一等就是十多年。

显然,扎马之战前夜的西庇阿肯定是在极度焦虑中度过的,但当次日罗马军团第一条战线里齐集的号兵,面对冲过来的战象一起吹响他们的号角时,罗马应该明白他们终于找到了可以和这位令人敬畏的军神对阵的人选了。

汉尼拔,最终是败在他的学生手里的…..

今天的分析家都会指出第一二线新兵的溃散是汉尼拔在扎马的首要败因。然而,是什么原因造成汉尼拔在第一二线上只能部署新兵的呢?如果汉尼拔带回北非的是一支步骑完整的军队,扎马之战还是现在我们看到的结果吗?

至此,我们会看到法比乌斯的拖延战术并不仅仅是“利用手中这支军队把迦太基人拖得失去耐心”。


夫战胜攻取,而不修其功者,凶,命曰费留。故曰:明主虑之,良将修之,非利不动,非得不用,非危不战。主不可以怒而兴师,将不可以愠而致战;合于利而动,不合于利而止。怒可以复喜,愠可以复悦,亡国不可以复存,死者不可以复生。故明君慎之,良将警之,此安国全军之道也。

谨以此表达对汉尼拔以及他所热爱的迦太基的哀婉之情……

2006年11月08日

一、赫梯(Hittites)的前期历史概略

赫梯是上古的一个强国,其强盛期在公元前2000年至前1200年之间,中心在今小亚细亚中、东部。关于赫梯人的人种归属与来源,以前几乎一无所知,近一二百年由于在考古学上取得了不小的进展,特别是一批古赫梯的铭文的出土,使赫梯这个神秘的古国的面目逐渐浮出了水面。

在 小亚细亚高原的中部、东部的Halys河流域(注:就是后来的卡帕多西亚地区),最初已知的上古居民自称为哈提人,主要是一些零散的游牧集团,非印欧语 系。大约前2000年初,从高加索南下(最初来源地可能是黑海北岸或巴尔干)的一支印欧语系民族(操涅希特语)来到此地,征服了土著的哈提人,并以其为主 体融合成为赫梯人(注:原来的哈提人在学术著作中常被称之为原始赫梯人)。赫梯人不断地向东部、南部进行侵寇,原驻于叙利亚、巴勒斯坦一带的喜克索斯人 (Hyksos)就是受到其压迫而南迁占领埃及的。以后类似的多米诺骨牌效应式的民族迁徙屡见不鲜,最后遭殃的往往都是那些积弱的定居民族。

在 公元前18至17世纪之交,赫梯人开始形成了几个城邦式的小国,互相争夺霸权。其中最大的有涅萨、库萨尔和察尔帕,最终库萨尔之主阿尼塔征服了涅萨并都于 此。其后嗣者之一拉巴尔纳斯一世(Labarnas I)继续征服其他部落,称王建国,是外赫梯的古王国时期之始。至其孙穆尔西利斯一世(Mursillis I) 时迁都于原始赫梯人的中心哈图萨斯(注:今土耳其波尔兹科伊),并在前1600年左右南下毁灭了喜克索斯人在叙利亚的据点哈尔帕(注:不久喜克索斯人就在 埃及第18王朝和赫梯的南北夹击下崩溃),前1595年前后又洗劫了巴比伦城,灭亡了古巴比伦王国,回师时又击败了胡里人。赫梯遂威名远震,成为大国,疆 域东至两河流域西北部,北至库麦什马哈什河,南至叙利亚。穆尔西利斯死后国家内乱了几十年,至前十六世纪后期铁列平王(Telepinus)即位,颁布王 位继承法,以禁止王族内讧。

至前15世纪,赫梯进入了新王国时代,再次强大,并开始南向争霸。而此时早已驱逐了喜克索斯人的埃及第18王 朝先一步进入了全盛期,在法老图特摩斯三世(Tuthmosis III)时重新确立了对叙利亚和巴勒斯坦的统治,西亚的一些小国纷纷与埃及交好。但埃赫那吞(Akhenaton)即位后,由于取消了长久以来崇拜的阿蒙 神,强制改崇新创立的太阳神阿吞(Aton),忙于国内斗争,无暇于外部事物。适赫梯南下,当时的这两大超级帝国不可避免地在他们的中间地带-叙利亚发生 了碰撞。

二、叙利亚的争夺

赫梯南下的第一个障碍是他们的老对手-米坦尼,米坦尼为胡里人所建,先前曾被穆尔西利 斯一世打败,赫梯内乱时曾强盛,占有叙利亚北部与两河流域北部,一度南抗埃及、北敌赫梯。至赫梯再兴时其国势已衰,转与埃及结盟,成为两大国间的缓冲国。 (时米坦尼据有北叙利亚,埃及则领有南叙利亚与巴勒斯坦)

约前1375年前后,雄才大略的苏皮卢琉玛斯一世(Suppiluliymas I)为赫梯王,正式开始了赫梯向南的征服行动。初期他曾在西边的陶鲁斯隘道等地对米坦尼发动过几次正面的试探,并没得到什么进展。于是改变了战略,采用迂 回战术,出奇兵自玛拉提亚(在小亚东部)附近渡过幼发拉底河,从北面一举夺取了米坦尼首都Vashuganni。然后回师西向,再渡幼发拉底河,从后面攻 占了米坦尼人在叙利亚北部据点哈尔帕(今阿勒颇附近)。经此一战,米坦尼大部领土被赫梯占去。以后赫梯扶立玛提瓦扎(Matiwaza)为王,作为与新兴 的亚述之间的缓冲小国。

苏皮卢琉玛斯打败米坦尼后,随即引兵南下埃及在叙利亚的北部边境,击溃了臣服于埃及的卡迭石王公的抵抗,一直进至 大马士革以北。时埃及仍亡于内争,坐视了米坦尼的失败和北部疆土的失陷而无力出兵。其后苏皮卢琉玛斯回师本土,其后约二十年时间忙于征服小亚西部的诸小 国,留下其子泰利皮努斯以大祭司身份管理叙利亚的防务。约前1340年其后,苏皮卢琉玛斯卷土重来,这一次他经过八日的围攻战拿下了地中海岸靠近幼发拉底 河的卡赫美什重镇,封两子分别为哈尔帕和卡赫美什之王,并与臣服赫梯的叙利亚诸小邦王公缔约结盟,至此北叙利亚(南至大马士革)已牢牢地控制在赫梯手中。

埃 及图坦哈蒙(Tutanhamun)为法老时,曾一度出兵亚洲企图恢复旧土,但没有动摇赫梯在叙利亚的霸权。其死后寡后安开孙巴阿吞曾致函苏皮卢琉玛斯, 请求派赫梯王子与她结婚,以便成为埃及法老。但当赫梯王子至埃及时,王后已被迫与宫廷祭司阿伊(Ay)成婚,阿伊成为法老,赫梯王子被杀。两国再次开战, 赫梯占有了叙利亚,但带来的副作用是:俘获的埃及俘虏给赫梯国家带来了传染病,使得其人口减少,扩张也陷入了停顿。

约前1315年前后, 埃及建立了第19王朝,第一任法老拉美西斯一世(Ramses I,因前长期担任前朝大臣或统帅,即位时已是高龄,仅在位两年)与其子西提一世(Seti I)共治,改革了军队,使国家复兴。西提一世出兵镇压了巴勒斯坦和腓尼基地区的反埃及人的起义,并在与赫梯人的战争中取得小胜,并一度使卡迭石王公臣服。 他重新规复了巴勒斯坦、腓尼基、南叙利亚等地,北叙利亚仍在赫梯人之手。

至西提一世之子拉美西斯二世(Ramses II,大王)即位,继续其父的政策,改进了军队,并从上游的旧都底比斯迁都至三角洲东部的新都塔尔.拉美斯(“拉美西斯之城”),以便于将军队迅速调往亚 洲战场。而一方,此时的赫梯王穆瓦塔立斯(Muwatallis)也深知与埃及的冲突不可避免,一面假惺惺地派使节到埃及祝贺拉美西斯二世即位法老,一面 也在做狂热的战争准备,一面移驻于靠近叙利亚的新都达萨塔,而将后方交给他的兄弟哈图西利斯,并积极争取西部诸邦的支援。此时,这种形势很象春秋时期晋楚 争霸的局面,双方的焦点都在于其势力范围的交汇点-叙利亚(好比是郑国),等待他们的将是一次鄢陵式的会战-卡迭石会战。

三、卡迭石战前两国的军队

埃 及第18王朝建立后,古老的民军逐渐过渡到了一支组织良好的陆军。最初分为两个“大师”(Grand division),分别驻于上埃及和三角洲。陆军兵种由步兵和战车兵组成,士兵装备有弓和矛,携带防盾,起初无甲胄,战斗中弓箭已是成排发射。从喜克索 斯人那里学会了战车,战车制造精良,成了一种艺术,养有大量的马匹。军人的待遇很高,发给土地、财物与奴隶,据第19王朝初期铭文记载:每个采石场的军人 每天可发给约1.8公斤的面包、煎肉块、两束青菜,每月可领两件麻衣。前15世纪在叙利亚的美吉多战役中,战利品就有924辆战车、2238匹马、200 副盔甲(此后埃及人也学会了使用盔甲),大致可见战争规模与兵器使用情况。

埃及军队中很大一部分是外族人。早在反喜克索斯人的战争中,埃塞俄比亚人就开始在埃及军中服役,整个第18、19王朝,埃塞俄比亚人、叙利亚人、努比亚人、舍尔丹人等外族雇佣兵所占比例很大,通常由埃及军官统率。外族军人在地位上与埃及人大致平等,也赐有份地、财物等。

及 拉美西斯一世建立第19王朝,在与西提一世共治期间,进一步改编了埃及军队:将常备军分为三个军团,分别以底比斯城等三城主神命名(阿蒙、拉、塞特),每 个军团仍以传统步兵为主,但都配备有战车队,以增强机动性与攻击力;大量招募外族雇佣军人以守卫边境要塞和道路(如埃及在叙利亚的驻军主要由外族人组 成)。拉美西斯二世即位后,又组建了第四个军团(普塔赫军团),并且率领这四个军团踏上了卡迭石会战的征途。

赫梯人拥有巨额的常备军,兵 种主要是战车和步兵。其中战车兵是主要打击力量,贵族均编入战车队,赫梯的战车为双马,三名乘员(一名驭手,一名持盾兵,一名装备弓箭和标枪的战士),比 埃及战车要大(埃及战车一般两人),且数目庞大(卡迭石之役赫梯投入作战的战车达3500辆);赫梯的步兵处于一种辅助地位,战斗主要由战车兵来决定。前 14世纪时赫梯军队中开始装备部分铁制兵器(世界上最早),而埃及军队装备的仍然是青铜兵器。赫梯军队在装备上要强于埃及军队。在卡迭石之战时,赫梯的西 部各小盟邦也派来了一批为数不少的援军。

四、卡迭石会战

拉美西斯二世即位后的第五年(时间仍在争论,有前 1312、1294、1285年等多种说法)四月末,他亲自率领四个军团(每个军团约五千人)2万余人从三角洲出发,沿海岸北上,远征叙利亚。在途径阿穆 路时留下一支分队(后来该分队折向东,在会战关键时刻赶到了卡迭石)。在出发的第29日进至卡迭石城附近宿营,此时法老对敌方军情尚无确切了解。

早 在拉美西斯二世刚出征时,这个情报以被赫梯的“内间”获悉,报告了赫梯王穆瓦塔立斯。穆瓦塔立斯征集了约16000-20000人的军队、战车约3500 辆,以逸待劳,在卡迭石附近隐蔽设伏,准备给法老以迎头痛击。当他获悉法老大军来到时,派出两名“死间”伪装成贝都因游牧人,故意被埃及军捕获,向法老提 供了如下假情报:赫梯军主力仍在距此很远的哈尔帕,因畏惧法老不敢前来;叙利亚诸王公皆愿内附,迎接法老,等等。法老闻讯大喜,次日晨亲率阿蒙军团在前, 后面依次是拉军团、普塔赫军团、塞特军团,各军团间隔有一段距离,一步步向赫梯王设下的陷井进发。

当拉美西斯二世率阿蒙军团从东南方渡过 奥伦特河,沿河西岸北上至卡迭石西北扎营时,审问俘虏知道赫梯大军就在附近,大惊失色,立刻派人催促后续军团赶来。但为时已晚,赫梯战车从伏击地冲出,从 侧翼急袭正在行军途中的拉军团,将其一举击溃。随后,2500辆赫梯战车围住阿蒙军团的营地猛攻。拉美西斯二世的卫队与阿蒙军团陷于苦战,法老虽一再祈求 阿蒙神保佑,仍节节退守,眼看即将被歼。但赫梯人的组织纪律上看来比埃及人稍逊,不少人忙于在战场上抢劫埃军财物,影响了进攻的威力。

正 在此千钧一发之时,埃及军留在阿穆路的那支分队从间道而至,从侧后打击赫梯军,减轻了法老的压力。与此同时,普塔赫军团与一部被打散的拉军团也及时赶到。 法老终于化险为夷,组织起了反攻,连续冲锋了六次,把不少赫梯战车赶入河中。赫梯王再次投入了剩下的1000辆战车,仍未取得优势。入夜后战斗结束。时赫 梯还有约8000名步兵没有参战,作为预备,而埃及的塞特军团也还没投入战斗。赫梯王因所有战车都已交战,未有优势,次日致函法老,双方均同意停战。埃军 南撤往阿巴(大马士革附近)。

卡迭石之战,双方的铭文中都宣称自己取得了重大胜利,但实际上只是一次平手,都损失巨大。从战略上看,埃及的战略目的没有达到,赫梯在叙利亚北部的地位更加牢固了。

五、后话

卡 迭石会战后,双方又进行了16年的战争,但规模较小。拉美西斯二世根据经验教训,采取了稳扎稳打的战略,占领了奥伦特河流域的一些城市;赫梯人的对策是固 守北叙利亚的各城堡,避免与埃及军野外决战。最后双方缔结了合约,结束了长期的战争,合约要点为:两国结为同盟,互相支援;均不得接纳对方的逃亡者。根据 此约,包括阿穆路在内的叙利亚北部归赫梯所有,南叙利亚及沿海地区归埃及所有。数年后,赫梯王以长女嫁与拉美西斯二世为妻。

合约签订以 后,这两个大国很快转衰。赫梯面临东面新兴的亚述的打击,而西面来自爱琴海的“海上民族”又从安纳托利亚西部入侵,在双重压力下终于在前1200年左右崩 溃,分裂为诸小国,苟延残喘至前八世纪末相继为亚述征服。赫梯崩溃的同时,埃及也在遭受着东面的“海上民族”腓力斯丁人和西面的利比亚人的入侵而苦苦挣 扎,虽以后一度又有短暂的辉煌,但长期以来始终处于内忧外患交加的局面而难以自拔。

2006年10月12日

    最近一直在用python处理xml文件,查阅了不少资料,看到一篇挺有用的文章,所以转过来与大家分享。

 

ElementTree,一个用于 Python 的本机 XML 库。在 Python 的标准分发版中早已包括了几个 XML API,包括:DOM 模块、SAX 模块、 expat包装器和不赞成使用的 xmllib。其中,只有 xml.dom将 XML 文档转换为内存中的对象,您可以通过节点上的方法调用来操作这些对象。实际上,您将发现存在几种不同的 Python DOM 实现,其特性各有不同:

  • xml.minidom是一个基本的实现。
  • xml.pulldom只在需要时构建被访问的子树。
  • 考虑到速度问题,4Suite 的 cDomletteFt.Xml.Domlette)用 C 语言构建 DOM 树,避免了使用 Python 回调。

当然,出于我身为作者的自负,我最想做的是将 ElementTree和我自己的 gnosis.xml.objectify及其它几种目的和行为都极其接近的库进行比较。 ElementTree的目标是以数据结构的形式存储 XML 文档的表示,这些数据结构的行为方式同您在 Python 中考虑数据的方式非常相似。这里的关注焦点在于以 Python 进行编程,而不是使您的编程风格顺应 XML。

一些基准测试

我的同事 Uche Ogbuji 曾为另一个出版物写过一篇关于 ElementTree的短文。(请参阅 参考资料。)他对 ElementTree和 DOM 做了几个测试,其中之一比较了它们的相对速度和内存消耗。Uche 选用了他自己的 cDomlette 作为比较对象。很遗憾,我不能在我使用的 Mac OSX 机器上安装 4Suite 1.0a1(我正在研究一种变通方法)。然而,我可以根据 Uche 的评估来估计大致性能 - 他指出 ElementTreecDomlette相比,速度慢 30%,但消耗的内存也要少 30%。

我极为好奇的是, ElementTreegnosis.xml.objectify在速度和内存上比较,结果会如何。实际上,之前我从未对我的模块进行过非常精确的基准测试,因为我始终没有一个具体的 可比对象。我选择了两个过去我曾用于基准测试的文档:莎士比亚的 哈姆雷特289 KB XML 版本,及 3 MB XML Web 日志。我创建了几个脚本,仅用于将 XML 文档解析为几种工具的对象模型,但此外不作任何其它操作:

清单 1. 对用于 Python 的 XML 对象模型计时的脚本

% cat time_xo.py

        import sys

        from gnosis.xml.objectify
        import XML_Objectify,EXPAT
doc = XML_Objectify(sys.stdin,EXPAT).make_instance()
---
% cat time_et.py

        import sys

        from elementtree
        import ElementTree
doc = ElementTree.parse(sys.stdin).getroot()
---
% cat time_minidom.py

        import sys

        from xml.dom
        import minidom
doc = minidom.parse(sys.stdin)
      

在所有三个案例中,程序对象的创建非常类似,对于 cDomlette 也一样。我在另一个窗口观察 top 的输出,以评估内存使用情况;每种测试进行三遍以确保其一致性,并取其结果的平均值(每次运行使用的内存是相同的)。

图 1. 以 Python 编写的 XML 对象模型的基准测试结果
以 Python 编写的 XML 对象模型的基准测试

很明显,对于稍大一点的 XML 文档, xml.minidom很快就变得不实用了。其它则都还算合理(公正地说)。 gnosis.xml.objectify消耗内存最少,但这并不奇怪,因为它不保存原始 XML 实例中 所有的信息(保存了数据内容,但不保存所有的结构信息)。

我也对 Ruby 的 REXML进行了测试,使用了以下脚本:

清单 2. Ruby REXML 解析脚本(time_rexml.rb)


require "rexml/document"
include REXML
doc = (Document.new File.new ARGV.shift).root

测试结果表明, REXMLxml.minidom一样消耗大量资源:解析 Hamlet.xml 用了 10 秒,占用了 14 MB 内存;解析 Weblog.xml 用了 190 秒,占用了 150 MB 内存。显然,编程语言的选择通常优先于库的比较。


回页首

处理 XML 文档对象

ElementTree 的一个优点在于它能够被循环运行。这是指,您可以读入一个 XML 实例,修改数据结构使之非常类似于本机风格,然后调用 .write() 方法进行重新序列化得到格式良好的 XML。当然,DOM 也能做到这一点,但 gnosis.xml.objectify不行。为 gnosis.xml.objectify构造一个定制输出函数用于生成 XML 也不是 那么困难 - 但这不能自动进行。使用 ElementTree 以及 ElementTree 实例的 .write() 方法,通过便利函数 elementtree.ElementTree.dump() 可以序列化单独的 Element 实例。这让您可以从单独的对象节点 - 其中包括 XML 实例的根节点 - 编写 XML 片段。

我提出了一个简单的任务来比较 ElementTreegnosis.xml.objectify 的 API。用于基准测试的大型文档 weblog.xml 包含大约 8,500 个 <entry> 元素,每个元素都含有相同的子字段集合 - 这是一个面向数据的 XML 文档的典型布局。在处理该文件时,任务之一可能是从每一个 entry 收集一些字段,但这只是在其它某些字段有特定值(或范围,或匹配的部分内容)的情况下。当然,如果您确实只想要运行这一个任务,可使用一个流 API(如 SAX)以避免在内存中为整个文档建模 - 但这里假定该任务是应用程序在大型数据结构上运行的任务之一。一个 <entry> 元素可能像这样:

清单 3. <entry> 元素样本


<entry>
  <host>64.172.22.154</host>
  <referer>-</referer>
  <userAgent>-</userAgent>
  <dateTime>19/Aug/2001:01:46:01</dateTime>
  <reqID>-0500</reqID>
  <reqType>GET</reqType>
  <resource>/</resource>
  <protocol>HTTP/1.1</protocol>
  <statusCode>200</statusCode>
  <byteCount>2131</byteCount>
</entry>

如果使用 gnosis.xml.objectify,我也许会这样编写一个“过滤和抽取”应用程序:

清单 4. “过滤和抽取”应用程序(select_hits_xo.py)


        from gnosis.xml.objectify
        import XML_Objectify, EXPAT
weblog = XML_Objectify(
        'weblog.xml',EXPAT).make_instance()
interesting = [entry
        for entry
        in weblog.entry

        if entry.host.PCDATA==
        '209.202.148.31' 
        and
             entry.statusCode.PCDATA==
        '200']

        for e
        in interesting:

        print
        "%s (%s)" % (e.resource.PCDATA,
                     e.byteCount.PCDATA)
      

列表理解用作数据过滤器是相当方便的。本质上, ElementTree的工作方式与此相同:

清单 5. “过滤和抽取”应用程序(select_hits_et.py)


        from elementtree
        import ElementTree
weblog = ElementTree.parse(
        'weblog.xml').getroot()
interesting = [entry
        for entry
        in weblog.findall(
        'entry')

        if entry.find(
        'host').text==
        '209.202.148.31' 
        and
             entry.find(
        'statusCode').text==
        '200']

        for e
        in interesting:

        print
        "%s (%s)" % (e.findtext(
        'resource'),
                    e.findtext(
        'byteCount'))
      

请注意上面的不同之处。 gnosis.xml.objectify 将子元素节点直接作为节点的属性进行连接(每个节点都是一个根据标记名命名的定制类)。另一方面, ElementTree 使用 Element 类的方法查找子节点。 .findall() 方法返回所有匹配节点的列表; .find() 则仅返回首次匹配的节点; .findtext() 返回节点的文本内容。如果您只想要 gnosis.xml.objectify 子元素上的首次匹配,只要为其建立索引即可 - 例如, node.tag[0] 。但如果这样的子元素只有一个,那么无需建立显式的索引,您也可以引用它。

但是在 ElementTree的示例中,其实您并不 需要显式查找所有 <entry> 元素;迭代时 Element 实例的行为方式类似于列表。在这里要注意一点,不管子节点有何标记,对 所有的子节点都进行迭代。相比之下, gnosis.xml.objectify 节点没有内置方法可遍历它所有的子元素。尽管如此,构造一个一行的 children() 函数还是挺简单的(我会在将来的发行版中包含该函数)。比照清单 6:

清单 6. ElementTree 对节点列表和特定子类型进行的迭代


>>> open('simple.xml','w.').write('''<root>
... <foo>this</foo>
... <bar>that</bar>
... <foo>more</foo></root>''')
>>> from elementtree import ElementTree
>>> root = ElementTree.parse('simple.xml').getroot()
>>> for node in root:
...     print node.text,
...
this that more
>>> for node in root.findall('foo'):
...     print node.text,
...
this more

和清单 7:

清单 7. gnosis.xml.objectify 对所有子节点进行的有损耗的迭代


>>> children=lambda o: [x for x in o.__dict__ if x!='__parent__']
>>> from gnosis.xml.objectify import XML_Objectify
>>> root = XML_Objectify('simple.xml').make_instance()
>>> for tag in children(root):
...     for node in getattr(root,tag):
...         print node.PCDATA,
...
this more that
>>> for node in root.foo:
...     print node.PCDATA,
...
this more

正如您所见, gnosis.xml.objectify 目前抛弃了有关散布在代码中的 <foo><bar> 元素原始顺序的信息( 能够通过另一个奇妙的属性,如 .__parent__ 记住该顺序,但没有人需要或发送一个补丁来做这件事)。

ElementTree 在一个称为 .attrib 的节点属性中存储 XML 属性;这些属性被存储在字典中。 gnosis.xml.objectify将 XML 属性直接放置到相应名称的节点属性中。我使用的样式往往弱化 XML 的属性和元素内容之间的差异 - 我认为,这应该是由 XML,而不是我的本机数据结构所担心的问题。举例来说:

清单 8. 访问子节点和 XML 属性时的差异


>>> xml = '<root foo="this"><bar>that</bar></root>'
>>> open('attrs.xml','w').write(xml)
>>> et = ElementTree.parse('attrs.xml').getroot()
>>> xo = XML_Objectify('attrs.xml').make_instance()
>>> et.find('bar').text, et.attrib['foo']
('that', 'this')
>>> xo.bar.PCDATA, xo.foo
(u'that', u'this')

在 XML 属性(创建了包含有文本的节点属性)和 XML 元素内容(创建了包含对象 - 也许还包括具有 .PCDATA 的子节点 - 的节点属性)之间, gnosis.xml.objectify仍造成了 一些差异。


回页首

XPath 和 tail 属性

ElementTree 在其 .find*() 方法中实现了一个 XPath 的子集。对于在子节点层次之间进行查找而言,使用该样式与使用嵌套代码相比,要简洁许多,尤其对含有通配符的 XPath 更是如此。举例来说,如果我想知道对我的 Web 服务器所有访问的时间戳记,可以这样检查 weblog.xml:

清单 9. 使用 XPath 查找嵌套子元素


>>> from elementtree import ElementTree
>>> weblog = ElementTree.parse('weblog.xml').getroot()
>>> timestamps = weblog.findall('entry/dateTime')
>>> for ts in timestamps:
...     if ts.text.startswith('19/Aug'):
...         print ts.text

当然,对于像 weblog.xml 这样标准、浅显的文档,使用列表理解很容易就可以做同样的工作:

清单 10. 使用列表理解查找并过滤嵌套子元素


>>> for ts in [ts.text for e in weblog
...            for ts in e.findall('dateTime')
...            if ts.text.startswith('19/Aug')]:
...     print ts

然而,面向散文的 XML 文档,其文档结构往往拥有更多的变化,且嵌套标记通常有至少五或六层深。举例来说,一个 XML 模式(如 DocBook 或 TEI)可能会在节、子节、参考书目中含有引证,或者是在斜体标记、块引用中含有引证,等等。查找每个 <citation> 元素会要求涉及多个层次,进行繁琐(可能需要递归)的搜索。而使用 XPath,您只要这样写:

清单 11. 使用 XPath 查找深层嵌套子元素


>>> from elementtree import ElementTree
>>> weblog = ElementTree.parse('weblog.xml').getroot()
>>> cites = weblog.findall('.//citation')

然而, ElementTree对 XPath 的支持是有限的:您不能使用完整 XPath 所包含的各种函数,也不能按属性进行搜索。可是,在可行范围内,在 ElementTree中使用 XPath 子集可以大大提高其可读性和表达能力。

在结束本文前我还想要再提一点 ElementTree比较奇怪的地方。XML 文档可以是混合内容。尤其是面向散文的 XML 往往会任意散布 PCDATA 和标记。但是您应该在哪里正确地 存储子节点之间的文本呢?由于 ElementTreeElement 实例有一个单一的 .text 属性 - 包含一个字符串 - 它并不真正为断开的字符串序列保留空格。 ElementTree 采用的解决方案赋予了每个节点一个 .tail 属性,它包含了位于结束标记之后,下一元素开始或父元素结束之前所有的文本。举例来说:

清单 12. 存储在 node.tail 属性中的 PCDATA


>>> xml = '<a>begin<b>inside</b>middle<c>inside</c>end</a>'
>>> open('doc.xml','w').write(xml)
>>> doc = ElementTree.parse('doc.xml').getroot()
>>> doc.text, doc.tail
('begin', None)
>>> doc.find('b').text, doc.find('b').tail
('inside', 'middle')
>>> doc.find('c').text, doc.find('c').tail
('inside', 'end')

回页首

结束语

ElementTree是一个非常不错的模块,和 DOM 相比,它提供了一个更轻量级的对象模型,用于以 Python 处理 XML。虽然我没有在本文中提及, ElementTree在从头生成 XML 文档方面和它在操作现有的 XML 数据方面一样出色。

作为与之类似的库 gnosis.xml.objectify的作者,我无法完全客观地评价 ElementTree;尽管如此,与那些 ElementTree所提供的方法相比,我始终尝试在 Python 程序中用我自己的方法更简单自然地予以实现。ElementTree 通常仍利用节点的方法来操作数据结构,而不是像人们通常处理应用程序中构建的数据结构那样直接访问节点属性。

然而,在有些方面, ElementTree很出色。使用 XPath 访问深层嵌套元素要比手工递归搜索容易得多。显然,在 DOM 中也可使用 XPath,但代价是形成一个过于庞大且不够统一的 API。 ElementTree 所有的 Element 节点的工作方式是一致的,不像 DOM 的节点类型那样“装饰华丽”。

2006年10月09日

    这是一部去年的电影,我到今年才看,甚是惭愧。这是一部有关宗教,英雄,骑士的电影,对骑士,贵族,基督教和伊斯兰教有兴趣的朋友都不容错过。当然,里面的战争场面也非常宏大,喜欢军事的朋友同样值得一看。

    《天国王朝》(Kingdom of Heaven)的故事发生在第二次和第三次十字军东征之间,时间大约是1183年到1187年撒拉丁攻占耶路撒冷。

    电影里奥兰多布鲁姆扮演的铁匠贝里昂非常帅气,然而我个人感觉这是一部男人的电影,所以帅气的男主角并不能给电影带来太多东西。这个人物的原型是伊贝林的贝里昂,在历史上是有名的贵族,也非铁匠出身。

    让我们来看看那段时期真正的两位主角,历史上极其著名的萨拉丁和麻风鲍德温。萨拉丁是阿拉伯世界真正的王者,是近代以来但凡有点抱负的阿拉伯领导人梦寐以求成为的人物。他的伟大功绩不仅在于夺取并守住了耶路撒冷,更重要的是,他的仁慈。

    与88年前十字军攻克耶路撒冷时大开杀戒形成鲜明的对比,萨拉丁进入耶路撒冷没有杀一个人,没有烧一栋房子。根据受降时签订的协议,耶路撒冷每个男人要缴纳10第纳尔赎金,每个女人缴纳5第纳尔,儿童1第纳尔;无力缴纳的人则成为奴隶。萨拉丁免去了7000穷人的赎金。萨拉丁的弟弟向萨拉丁要了1000名奴隶,随即将他们释放。耶路撒冷主教也随即效仿,向他要了700名奴隶然后释放。最后,萨拉丁自己宣布释放了所有战俘,不要一分赎金。

    在十字军占领期间,阿克萨清真寺被改为圣殿骑士团的总部,磐石清真寺成了教堂。萨拉丁将它们恢复为清真寺。磐石清真寺金顶上的十字架被拆除,宣礼的声音再次回荡在阿克萨清真寺的上空。有人向他建议拆毁耶路撒冷的圣墓大教堂,萨拉丁没有同意。相反,他将耶路撒冷的圣地向所有宗教开放。

    在后来狮心王的第三次十字军东征中,萨拉丁还让弟弟阿迪勒给理查送去两匹好马。最后萨拉丁和理查都病倒  了,萨拉丁派人给自己的死对头理查送去了水果,还派去了医生。双方签订和约,穆斯林占有巴勒斯坦内地,基督教徒占有海岸,耶路撒冷向朝觐的基督教徒开放。
    看过电影的人,一定对戴着面具的麻风国王印象深刻。虽然受着麻风病的煎熬,身上已经血肉模糊,但是其处事之英明,还是让人赞叹。在历史上,他的第一次成功在1177年。那年秋天,撒拉丁的三万骑兵分成两路发动进攻,其中两万进攻圣殿骑士团所在的加沙地带(这个地名是不是很熟?),一万包围阿斯卡伦。耶路撒冷几乎没有任何准备,一开始就被打了个措手不及。他们事前甚至没有得到什么情报,情报工作失误导致的结果是,在敌军到达阿斯卡伦之前,竟然让国王在少数部队的陪同下到了那个地方,自动把羊羔送到饿虎的嘴边。这一年的麻风国王十六岁,仍然整日生活在面具之下。塞尔柱人很快就清楚了耶路撒冷王正被他们围困在阿斯卡伦。欣喜若狂的撒拉丁马上组织部队对该地区进行猛烈的攻击,誓要生擒敌酋。国王的骑士卫队镇定而毫无主意地保卫在君主的周围,抱定了进行最后死战的决心。然而这时候,他们却从身后的孩子口中听到了冷静清醒而又条理清晰的指令。骑士们惊奇地转身看着国王,然后鞠躬并举剑示意,执行命令。在打退塞尔柱人的进攻后,鲍德温家的少年战术天才抓住对方组织攻势的间隙,率领帐下突围而走。撒拉丁闻讯大发雷霆并派遣马木留克骑兵卫队狂追,但是无济于事。鲍德温突围后并不向耶路撒冷撤退,他派出通讯员命令各地骑士立即前来与他相会,同时往医院骑士团的驻地进发。在那里,他与带领圣殿骑士团残部突围的雷纳德相遇。狼狈的雷纳德原以为被围的国王已经提前归天,想不到国王已经在部署决战的事宜。他第一次感到那个银面具下所散发的气度,也第一次认识到了国王的权利和威严。于是,耶路撒冷国王集结主力军队,与同样收束军队前来夺取耶路撒冷的撒拉丁在蒙吉萨相遇。11月25日,双方大战。结果以撒拉丁的溃败而告终,其最精锐的马木留克近卫部队几乎全灭。

    1179年,撒拉丁率军偷袭了在泉水谷的雷纳德和圣殿骑士团,鲍德温闻讯马上亲提大军前来交战。双方对峙许久,撒拉丁无法占到便宜。于是双方缔结两年的休战协议。沙漠之王终于被拒绝在耶路撒冷国土之外。

    1183年,撒拉丁进攻耶路撒冷。撒拉丁进行了数次挑战,使用各种手段引诱摄政王盖伊外出决战,眼见即将成功。但是,那个麻风病人,那个半死不活的麻风病人还是来了。沙漠之王在军帐里沉默了很久,最后下令撤军,默默地回到沙漠去等待他的麻风对手死去的那天。

    1185年3月,麻风病人,沙漠之雄撒拉丁永远无法跨越的对手,耶路撒冷王国最后的强者,也是最脆弱的强者——鲍德温四世,终于得到了身体和灵魂的最后解脱。在《天国王朝》片中,鲍德温临死时对西比拉说,他仿佛又回到了16岁那一年,一个意气风发的少年指挥王国击败了撒拉丁的精锐。这一战即是1177年的蒙吉萨之战。

惺惺相惜