C++抽象编程——面向对象(9)——token扫描器

token扫描器(Designing a token scanner class)

在字符串系列中,最为复杂的字符串处理示例是Pig Latin转换了。 (链接:C++抽象编程——字符串(4)——回文数的检查与Pig Latin游戏)PigLatin程序将问题分解为两个阶段:lineToPigLatin函数将输入划分为单词,然后调用wordToPigLatin将每个单词转换为Pig Latin的句子。然而,这种分解的第一阶段并不完全是在Pig Latin这里特有的。许多应用程序需要将字符串划分为单词,或更一般地将其划分为大于单个字符的逻辑单元。 在计算机科学中,这样的单元通常称为token(这个单词我真的不知道怎么翻译,有人称为令牌)。
鉴于将字符串划分为单独的token的问题在应用程序中频繁出现,构建一个负责该任务的库包是有用的。 这次我们介绍为此目的设计的TokenScanner类。主要目标是构建一个易于使用的软件包,但仍具有足够的灵活性以满足各种客户端的需求。

客户端想从token扫描器得到什么

和往常一样,开始设计TokenScanner类的最好方法是从客户端的角度来看问题。想要使用扫描仪的每个客户端都会以token的来源为开头,这可能是字符串,但也可能是从文件读取数据的应用程序的输入流。 在这两种情况下,客户端需要的方法是从该来源开始检索单个token。设计提供必要功能的TokenScanner类有几种策略。例如,我们可以使token扫描器返回包含整个token列表的vector。但是,该策略不适用于使用大型输入文件的应用程序,因为扫描程序必须创建包含整个token列表的单个vector。 更节省空间的方法是让扫描仪一次提供一个token。 您使用此设计时,从扫描仪读取令牌的过程可以用下面的伪代码表示:

Set the input for the token scanner to be some string or input stream.
while (more tokens are available) {
Read the next token.
}

为token扫描器设置输入,当其他的tokens可以用的时候,我们读取下一个token。
该伪代码结构立即提示了TokenScanner类必须支持的方法。 从这个例子可以看出,TokenScanner可以导出以下方法:

  • setInput方法:允许客户端指定token源。为了最大的灵活性,该方法应该被重载,以将字符串或输入流作为参数
  • hasMoreTokens方法:用来测试token扫描器是否有更多的token待处理
  • nextToken方法: 用于扫描并返回下一个token。

这些方法定义了令牌扫描器的操作结构,并且在很大程度上独立于应用程序的细节。然而,不同的应用程序以各种不同的方式定义token,这意味着TokenScanner类必须给客户端一些提示或者方式控制哪些类型的token,以便它被识别。
回到PigLatin,如果用使用token扫描器重写PigLatin程序,我们就不能忽略空格和标点符号,因为这些字符需要是输出的一部分。在这个问题的背景下,token分为两类:
1. 一串连续的字母数字字符,代表一个字。(A string of consecutive alphanumeric characters representing a word
2. 由空格或标点符号组成的单字符串。(A single-character string consisting of a space or punctuation mark.

举个例子,如果你输入的是下面的句子:

this is "pig latin"

调用nextToken将返回以下9个tokens的序列:

然而,其他应用程序可能会以不同的方式定义token。例如,您的C++编译器使用令牌扫描程序将程序中断到编程语境中有意义的令牌,包括标识符,常量,运算符以及定义语言句法结构的其他符号。 例如,如果给编译器的token扫描器下面这一行:

cout << "hello, world" << endl;

您希望提供以下token序列如下:

这里跟上面不同,hello world作为了整体。token的定义中这两个应用程序之间存在一些差异。 在Pig Latin中,任何不是字母数字字符序列的任何内容都将返回为单字符token。在编译器的例子中,情况比较复杂。一方面,编程语言通常定义多个字符运算符,如 << 必须被视为单个token。类似地,字符串常量“hello,world”只有在令牌扫描器将其视为单个实体时才具有正确的含义。编译器的token扫描器会完全忽略输入空格,除非它们出现在字符串常量中。
如果您继续在编译器上学习class,可以构建一个token扫描器,允许客户端通过提供一组精确的规则来指定什么构成特定的token。 然而,这种普遍性往往以牺牲简单性为代价。 如果强制客户指定token形成的规则,则需要学习如何编写这些规则,这在很多方面与学习新语言相似。 更糟糕的是,令牌形成的规则,特别是如果你试图指定一个编译器用来识别数字的规则,这对于客户来说是很困难的。
如果你在接口中的目标是最大限度地简化,那么设计TokenScanner类可能更好,以便客户端可以启用特定的选项,使其能够识别在特定应用程序上下文中使用的令牌类型。 如果您想要的是token扫描器,可以将连续的字母数字字符收集到单词中,那么可以在最简单的配置中使用TokenScanner类。如果想要让TokenScanner识别C++程序中的单位,则可以启用说明扫描仪的选项,例如忽略空格,将引用的字符串视为单个单位,并且标点符号的某些组合表示多字符运算符。

你可能感兴趣的:(抽象编程(C++),C++学习与基础算法,class,扫描器,类,字符串)