15.4 解析输入

优质
小牛编辑
129浏览
2023-12-01

在1.4节,我们把“解析”定义为分析自然语言句子或形式语言语句之结构的过程。比如,编译器在将代码翻译成机器语言程序之前必须先进行解析。

此外,当你从文件或键盘读取输入时,一般也需要进行解析,以提取想要的信息并发现错误。

例如,我有一个文件distances,其中包含了美国主要城市之间的距离信息。这些信息是我从一个随机选择的网页(http://www.jaring.my/usiskl/usa/distance.html)中得到的,所以数据可能不是很准确,不过这也没什么关系。文件格式看起来是这样的:

"Atlanta"     "Chicago"       700
"Atlanta"     "Boston"        1100
"Atlanta"     "Chicago"       700
"Atlanta"     "Dallas"        800
"Atlanta"     "Denver"        1450
"Atlanta"     "Detroit"       750
"Atlanta"     "Orlando"       400

文件中的每一行包含了两个城市的名字以及它们之间的距离,其中城市名用引号标记,距离以英里为单位。引号是有用的,因为它能让我们很容易地处理多于一个单词的城市名,如”San Francisco“(旧金山)。

通过搜索一行输入中的引号,我们能找到每个城市在该行的开始和结束位置。不过查找引号这样的特殊字符可能让人有点困惑,因为引号是C++中用于标识字符串的特殊字符。

要找到引号第一次出现的位置,应该这样写:

int index = line.find (’\"’);

参数看起来有点乱,不过它就是表示双引号字符。最外层的单引号依然用于表示这是个字符值。反斜杠(\)说明我们想使用下一个字符的字面意义。 所以序列 \" 表示双引号,而序列 \’表示单引号。有趣的是, 序列\\表示一个反斜杠。第一个反斜杠指示我们要认真对待第二个反斜杠。

解析输入行由这几部分组成:找到每个城市名在该行中的开始和结束位置,使用substr函数提取城市和距离信息。substr是apstring的成员函数之一,它有两个参数,分别是子串的起始位置和长度。

void processLine (const apstring& line)
{
  // 我们要查找的字符是引号
  char quote = ’\"’;

  // 将引号的索引保存在一个向量中
  apvector<int> quoteIndex (4);

  // 使用内置的find函数查找到第一个引号
  quoteIndex[0] = line.find (quote);

  // 使用第7章定义的find函数查找其他引号
  for (int i=1; i<4; i++) {
    quoteIndex[i] = find (line, quote, quoteIndex[i-1]+1);
  }

  // 将一行的内容分割成子串
  int len1 = quoteIndex[1] - quoteIndex[0] - 1;
  apstring city1 = line.substr (quoteIndex[0]+1, len1);
  int len2 = quoteIndex[3] - quoteIndex[2] - 1;
  apstring city2 = line.substr (quoteIndex[2]+1, len2);
  int len3 = line.length() - quoteIndex[2] - 1;
  apstring distString = line.substr (quoteIndex[3]+1, len3);

  // 输出提取的信息
  cout << city1 << "\t" << city2 << "\t" << distString << endl;
}

当然,我们真正想要的并不仅仅是提取并显示信息,不过这是一个好的开始。