C++ - 有没有一种优雅的方法来读取文本文件?

3
我希望您能用一种优雅的方式将文本文件读入一个2x2向量中。我正在使用以下方法将数据写入文本文件:
ofstream FILE(theFile, ios::out | ofstream::binary);
for(const auto& vt : data) {
   std::copy(vt.begin(), vt.end(), std::ostream_iterator<double>(FILE, " "));
   FILE << "\n\n";
 }

我的问题是,是否有一种类似的方法可以使用std :: istream_iterator来读取文本文件的内容?
编辑:
数据:
std::vector<std::vector<double> > data; 

请展示你的 data 声明。它是一个双重向量的向量吗? - P0W
一种优雅(但不是特别高效)的方法是逐行读取文本,将每行存储在流中,然后读取该流以获取每个元素。 - Manu343726
你可以使用 back_inserteristream_iterator 来使它更加优雅。但是,优雅是观察者的眼中之物。为什么不直接做事情呢 -- 这样你也会拥有更多的控制。 - Cheers and hth. - Alf
1
不推荐使用嵌套向量(vector of vectors)参考链接 - oblitum
2个回答

5

既然你坚持使用std::istream_iterator,那么这是一种方法

#include <sstream>
//....

std::vector<double> v ;
std::string str ;

while ( std::getline(FILE, str) )
{

    std::stringstream ss(str);

    std::copy( std::istream_iterator<double>(ss),
               std::istream_iterator<double>(),
               std::back_inserter(v)
             ) ;

    data.push_back( v ); // data is your vector of vector
    v.clear( );
}

只是想知道,他不需要处理双换行符吗? - Veritas
1
@Veritas 可能不会,因为 std::istream_iterator<double>() 将处理本地的 stringstream 对象,而不是 FILE - P0W
1
建议:将 v 设为本地变量(可能具有初始保留大小),在向 data 推送时使用移动语义,删除 v.clear() - oblitum
@pepper_chico 实际上,他甚至不需要 std::copy。 std::stringstream ss(str); data.push_back({std::istream_iterator<double>(ss),{}}) - Veritas

1
你可以制作一个使用向量作为底层类型的迭代器。它可以类似于std::istream_iterator使用:
#include <vector>
#include <sstream>
#include <string>
#include <algorithm>
#include <iterator>

template <
    class Value,
    class Container       = std::vector<Value>,
    class charT           = char,
    class traits          = std::char_traits<charT>
>
class line_iterator
    : public std::iterator<std::input_iterator_tag, std::vector<Value>>
{
public:
    using value_type      = Value;
    using container_type  = Container;
private:
    std::basic_istream<charT, traits>*      is_;
    std::basic_string<charT, traits>        line;
    std::basic_istringstream<charT, traits> str;
    container_type                          temp;
    int                                     count = -1;
public:
    line_iterator(std::basic_istream<charT, traits>& is,
                  int count = -1) noexcept
        : is_(&is), count(count)
    { this->read(); }

    constexpr line_iterator() noexcept : is_(nullptr)
    { }

    constexpr line_iterator(const line_iterator& rhs) noexcept
        : is_(rhs.is_), temp(rhs.temp), count(rhs.count)
    { };

    constexpr container_type operator*() const noexcept
    {
        return std::move(temp);
    }

    line_iterator& operator++()
    {
        this->read();
        return *this;
    }

    line_iterator operator++(int)
    {
        line_iterator copy(*this);
        ++*this;
        return std::move(copy);
    }

    friend constexpr bool operator==(const line_iterator& lhs,
                                     const line_iterator& rhs) noexcept
    {
        return lhs.is_ == rhs.is_;
    }

    friend constexpr bool operator!=(const line_iterator& lhs,
                                     const line_iterator& rhs) noexcept
    {
        return !(lhs == rhs);
    }
private:
    void read()
    {
        temp.clear();

        if (is_)
        {
            if (std::getline(*is_, line))
            {
                str.str(line);

                if (count == -1)
                    temp.assign(
                    std::istream_iterator<Value, charT, traits>{str}, {});
                else if (0 <= count)
                    std::copy_n(
                        std::istream_iterator<Value, charT, traits>{str},
                        count,
                        std::back_inserter(temp));
            }

            if (!*is_)
                is_ = nullptr;

            str.clear();
        }
    }
};

template <
    class Value,
    class Container       = std::vector<Value>,
    class charT           = char,
    class traits          = std::char_traits<charT>
>
line_iterator<Value, Container, charT, traits>
constexpr line_iter(std::basic_istream<charT, traits>& is,
                    int count = -1) noexcept
{
    return line_iterator<Value, Container, charT, traits>{is, count};
}

template<class Value>
constexpr line_iterator<Value> line_iter() noexcept
{
    return line_iterator<Value>{};
}

现在你可以使用以下代码初始化向量:

std::vector<std::vector<double>> v{line_iter<double>(FILE), line_iter<double>()};

实时演示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接