8.8.5 搜索字符串(3)
下面是该程序的部分输出:
- Enter some text on as many lines as you wish.
- Terminate the input with an asterisk:
- I sometimes think I'd rather crow
- And be a rooster that to roost
- And be a crow. But I dunno.
-
- A rooster he can roost also,
- Which don't seem fair when crows can't crow
- Which may help some. Still I dunno.*
-
- In ascending sequence, the words in the text are:
|
A < xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> |
1 |
|
And |
2 |
|
But |
1 |
|
I |
3 |
|
I'd |
1 |
|
Still |
1 |
|
Which |
2 |
|
a |
2 |
|
also |
1 |
|
be |
2 |
|
can |
1 |
|
can't |
1 |
|
crow |
3 |
|
crows |
1 |
|
don't |
1 |
|
dunno |
2 |
|
fair |
1 |
|
he |
1 |
|
help |
1 |
|
may |
1 |
|
rather |
1 |
|
roost |
2 |
|
rooster |
2 |
|
seem |
1 |
|
some |
1 |
|
sometimes |
1 |
|
that |
1 |
|
think |
1 |
|
to |
1 |
|
when |
1 |
示例说明
在本示例中使用getline()从cin中读取输入,终止字符被指定为一个星号。这样允许输入任意行代码。单个单词从对字符串对象text的输入中提取出来,并存储在words数组中。这是通过while循环完成的。
从text中提取单词的第一步是找到单词的第一个字符的索引位置:
- start = text.find_first_not_of(separators,
offset); // Find non-separator - if(start == string::npos) // If we did not find it, we are done
- break;
- offset = start + 1; // Move past character found
调用find_first_not_of()函数返回位置offset中第一个不是separators中的字符之一的字符的索引位置。这里我们可以用find_first_of()函数来搜索A~Z,a~z中的任何字符来得到同样的结果。当提取了最后一个单词后,搜索会到达字符串的末尾,而没有发现任何匹配的字符,因此我们通过将返回的值与string::npos作比较来进行测试。如果它是字符串的末尾,就提取所有单词,因此我们退出循环。在任何其他情况下,我们就在发现的字符后面一个字符处设置offset。
下一步是搜索任何分隔符字符:
- end = text.find_first_of(separators,offset); // Find separator
- if(end == string::npos) // If it's the end of the string
- { // current word is last in string
- offset = end; // We use offset to end loop later
- end = text.length(); // Set end as 1 past last character
- }
- else
- offset = end + 1;
- // Move past character found
这段代码从索引位置offset处搜索任何分隔符,这是单词的第一个字符后面的字符,因此通常我们会发现分隔符是单词的最后一个字符后面的字符。当单词是文本中的最后一个词,而且该单词的最后一个字符后面没有分隔符时,函数就会返回string::npos,因此我们这样处理此类情况:将end设置为字符串中最后一个字符后面的字符,并将offset设置为string::npos。以后在提取当前单词之后的循环中会测试offset变量来判断是否应结束循环。
单词的提取并不难:
- words[nwords] = text.substr(start, end-start);
// Extract the word
substr()函数在text中从strart处的字符开始提取end-start个字符。单词的长度是end-start,因为start是第一个字符,而end是单词中的最后一个字符后面的那个字符。
while循环体的其余部分以前面介绍的方式跟踪最大单词长度,检查字符串结束条件,并检查words数组有没有满。
这些单词在一个for循环中输出,它们在words数组中的所有元素上迭代。循环中的if语句用来计数重复的单词:
- if(count == 0)
- count = 1;
- if(i < nwords-2 && words[i] == words[i+1])
- {
- ++count;
- continue;
- }
count变量记录重复的单词个数,因此它的最小值总是为1。在循环的末尾,当一个单词和它的个数被写出后,count被设置为0。它充当一个指示器,表示新的单词计数开始了,因此当count为0时,第一个if语句将它设置为1,否则它就会保留为当前值。
第二个if语句检查下一个单词与当前单词是否相同,如果相同,则count递增,并跳过当前循环迭代的其余部分。该机制用count累计单词的重复次数。循环条件也检查索引i是否小于nwords-2,若当前单词是数组中的最后一个单词,我们就不用检查下一个单词。因此,当下一个单词与当前单词不同,或者当前单词是数组中的最后一个单词时,我们仅输出一个单词和它的计数。
for循环中的最后一步是输出一个单词和它的计数:
- cout << setiosflags(ios::left) // Output word left-justified
- << setw(maxwidth+2) << words[i];
- cout << resetiosflags(ios::right) // and word count right-justified
- << setw(5) << count << endl;
- count = 0;
该输出语句将单词在比最长单词长两个字符的字段宽度中左对齐。计数输出是在宽度为5的字段宽度中右对齐。