Есть ли итератор С++, который может перебирать файл построчно?

Я хотел бы получить итератор в стиле istream_iterator, который возвращает каждую строку файла в виде строки, а не каждого слова. Это возможно?


person thehouse    schedule 18.02.2010    source источник
comment
Я думаю, вы всегда можете написать свою собственную, используя функцию getline(), как сказал Маттео Италия.   -  person Jaime Garcia    schedule 18.02.2010
comment
Дубликат: stackoverflow.com/questions/1567082/   -  person Jerry Coffin    schedule 18.02.2010
comment
@Jerry: В этой ветке есть ответ. Но вопрос совсем в другом.   -  person UncleBens    schedule 18.02.2010
comment
@UnbleBens: вопрос сформулирован по-другому, но на самом деле он не сильно отличается.   -  person Jerry Coffin    schedule 18.02.2010
comment
@Джерри: Спасибо! Я собираюсь пойти с решением, которое вы опубликовали на другой вопрос. Но я согласен с UncleBens, что я вообще не задавал этот вопрос. Мне особенно нужен «итератор», поскольку функция, которую я передаю, принимает начало и конец.   -  person thehouse    schedule 19.02.2010
comment
Кроме того, я заметил некоторые другие ответы на этот вопрос ранее, и когда я проверил, они исчезли. Почему это может произойти (это мой первый вопрос SO)?   -  person thehouse    schedule 19.02.2010
comment
@thehouse - я удалил свой ответ, когда понял, что точный трюк уже был опубликован Джерри в той другой ветке (на самом деле, вполне вероятно, что я действительно научился этому именно там). Я восстановлю ответ и заменю его ссылкой на ответ Джерри.   -  person Manuel    schedule 19.02.2010
comment
@thehouse: ответы могут исчезнуть, когда/если кто-то их удалит. Практически любой может удалить свой ответ, и модераторы также могут удалять сообщения других людей. Что бы это ни стоило, есть целый веб-сайт (meta.stackoverflow.com), посвященный подобным вопросам о stackoverflow.   -  person Jerry Coffin    schedule 19.02.2010
comment
Подумав еще немного, я бы согласился с тем, что между этим вопросом и предыдущим есть некоторая разница, но не большая, но ответ UncleBens на этот вопрос также довольно хорошо отвечает на этот вопрос, поэтому я все еще думаю разница в основном в формулировках, но какого черта...   -  person Jerry Coffin    schedule 19.02.2010


Ответы (7)


EDIT: Этот же трюк уже был опубликован кем-то другим в предыдущей теме.

Легко заставить std::istream_iterator делать то, что вы хотите:

namespace detail 
{
    class Line : std::string 
    { 
        friend std::istream & operator>>(std::istream & is, Line & line)
        {   
            return std::getline(is, line);
        }
    };
}

template<class OutIt>
void read_lines(std::istream& is, OutIt dest)
{
    typedef std::istream_iterator<detail::Line> InIt;
    std::copy(InIt(is), InIt(), dest);
}

int main()
{
    std::vector<std::string> v;
    read_lines(std::cin, std::back_inserter(v));

    return 0;
}
person Community    schedule 18.02.2010
comment
Не нарушает ли здесь наследование от std::string правило 35 стандарта кодирования C++: избегайте наследования от классов, которые не предназначены для использования в качестве базовых классов? - person thehouse; 19.02.2010
comment
@thehouse - Какой стандарт кодирования вы имеете в виду? Я не думаю, что есть что-то неправильное в использовании произвольного класса в качестве основы при условии, что он не используется в полиморфном контексте. Например, схема наследования в моем ответе была бы опасной, если бы я сделал что-то вроде string * ptr = new Line; delete ptr;, но здесь это не так. - person Manuel; 19.02.2010
comment
@Manuel: Херб Саттер и Андрей Александреску в своей книге «Стандарты кодирования C ++» заявляют, что использование автономного класса в качестве основы является серьезной ошибкой проектирования, и ее следует избегать. Далее они явно упоминают строку как плохой класс для наследования. Их аргумент вращается вокруг того факта, что вы должны сделать один набор вещей, чтобы обеспечить безопасную работу базового класса, и противоречивый набор вещей, чтобы сделать безопасными конкретные классы. - person thehouse; 19.02.2010
comment
Это неправильно, совершенно неправильно, и в исходном примере такого не было (автор мудро выбрал вместо него Composition). @Manuel докажите мне, что никто не будет использовать их в полиморфном контексте... Я жду. - person Matthieu M.; 19.02.2010
comment
Не совсем так, но лучше я признаю это. Извините, но я не люблю наследование в целом... и, конечно, частное наследование, когда подойдет композиция. Наверное, у всех нас есть свои привычки... - person Matthieu M.; 20.02.2010
comment
Можете ли вы объяснить, почему нам нужно было наследовать от строкового класса? - person Mr.Anubis; 06.09.2011
comment
В конце концов я использовал этот метод, но сохранение std::string в качестве члена, а не наследование - дело вкуса. - person thehouse; 29.05.2012
comment
Стоит отметить, что наследование — хороший способ настройки интерфейсов. Это легко читать и понимать. Если новые члены не введены, то логика на основе кучи вас не разрушит. Все, что сложнее, напрашивается на неприятности - person Polymer; 12.11.2013

Стандартная библиотека не предоставляет итераторов для этого (хотя вы можете реализовать что-то подобное самостоятельно), но вы можете просто использовать функция getline (не метод istream) для чтения всей строки из входного потока в строку C++.

Пример:

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    ifstream is("test.txt");
    string str;
    while(getline(is, str))
    {
        cout<<str<<endl;
    }
    return 0;
}
person Matteo Italia    schedule 18.02.2010
comment
Обрабатывает ли он разницу в символах eol для разных платформ (windows/unix/mac)? - person Kelly S. French; 13.07.2011
comment
Это различие уже обрабатывается в объекте потока: когда вы открываете файл в текстовом режиме (по умолчанию, если вы не укажете флаг ios::binary), поток автоматически преобразует eol для конкретной платформы в обычный \n. - person Matteo Italia; 13.07.2011
comment
мы используем COM istream, который по-разному относился к EOL. Анализ файла dos работал, но анализ файла UNIX (без LF) приводил к тому, что он обрабатывался так, как если бы это была одна большая строка. - person Kelly S. French; 14.07.2011
comment
@Келли: эээ, подожди; std::istream корректно конвертирует только EOL, родной для текущей платформы, для других, вероятно, ничего не сделает. Кроме того, теперь вы говорите о потоке COM, поэтому вам следует обратиться к его документации. - person Matteo Italia; 15.07.2011

Вот решение. Пример печатает входной файл с @@ в конце каждой строки.

#include <iostream>
#include <iterator>
#include <fstream>
#include <string>

using namespace std;

class line : public string {};

std::istream &operator>>(std::istream &is, line &l)
{
    std::getline(is, l);
    return is;
}

int main()
{
    std::ifstream inputFile("input.txt");

    istream_iterator<line> begin(inputFile);
    istream_iterator<line> end;

    for(istream_iterator<line> it = begin; it != end; ++it)
    {
        cout << *it << "@@\n";
    }

    getchar();
}

Редактировать: Мануэль был быстрее.

person Rexxar    schedule 18.02.2010

Вы можете написать свой собственный итератор. Это не так сложно. Итератор — это просто класс, в котором (просто говоря) определены операторы приращения и *.

Посетите http://www.drdobbs.com/cpp/184401417, чтобы начать писать свой собственные итераторы.

person Patrick    schedule 18.02.2010
comment
@thehouse: вы также можете проверить boost::iterator_facade, который реализует полную концепцию итератора STL с точки зрения нескольких основных функций. - person Emile Cormier; 19.02.2010

Вы можете использовать istreambuf_iterator вместо istream_iterator. Он не игнорирует управляющие символы, такие как istream_iterator.

code.cpp:

#include <iterator>
#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ifstream file("input.txt");

    istreambuf_iterator<char> i_file(file);

    istreambuf_iterator<char> eof;

    std::string buffer;
    while(i_file != eof)
    {
        buffer += *i_file;
        if(*i_file == '\n')
        {
            std::cout << buffer;
            buffer.clear();
        }
        ++i_file;
    }

    return 0;
}

входной файл.txt:

ahhhh test *<-- There is a line feed here*
bhhhh second test *<-- There is a line feed here*

вывод:

ahhhh test
bhhhh second test
person coelhudo    schedule 18.02.2010

Вот довольно чистый подход, который использует boost::tokenizer . Это возвращает объект, предоставляющий функции-члены begin() и end(); полный интерфейс см. в документации класса tokenizer .

#include <boost/tokenizer.hpp>
#include <iostream>
#include <iterator> 


using istream_tokenizer = boost::tokenizer<boost::char_separator<char>,
                                           std::istreambuf_iterator<char>>;

istream_tokenizer line_range(std::istream& is);
{
    using separator = boost::char_separator<char>;

    return istream_tokenizer{std::istreambuf_iterator<char>{is},
                             std::istreambuf_iterator<char>{},
                             separator{"\n", "", boost::keep_empty_tokens}};
}

Это жестко кодирует char как тип символа потока, но это может быть шаблоном.

Функцию можно использовать следующим образом:

#include <sstream>

std::istringstream is{"A\nBB\n\nCCC"};

auto lines = line_range(is);
std::vector<std::string> line_vec{lines.begin(), lines.end()};
assert(line_vec == (std::vector<std::string>{{"A", "BB", "", "CCC"}}));

Естественно, его также можно использовать с std::ifstream, созданным путем открытия файла:

std::ifstream ifs{"filename.txt"};
auto lines = line_range(ifs);
person NicholasM    schedule 10.10.2019
comment
Рад видеть ответ с токенизатором - person prehistoricpenguin; 10.07.2020

В связанном потоке cin-line-by-line, процитированное выше, Джерри Коффин описал «другую возможность (которая) использует часть стандартной библиотеки, о существовании которой большинство людей даже не подозревают». Следующее применяет этот метод (который я искал) для решения построчной проблемы итерации по файлу, как это было запрошено в текущем потоке.

Сначала фрагмент, скопированный непосредственно из ответа Джерри в соответствующей ветке:

struct line_reader: std::ctype<char> {
line_reader(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
    static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());
    rc['\n'] = std::ctype_base::space;
    return &rc[0];
}}; 

А теперь наполните ifstream пользовательской локалью, как описано Джерри, и скопируйте из infstream в ofstream.

ifstream is {"fox.txt"};
is.imbue(locale(locale(), new line_reader()));
istream_iterator<string> ii {is};
istream_iterator<string> eos {};

ofstream os {"out.txt"};
ostream_iterator<string> oi {os,"\n"};

vector<string> lines {ii,eos};
copy(lines.begin(), lines.end(), oi);

Выходной файл ("out.txt") будет точно таким же, как входной файл ("fox.txt").

person winvicta    schedule 08.04.2019