# 计算文件中的单词数量

我们在读取一个文件的时候，也想知道这个文件中包含的单词数量。我们定义的单词是位于两个空格之间的字符组合。那要如何进行统计呢？

根据对单词的定义， 我们可以统计空格的数量。例如句子`John has a funny little dog.`，这里有五个空格，所以说这句话有六个单词。

如果句子中有空格干扰怎么办，例如：`John has \t a\nfunny little dog .`。这句中有很多不必要的空格、制表符和换行符。本书的其他章节中，我们已经了解如何将多余空格从字符串中去掉。所以，可以对字符串进行预处理，将不必要的空格都去掉。这样做的确可行，不过我们有更加简单的方法。

为了寻找最优的解决方案，我们将让用户选择，是从标准输入中获取数据，还是从文本文件中获取数据。

## How to do it...

本节，我们将完成一个单行统计函数，其可以对输入的数据进行计数，数据源的具体方式我们可以让用户来选择。

1. 包含必要的头文件，并声明所使用的命名空间：

   ```cpp
   #include <iostream>
   #include <fstream>
   #include <string>
   #include <algorithm>
   #include <iterator>

   using namespace std;
   ```
2. `wordcount`函数能接受一个输入流，例如`cin`。其能创建一个`std::input_iterator`迭代器，其能对输出字符进行标记，然后交由`std::distance`进行计算。`distance`接受两个迭代器作为参数，并确定从一个迭代器到另一个迭代器要用多少步(距离)。对于随机访问迭代器，因为有减法操作符的存在，所以实现起来非常简单。其迭代器如同指针一样，可以直接进行减法，计算出两点的距离。不过`istream_iterator`就不行，因为其是前向迭代器，只能向前读取，直至结束。最后所需要的步数也就是单词的数量：

   ```cpp
   template <typename T>
   size_t wordcount(T &is)
   {
       return distance(istream_iterator<string>{is}, {});
   }
   ```
3. 主函数中，我们会让用户来选择输入源：

   ```cpp
   int main(int argc, char **argv)
   {
       size_t wc;
   ```
4. 如果用户选择使用文件进行输入(例如：`./count_all_words some_textfile.txt`)，我们可以通过`argv`获取命令行中的文件名称，并将文件打开，读取数据，从而对其文本进行单词统计：

   ```cpp
       if (argc == 2) {
           ifstream ifs {argv[1]};
           wc = wordcount(ifs);
   ```
5. 如果用户没有传入任何参数，就认为用户要使用标准输入流输入数据：

   ```cpp
       } else {
           wc = wordcount(cin);
       }
   ```
6. 然后只需要将统计出的单词数量保存在变量`wc`中即可：

   ```cpp
       cout << "There are " << wc << " words\n";
   };
   ```
7. 编译并运行程序。首先，从标准输入中进行输入。我们可以这里通过echo命令将字符串，通过管道传递给程序。当然，我们也可以直接进行输入，并使用`Ctrl+D`来结束输入：

   ```cpp
   $ echo "foo bar baz" | ./count_all_words
   There are 3 words
   ```
8. 这次我们使用文件作为输入源，并对其中单词数量进行统计：

   ```cpp
   $ ./count_all_words count_all_words.cpp
   There are 61 words
   ```

## How it works...

本节也没有什么好多说的；实现很短，难度很低。需要提及的可能就是我们对`std::cin`和`std::ifstream`的实例进行了互换。`cin`是`std::istream`的类型之一，并且`std::ifstream`继承于`std::istream`。可以回顾一下本章开头的类型继承表。这两种类型即使在运行时，都能进行互换。

> Note：
>
> 使用流来保持代码的模块性，这有助于减少代码的耦合性。因为其可以匹配任意类型的流对象，所以更容易对代码进行测试。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zoupers.gitbook.io/cpp-17-stl-cookbook/chapter7-0-chinese/chapter7-5-chinese.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
