35

を 1 行ずつ反復処理しstd::cin、各行を としてアドレス指定しstd::stringます。どちらが良いですか:

string line;
while (getline(cin, line))
{
    // process line
}

また

for (string line; getline(cin, line); )
{
    // process line
}

? これを行う通常の方法は何ですか?

4

5 に答える 5

80

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));

重要な点は、何かを読むときは行を指定することですが、それ以外の場合は文字列だけです。

もう 1 つの可能性は、ほとんどの人がほとんど存在すら知らない標準ライブラリの一部を使用することです。operator>> を使用して文字列を読み取ると、ストリームは、そのストリームのロケールが空白文字であると言っているものまでの文字列を返します。特に、すべて行指向の多くの作業を行っている場合は、改行のみを空白として分類する 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];
    }
};  

これを使用するには、読み取り元のストリームにそのファセットを使用するロケールを吹き込み、通常どおり文字列を読み取り、文字列の operator>> は常に行全体を読み取ります。たとえば、行単位で読み取り、一意の行をソート順に書き出したい場合は、次のようなコードを使用できます。

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通常は失敗します)。

于 2009-10-14T17:10:50.303 に答える
8

What I have (written as an exercise, but perhaps turns out useful one day), is 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

So your loop could be replaced with an algorithm (another recommended practice in C++):

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

Perhaps a common task is to store every line in a container:

vector<string> lines((LineInputIterator<>(stream)), LineInputIterator<>());
于 2009-10-14T16:07:34.013 に答える
1

最初の1つ。

どちらも同じことを行いますが、最初のものははるかに読みやすく、さらにループが完了した後も文字列変数を保持できます (2 番目のオプションでは、for ループ スコープに囲まれています)。

于 2009-10-14T15:29:33.513 に答える
0

while ステートメントを使用します。

Steve McConell による Code Complete 2 の 16.2 章 (具体的には 374 および 375 ページ) を参照してください。

引用するには:

while ループの方が適切な場合は、for ループを使用しないでください。C++、C#、および Java での柔軟な for ループ構造の一般的な乱用は、while ループの内容を for ループ ヘッダーに無計画に詰め込むことです。

.

for ループ ヘッダーに乱用された while ループの C++ の例

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

C++ while ループの適切な使用例

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

途中一部省略していますが、参考になれば幸いです。

于 2009-10-14T15:30:11.990 に答える