Как я выполняю итерации по cin линию за линией в C++?

Мы использовали API от EarthTools с большим успехом:

EarthTools

сервис возвращает XML с информацией, такой как текущий GMT & смещения часового пояса (при предоставлении широты & долгота). Мы использовали WebClient в сочетании с XMLDocument в бэкенде VB нашей страницы ASP.NET для чтения & интерпретируйте XML.

32
задан Wolf 17 March 2014 в 15:24
поделиться

4 ответа

Что у меня есть ( написано в качестве упражнения, но, возможно, однажды окажется полезным), это LineInputIterator:

#ifndef UB_LINEINPUT_ITERATOR_H
#define UB_LINEINPUT_ITERATOR_H

#include <iterator>
#include <istream>
#include <string>
#include <cassert>

namespace ub {

template <class StringT = std::string>
class LineInputIterator :
    public std::iterator<std::input_iterator_tag, StringT, std::ptrdiff_t, const StringT*, const StringT&>
{
public:
    typedef typename StringT::value_type char_type;
    typedef typename StringT::traits_type traits_type;
    typedef std::basic_istream<char_type, traits_type> istream_type;

    LineInputIterator(): is(0) {}
    LineInputIterator(istream_type& is): is(&is) {}
    const StringT& operator*() const { return value; }
    const StringT* operator->() const { return &value; }
    LineInputIterator<StringT>& operator++()
    {
        assert(is != NULL);
        if (is && !getline(*is, value)) {
            is = NULL;
        }
        return *this;
    }
    LineInputIterator<StringT> operator++(int)
    {
        LineInputIterator<StringT> prev(*this);
        ++*this;
        return prev;
    }
    bool operator!=(const LineInputIterator<StringT>& other) const
    {
        return is != other.is;
    }
    bool operator==(const LineInputIterator<StringT>& other) const
    {
        return !(*this != other);
    }
private:
    istream_type* is;
    StringT value;
};

} // end ub
#endif

Итак, ваш цикл можно заменить алгоритмом (еще одна рекомендуемая практика в C ++):

for_each(LineInputIterator<>(cin), LineInputIterator<>(), do_stuff);

Возможно, распространенной задачей является сохранение каждой строки в контейнер:

vector<string> lines((LineInputIterator<>(stream)), LineInputIterator<>());
8
ответ дан 27 November 2019 в 20:11
поделиться

Первый.

Оба делают то же самое, но первый гораздо более читабелен, плюс вы можете сохранить строковую переменную после завершения цикла (во 2-м варианте он заключен в область действия цикла for)

1
ответ дан 27 November 2019 в 20:11
поделиться

Поскольку UncleBen поднял свой LineInputIterator, я подумал, что добавлю еще пару альтернативных методов. Во-первых, очень простой класс, который действует как строковый прокси:

class line {
    std::string data;
public:
    friend std::istream &operator>>(std::istream &is, line &l) {
        std::getline(is, l.data);
        return is;
    }
    operator std::string() const { return data; }    
};

С этим вы все равно будете читать, используя обычный istream_iterator. Например, чтобы прочитать все строки в файле в векторе строк, вы можете использовать что-то вроде:

std::vector<std::string> lines;

std::copy(std::istream_iterator<line>(std::cin), 
          std::istream_iterator<line>(),
          std::back_inserter(lines));

Ключевым моментом является то, что когда вы читаете что-то, вы указываете строку - но в противном случае у вас есть просто строки.

Другая возможность использует часть стандартной библиотеки, о существовании которой большинство людей даже не подозревает, не говоря уже о том, что она очень полезна. Когда вы читаете строку с помощью оператора >>, поток возвращает строку символов вплоть до того, что локаль этого потока говорит, что это символ пробела. Особенно если ты Если вы делаете много работы, ориентированной на строки, может быть удобно создать локаль с фасетом ctype, который классифицирует новую строку только как пробел:

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];
    }
};  

Чтобы использовать это, вы наполняете поток, который вы собираетесь для чтения из языкового стандарта, использующего этот фасет, тогда просто считайте строки в обычном режиме, а оператор >> для строки всегда читает всю строку. Например, если мы хотим читать строки и записывать уникальные строки в отсортированном порядке, мы могли бы использовать такой код:

int main() {
    std::set<std::string> lines;

    // Tell the stream to use our facet, so only '\n' is treated as a space.
    std::cin.imbue(std::locale(std::locale(), new line_reader()));

    std::copy(std::istream_iterator<std::string>(std::cin), 
        std::istream_iterator<std::string>(), 
        std::inserter(lines, lines.end()));

    std::copy(lines.begin(), lines.end(), 
        std::ostream_iterator<std::string>(std::cout, "\n"));
    return 0;
}

Имейте в виду, что это влияет на весь ввод из потока. Использование этого в значительной степени исключает смешивание строчно-ориентированного ввода с другим вводом (например, чтение числа из потока с использованием stream >> my_integer обычно приводит к ошибке).

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];
    }
};  

Чтобы использовать это, вы наполняете поток, из которого собираетесь читать, языковой стандарт, использующий этот фасет, затем просто читаете строки как обычно, а оператор >> для строки всегда читает целую строку. Например, если мы хотим читать строки и записывать уникальные строки в отсортированном порядке, мы могли бы использовать такой код:

int main() {
    std::set<std::string> lines;

    // Tell the stream to use our facet, so only '\n' is treated as a space.
    std::cin.imbue(std::locale(std::locale(), new line_reader()));

    std::copy(std::istream_iterator<std::string>(std::cin), 
        std::istream_iterator<std::string>(), 
        std::inserter(lines, lines.end()));

    std::copy(lines.begin(), lines.end(), 
        std::ostream_iterator<std::string>(std::cout, "\n"));
    return 0;
}

Имейте в виду, что это влияет на весь ввод из потока. Использование этого в значительной степени исключает смешивание строчно-ориентированного ввода с другим вводом (например, чтение числа из потока с использованием stream >> my_integer обычно приводит к ошибке).

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];
    }
};  

Чтобы использовать это, вы наполняете поток, из которого собираетесь читать, языковой стандарт, использующий этот фасет, затем просто читаете строки как обычно, а оператор >> для строки всегда читает целую строку. Например, если мы хотим читать по строкам и записывать уникальные строки в отсортированном порядке, мы могли бы использовать такой код:

int main() {
    std::set<std::string> lines;

    // Tell the stream to use our facet, so only '\n' is treated as a space.
    std::cin.imbue(std::locale(std::locale(), new line_reader()));

    std::copy(std::istream_iterator<std::string>(std::cin), 
        std::istream_iterator<std::string>(), 
        std::inserter(lines, lines.end()));

    std::copy(lines.begin(), lines.end(), 
        std::ostream_iterator<std::string>(std::cout, "\n"));
    return 0;
}

Имейте в виду, что это влияет на весь ввод из потока. Использование этого в значительной степени исключает смешивание строчно-ориентированного ввода с другим вводом (например, чтение числа из потока с использованием stream >> my_integer обычно приводит к ошибке).

мы могли бы использовать такой код:

int main() {
    std::set<std::string> lines;

    // Tell the stream to use our facet, so only '\n' is treated as a space.
    std::cin.imbue(std::locale(std::locale(), new line_reader()));

    std::copy(std::istream_iterator<std::string>(std::cin), 
        std::istream_iterator<std::string>(), 
        std::inserter(lines, lines.end()));

    std::copy(lines.begin(), lines.end(), 
        std::ostream_iterator<std::string>(std::cout, "\n"));
    return 0;
}

Имейте в виду, что это влияет на весь ввод из потока. Использование этого в значительной степени исключает смешивание строчно-ориентированного ввода с другим вводом (например, чтение числа из потока с использованием stream >> my_integer обычно приводит к ошибке).

мы могли бы использовать такой код:

int main() {
    std::set<std::string> lines;

    // Tell the stream to use our facet, so only '\n' is treated as a space.
    std::cin.imbue(std::locale(std::locale(), new line_reader()));

    std::copy(std::istream_iterator<std::string>(std::cin), 
        std::istream_iterator<std::string>(), 
        std::inserter(lines, lines.end()));

    std::copy(lines.begin(), lines.end(), 
        std::ostream_iterator<std::string>(std::cout, "\n"));
    return 0;
}

Имейте в виду, что это влияет на весь ввод из потока. Использование этого в значительной степени исключает смешивание строчно-ориентированного ввода с другим вводом (например, чтение числа из потока с использованием stream >> my_integer обычно приводит к ошибке).

72
ответ дан 27 November 2019 в 20:11
поделиться

Используйте оператор while.

См. Главу 16.2 (особенно страницы 374 и 375) Code Complete 2 Стива МакКонелла.

Цитата:

Не используйте цикл for, если цикл while более уместен. Распространенное злоупотребление гибкой структурой цикла for в C ++, C # и Java - это беспорядочное запихивание содержимого цикла while в заголовок цикла for

.

C ++ Пример цикла while, оскорбительно забитого в заголовок цикла for

for (inputFile.MoveToStart(), recordCount = 0; !inputFile.EndOfFile(); recordCount++) {
    inputFile.GetRecord();
}

C ++ Пример правильного использования цикла while

inputFile.MoveToStart();
recordCount = 0;
while (!InputFile.EndOfFile()) {
    inputFile.getRecord();
    recordCount++;
}

Я пропустил некоторые части в середине, но, надеюсь, это даст вам хорошее представление. 119751]

0
ответ дан 27 November 2019 в 20:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: