C ++ токенизация с использованием итераторов в цикле eof () - PullRequest
3 голосов
/ 27 января 2009

Я пытаюсь адаптировать этот ответ

Как токенизировать строку в C ++?

к моей текущей строковой проблеме, которая включает чтение из файла до eof.

из этого исходного файла:

Fix grammatical or spelling errors

Clarify meaning without changing it

Correct minor mistakes

Я хочу создать вектор со всеми токенизированными словами. Пример: v ector<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?

                }

Edit:

Я изменил код на

          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, почему?

1 Ответ

9 голосов
/ 28 января 2009

Использование цикла while (!….eof()) в C ++ прервано, потому что цикл никогда не завершится, когда поток перейдет в состояние ошибки!

Скорее, вы должны проверить состояние потока напрямую. Адаптированный к вашему коду, это может выглядеть так:

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 ++ (например, vector, list) имеют специальный конструктор, который позволяет легко копировать значения из другого диапазона в новый контейнер. Следовательно, этот конструктор ожидает двух итераторов. Например, следующий код копирует массив стиля C в вектор:

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

Здесь values является синонимом &values[0], что означает, что он указывает на первый элемент массива. values + 3, благодаря арифметике указателей, почти эквивалентен &values[3] (но это недопустимо C ++!) И указывает на виртуальный элемент позади массив.

Теперь мой код выше делает то же самое, что и в последнем примере. Единственное отличие - это тип итератора, который я использую. Вместо использования простого указателя я использую специальный класс итераторов, предоставляемый C ++. Этот класс итератора оборачивает входной поток таким образом, что ++ продвигает входной поток и * читает следующий элемент из потока. Тип элемента определяется аргументом типа (следовательно, 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...