读取文件以构造具有多种成员类型的文件

时间:2020-04-25 14:46:03

标签: c++ struct

我有一个结构:

struct foo {
    int a;
    string b;
    bool c;
    //... ad infinitum
};

我想用.txt文件中的数据加载它,就像这样:

string a, b, c;
string* ptr[] = { &a, &b, &c };
ifstream file("input.txt");
size_t i = 0;
while (file >> *ptr[i])
    (i < 3) ? i++ : i = 0;
//convert a, c to int and bool, then assign

但是随后我将不得不手动将它们从字符串类型转换为int或bool类型,是否有必要加载它们,而无需随后进行转换(也许使用void * ptr [])?
我不要这样:

while (!file.eof()){
  file>>a;
  file>>b;
  file>>c;
}

因为Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?,而且我不想file>> 10次。

我还没有学过诸如lexical_cast或运算符重载之类的东西,因此与它们相关的任何答案都将非常有帮助,但可能无法解决我的问题。

这是txt文件:

19127519
Jame Howard
0
19124567
Jacky Thomas
1
14527890
Lucas
1

3 个答案:

答案 0 :(得分:2)

您的解决方案中有几个问题

  • 当您需要std::stringint时,您仅读bool
  • 您读取std::string的方式将任何空格都视为分隔符,这意味着“ Jame Howard”不会读入一个字符串,而是读入两个分隔符,这不是您的代码在索引管理中所假定的< / li>
  • 您(错误地)仅保存了最后一个三元组而丢失了另一个

打开文件时,请始终检查您是否能够这样做。

您可以这样做:

#include <fstream>
#include <iostream>
#include <vector>
#include <string>

struct foo {
  int a;
  std::string b;
  bool c;
  //... ad infinitum
};

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

  if (!file) 
    std::cerr << "cannot open input.txt" << std::endl;
  else {
    std::vector<foo> foos; // to save all struct
    foo f; // to read one struict

    while ((file >> f.a) &&
           file.ignore(std::numeric_limits<std::streamsize>::max(), '\n') && // flush rest of line
           std::getline(file, f.b) &&
           (file >> f.c))
      foos.push_back(f);

    // check
    for (auto f : foos)
      std::cout << f.a << ' ' << f.b << ' ' << f.c << std::endl;
  }
}

该字符串可以包含空格,因此在 f foo 的情况下无法使用file >> f.b,可以使用getline,但因为每个元素都在单独的行中,所以必须在读取 int 之后刷新行尾。

编译和执行:

pi@raspberrypi:~ $ g++ -g -Wall f.cc
pi@raspberrypi:~ $ cat input.txt 
19127519
Jame Howard
0
19124567
Jacky Thomas
1
14527890
Lucas
1
pi@raspberrypi:~ $ ./a.out
19127519 Jame Howard 0
19124567 Jacky Thomas 1
14527890 Lucas 1
pi@raspberrypi:~ $ 

与其将代码放置为在 main 中读取 foo ,不如定义operator >>(如果需要的话,operator << ),所以:

#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <limits>

struct foo {
  int a;
  std::string b;
  bool c;
  //... ad infinitum
};

std::istream& operator >>(std::istream & in, foo & f) {
  if ((in >> f.a) &&
      in.ignore(std::numeric_limits<std::streamsize>::max(), '\n') && // flush rest of line
      std::getline(in, f.b))
    in >> f.c;
  return in;
}

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

  if (!file) 
    std::cerr << "cannot open input.txt" << std::endl;
  else {
    std::vector<foo> foos;
    foo f;

    while (file >> f)
      foos.push_back(f);

    for (auto f : foos)
      std::cout << f.a << ' ' << f.b << ' ' << f.c << std::endl;
  }
}

如果 foo 具有私有属性operator <<必须在 foo

中声明friend

答案 1 :(得分:0)

如果我正确理解了您的问题,则您想要执行以下操作(伪代码):

foreach member m of foo
{
    file >> m;
}

因为您要避免手动填充每个成员。

如果这是您想要的,那么我认为没有简单的解决方案。我不了解您程序的其余部分,因此无法确定它是否与您的项目兼容,但我建议您更改结构的设计。以您当前使用的方式,您无法真正遍历成员。您应该考虑将其更改为单个小型结构:

struct foo {
    int a;
    std::string b;
    bool c;
    // no other members here!
}

和一个包含其中许多内容的std::vector<foo>。这样,您可以按照布鲁诺回答的方式反复填充它们。当然,这仅在成员中存在重复模式(在这种情况下,例如int, string, bool, int, string, bool, int, string, bool, ...)时有效。

正如我已经说过的,我不确定我是否正确理解了您的问题以及您的程序是否支持这种设计,但是希望我能为您提供帮助。

编辑: 也许您应该只寻找某种序列化(例如JSON)。这正是您想要的,并且有很多图书馆为您服务。

答案 2 :(得分:0)

您可以使用BOOST_HANA_DEFINE_STRUCT反映结构成员的编译时间。

工作示例:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>

#include <boost/hana.hpp>
namespace hana = boost::hana;

struct Name {
    std::string value;
};

inline std::istream& operator>>(std::istream& is, Name& name) {
    is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    return getline(is, name.value);
}

inline std::ostream& operator<<(std::ostream& os, Name const& name) {
    return os << name.value;
}

template<class T>
std::istream& load(std::istream& is, T& object) {
    hana::for_each(hana::accessors<T>(), [&](auto accessor) {
        is >> hana::second(accessor)(object);
    });
    return is;
};

template<class T>
std::ostream& save(std::ostream& os, T const& object) {
    hana::for_each(hana::accessors<T>(), [&](auto accessor) {
        os << hana::second(accessor)(object) << '\n';
    });
    return os;
};

struct Foo {
    BOOST_HANA_DEFINE_STRUCT(
        Foo,
        (int, a),
        (Name, b),
        (bool, c)
        );
};

int main() {
    std::ifstream is("input.txt");
    std::vector<Foo> foos;

    for(Foo t; load(is, t);)
        foos.push_back(t);

    for(auto const& t : foos)
        save(std::cout, t);
}