3.5 字符串的应用

如果仔细查看本书的程序举例代码,读者会注意到注释中的一些标记。这些都是供Bruce所编写的一个Python程序使用的,该程序用于将代码提取到文件并生成makefile的程序。例如,以双斜线加冒号开始的一行表示源文件的第1行。其余行所描述的信息包括文件名、文件所在的位置以及是否应该只编译文件,而不是生成可执行文件。例如,在上面的程序中,第1行包含字符串C03:IWCompare.cpp,它表示文件IWCompare.cpp应该被提取到目录C03下。

源文件的最后一行包括3条斜线,其后是一个冒号和一个波形号。如果第1行有一个惊叹号,并且其后紧接着冒号,源代码的第1行和最后一行就不会输出到文件上(这只适用于数据文件)。(为什么在代码中隐藏这些标记呢,那是因为当将代码提取器应用于本教材的代码正文时,我们不想破坏提取器。)

Bruce的Python程序所做的远不止提取代码。如果文件名后有标记“{O}”,它在makefile中的条目将被设置为只编译文件,而不将其链接到可执行文件中。(第2章的测试框架就是这么构建的。)为了将这样一个文件与另一个源代码例子链接起来,目标执行文件的源文件会包括一个“{L}”指令,就像:

3.5 字符串的应用 - 图1

本部分将介绍一个程序,该程序仅用来提取所有的代码,以便程序员进行手工编译和检查。程序员可以用这个程序来提取本教材中的所有代码,并将文档保存为文本文件[1](称其为TICV2.txt),在shell命令行中执行类似下面的命令:

3.5 字符串的应用 - 图2

这个命令读取文本文件TICV2.txt,然后在根目录/TheCode下的子目录里写出所有源代码文件。目录树如下所示:

3.5 字符串的应用 - 图3

各章中例子的源文件包括在相应的目录里。

下面是程序:

3.5 字符串的应用 - 图4

3.5 字符串的应用 - 图5

3.5 字符串的应用 - 图6

首先,读者应注意某些条件编译指令。用于在文件系统中创建目录的mkdir()函数,是由POSIX标准[2]在头文件<sys/stat.h>中定义的。遗憾的是,很多编译器仍然在使用不同的另一个头文件(<direct.h>)。对于不同的头文件,各自的mkdir()识别标志也有所不同:POSIX指定了两个参数,而旧版本只有一个。因此,在以后的程序中就有了更多的条件编译,以便选择正确的mkdir()进行调用。在本教材的例子中通常不使用条件编译,但是这个特别的程序太有用了,以至于不能放置哪怕一点额外的工作进去,因为读者能用它提取教材中所有的代码。

ExtractCode. cpp中的exists()函数通过打开目录中一个临时文件的方式来判断这个目录是否存在。如果打开文件失败,目录就不存在。要删除一个文件,可以将其char*型名字传送到std:remove()中。

主程序首先判定命令行参数的合法性,然后一次一行地读取输入文件,同时查找特殊的源代码定界符。布尔标志符inCode表示程序在源文件的中间,所以这些代码行应当输出。如果源代码的开始标记不是跟随惊叹号,printDelims标志符将为真;否则第1行与最后一行将不会写出。首先查看结束定界符的存在性,这一点很重要,因为开始标记是结束定界符的一个子集。如果先查找开始标记,则程序在找到开始标记和结束定界符的时候都会返回成功。如果遇到结束标记,程序就知道源文件正在处理过程中;否则,文本文件中定界符的摆放方式就有错误了。如果inCode为真,那就没什么问题,程序(可选地)写下最后一行然后关闭文件。当找到开始标记时,系统就从语法上分析目录和文件名的组成,然后打开文件。下面几个与string有关的函数在此例中都用到了:length()、append()、getline()、find()(两个版本)、find_first_not_of()、substr()、find_first_of()、c_str(),当然还有operator<<()。

[1]注意,当文件被存成文本时,Microsoft Word的某些版本将会错误地将单个引述字符替换成扩展了的ASCII码字符,这会造成编译错误。我们不知道错误产生的原因。读者只需用单引号手工替换那个字符就行了。

[2]POSIX是一个IEEE标准,支持“便携式操作系统接口(Portable Operating System Interface)”在Unix系统中的许多低级系统调用,POSIX就是这些调用的一体化产物。