19 文本操纵
注重实效的程序员用与木匠加工木料相同的方式操纵文本。在前面的部分里,我们讨论了我们所用的一些具体工具——shell、编辑器、调试器。这些工具与木匠的凿子、锯子、刨子类似——它们都是用于把一件或两件工作做好的专用工具。但是,我们不时也需要完成一些转换,这些转换不能由基本工具集直接完成。我们需要通用的文本操纵工具。
文本操纵语言对于编程的意义,就像是刳刨机(router)对于木工活的意义。它们嘈杂、肮脏、而且有点用“蛮力”。如果使用有误,整个工件都可能毁坏。有人发誓说在工具箱里没有它们的位置。但在恰当的人的手中,刳刨机和文本操纵语言都可以让人难以置信地强大和用途广泛。你可以很快把某样东西加工成形、制作接头、并进行雕刻。如果适当使用,这些工具拥有让人惊讶的精微与巧妙。但你需要花时间才能掌握它们。
好的文本操纵语言的数目正在增长。Unix开发者常常喜欢利用他们的命令shell的力量,并用像awk和sed这样的工具加以增强。偏爱更为结构化的工具的人喜欢Python[URL 9]的面向对象本质。有人把Tcl[URL 23]当作自己的首选工具。我们碰巧喜欢用Perl[URL 8]编写短小的脚本。
这些语言是能赋予你能力的重要技术。使用它们,你可以快速地构建实用程序,为你的想法建立原型——使用传统语言,这些工作可能需要5倍或10倍的时间。对于我们所做的实验,这样的放大系数十分重要。与花费5小时相比,花费30分钟试验一个疯狂的想法要好得多。花费1天使项目的重要组件自动化是可以接受的;花费1周却不一定。在The Practice of Programming[KP99]一书中,Kernighan与Pike用5种不同的语言构建同一个程序。Perl版本是最短的(17行,而C要150行)。通过Perl你可以操纵文本、与程序交互、进行网络通信、驱动网页、进行任意精度的运算、以
及编写看起来像史努比发誓的程序。
|
提示28 |
|
|
Learn a Text Manipulation Language |
|
为了说明文本操纵语言的广泛适用性,这里列出了我们过去几年开发的一些应用示例:
l 数据库schema维护。一组Perl脚本读取含有数据库schema定义的纯文本文件,根据它生成:
- 用于创建数据库的SQL语句
- 用于填充数据词典的平板(flat)数据文件
- 用于访问数据库的C代码库
- 用于检查数据库完整性的脚本
- 含有schema描述及框图的网页
- schema的XML版本
l Java属性(property)访问。限制对某个对象的属性的访问,迫使外部类通过方法获取和设置它们,这是一种良好的OO编程风格。但是,属性在类的内部由简单的成员变量表示是一种常见情况,在这样的情况下要为每个变量创建获取和设置方法既乏味,又机械。我们有一个Perl脚本,它修改源文件,为所有做了适当标记的变量插入正确的方法定义。
l 测试数据生成。我们的测试数据有好几万记录,散布在若干不同的文件中,其格式也不同,它们需要汇合在一起,并转换为适于装载进关系数据库的某种形式。Perl用几小时就完成了这一工作(在此过程中还发现了初始数据的几处一致性错误)。
l 写书。我们认为,出现在书籍中的任何代码都应首先进行测试,这十分重要。本书中的大多数代码都经过了测试。但是,按照DRY原则(参见“重复的危害”,26页),我们不想把代码从测试过的程序拷贝并粘贴到书里。那意味着代码是重复的,实际上我们肯定会在程序被改动时忘记更新相应的例子。对于有些例子,我们也不想用编译并运行例子所需的全部框架代码来烦扰你。我们转向了Perl。在我们对书进行格式化时,会调用一个相对简单的脚本——它提取源文件中指定的片段,进行语法突显,并把结果转换成我们使用的排版语言。
l C与Object Pascal的接口。某个客户有一个在PC上编写Object Pascal应用的开发团队。他们的代码需要与用C编写的一段代码接口。我们开发了一个短小的Perl脚本,解析C头文件,提取所有被导出函数的定义,以及它们使用的数据结构。随后我们生成Object Pascal单元:用Pascal记录对应所有的C结构,用导入的过程定义对应所有的C函数。这一生成过程变成了构建的一部分,这样无论何时C头文件发生变化,新的Object Pascal单元都会自动被构造。
l 生成Web文档。许多项目团队都把文档发布在内部网站上。我们编写了许多Perl程序,分析数据库schema、C或C++源文件、makefile以及其他项目资源,以生成所需的HTML文档。我们还使用Perl,把文档用标准的页眉和页脚包装起来,并把它们传输到网站上。
我们几乎每天都使用文本操纵语言。与我们注意到的其他任何语言相比,本书中的许多想法都可以用这些语言更简单地实现。这些语言使我们能够轻松地编写代码生成器,我们将在下一节讨论这一主题。
相关内容:
l 重复的危害,26页
练习
11. 你的C程序使用枚举类型表示100种状态。为进行调试,你想要能把状态打印成(与数字对应的)字符串。编写一个脚本,从标准输入读取含有以下内容的文件: (解答在285页)
name
state_a
state_b
: :
生成文件name.h,其中含有:
extern const char* NAME_names[];
typedef enum {
state_a,
state_b,
: :
} NAME;
以及文件name.c,其中含有:
const char* NAME_names[] = {
"state_a",
"state_b",
: :
};
12. 在本书撰写的中途,我们意识到我们没有把use strict指示放进我们的许多Perl例子。编写一个脚本,检查某个目录中的.pl文件,给没有use strict指示的所有文件在初始注释块的末尾加上该指示。要记住给你改动的所有文件保留备份。 (解答在286页)







