【问题标题】:C++ Tokenizing using iterators in an eof() cycle在 eof() 循环中使用迭代器进行 C++ 标记化
【发布时间】:2010-10-03 20:51:21
【问题描述】:

我正在尝试调整这个答案

How do I tokenize a string in C++?

我当前的字符串问题涉及从文件读取直到 eof。

来自这个源文件:

Fix grammatical or spelling errors

Clarify meaning without changing it

Correct minor mistakes

我想创建一个包含所有标记化单词的向量。示例:vector<string> allTheText[0] should be "Fix"

我不了解istream_iterator<std::string> end; 的目的,但我将其包括在内,因为它在原始发帖人的回答中。

到目前为止,我得到了这个不起作用的代码:

vector<string> allTheText;
          stringstream strstr;
          istream_iterator<std::string> end;
          istream_iterator<std::string> it(strstr);

          while (!streamOfText.eof()){
                getline (streamOfText, readTextLine);
                cout<<readTextLine<<endl;

                stringstream strstr(readTextLine);
                // how should I initialize the iterators it and end here?

                }

编辑:

我把代码改成

          vector<string> allTheText;
          stringstream strstr;
          istream_iterator<std::string> end;
          istream_iterator<std::string> it(strstr);

          while (getline(streamOfText, readTextLine)) {
               cout << readTextLine << endl;

        vector<string> vec((istream_iterator<string>(streamOfText)), istream_iterator<string>()); // generates RuntimeError


          }

得到一个 RuntimeError,为什么?

【问题讨论】:

  • 你应该已经把你写的整个代码替换成了我的一行代码!正如我在回答中暗示的那样,循环是不必要的。

标签: c++ string iterator


【解决方案1】:

在 C++ 中使用 while (!….eof()) 循环被破坏了,因为当流进入错误状态时循环将永远不会退出!

相反,您应该直接测试流的状态。适应您的代码,这可能如下所示:

while (getline(streamOfText, readTextLine)) {
    cout << readTextLine << endl;
}

但是,您已经拥有一个流。为什么还要将其放入字符串流中?还是出于任何原因需要逐行执行此操作?

您可以直接使用输入迭代器初始化您的向量。无需构建字符串流,也无需使用copy 算法,因为有适当的构造函数重载。

vector<string> vec((istream_iterator<string>(cin)), istream_iterator<string>());

注意第一个参数周围的额外括号,这是消除函数声明歧义所必需的。

编辑这个代码做了什么的小解释:

C++ 提供了一种统一的方式来指定范围。范围只是类型值的集合,没有详细说明这些值是如何存储的。在 C++ 中,这些范围表示为半开区间 [a, b[。这意味着一个范围由两个 迭代器 分隔(它们有点像指针,但更通用;指针是一种特殊的迭代器)。第一个迭代器a 指向范围的第一个元素。第二个,b,指向最后一个元素之后。为什么在后面?因为这允许非常容易地迭代元素:

for (Iterator i = a; i != b; ++i)
    cout << *i;

像指针一样,迭代器通过应用*取消引用。这将返回它们的值。

C++ 中的容器类(例如vectorlist)有一个特殊的构造函数,它允许将值从另一个范围轻松复制到新容器中。因此,此构造函数需要两个迭代器。例如,以下将 C 样式的数组复制到向量中:

int values[3] = { 1, 2, 3 };
vector<int> v(values, values + 3);

这里,values&amp;values[0] 的同义词,这意味着它指向数组的第一个元素。 values + 3,由于指针算法,几乎等同于&amp;values[3](但这是无效 C++!)并指向虚拟元素后面 em> 数组。

现在,我上面的代码与上一个示例完全相同。唯一的区别是我使用的迭代器类型。我没有使用普通指针,而是使用 C++ 提供的特殊迭代器类。这个迭代器类包装一个输入流,++推进输入流和*从流中读取下一个元素。元素的种类由 type 参数指定(因此在本例中为 string)。

为了使它作为一个范围工作,我们需要指定一个开始和结束。唉,我们不知道输入的结尾(这是合乎逻辑的,因为随着用户在控制台中输入更多输入,流的结尾实际上可能会随着时间而移动!)。因此,要创建一个虚拟 end 迭代器,我们不向istream_iterator 的构造函数传递任何参数。相反,要创建一个开始迭代器,我们传递一个输入流。然后创建一个迭代器,指向流中的当前位置(此处为cin)。

我上面的代码在功能上等价于:

istream_iterator<string> front(cin);
istream_iterator<string> back;

vector<string> vec;

for (istream_iterator<string> i = front; i != back; ++i)
    vec.push_back(*i);

这又相当于使用以下循环:

string word;
while (cin >> word)
    vec.push_back(word);

【讨论】:

  • 为什么会有 (istream_iterator(cin),为什么是 cin?这个向量会在不覆盖的情况下获取 xt 文件的全部内容吗?
  • 我的意思是,我会从 ifstream 中读取文本文件,我想我应该将 ifstream 的名称放在 cin 所在的位置,对吧?
  • @dmindreader: cin 就是一个例子 - istream_iterator 可以从任何 istream 构造。 @Konrad:您可能会澄清 istream_iterator 的无参数构造函数是说“从流中没有更多的东西”(我忘记了 Josuttis 是怎么说的,我知道它更好)。
  • @dmindreader:是的。 cin 只是一个例子,主要是因为它比你的变量名短。 ;-)
  • @Harper:我已经发布了一些解释。这让你满意吗?我毫不怀疑 Josutti 比我写得更好,但我只能建议大家阅读 Josutti 的书。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-22
  • 1970-01-01
  • 2020-10-21
  • 2010-09-11
  • 1970-01-01
  • 2014-04-27
  • 2020-07-30
相关资源
最近更新 更多