читать файл для структурирования с несколькими типами членов - PullRequest
1 голос
/ 25 апреля 2020

У меня есть структура:

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

Потому что Почему iostream :: eof внутри условия al oop (то есть `while (! Stream.eof ())`) считается неправильным? , а я не хочу file>> 10 раз.

Я также не изучил такие вещи, как lexical_cast или перегрузка оператора, поэтому любой связанный с ними ответ очень полезен, но может не решить мою проблему.

Вот текстовый файл:

19127519
Jame Howard
0
19124567
Jacky Thomas
1
14527890
Lucas
1

Ответы [ 3 ]

2 голосов
/ 25 апреля 2020

В вашем решении есть несколько проблем

  • вы читаете только std::string, когда хотите int и bool
  • ваш способ чтения std::string рассмотрите любой пробел в качестве разделителя, это означает, что «Jame Howard» не читается в одну строку, а в две разделенные, это не то, что ваш код предполагает при управлении индексом
  • вы (ошибочно) сохраняете только последний триплет и потерял другой

Когда вы открываете файл, всегда проверяйте, что вы можете сделать.

Вы можете сделать это:

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

Строка может содержать пробелы, поэтому невозможно сделать file >> f.b, где f - это foo , getline можно использовать, но поскольку каждый элемент находится в отдельной строке, необходимо выполнить грипп sh конец строки после прочтения 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:~ $ 

Вместо того, чтобы поместить код для чтения foo в main , это более естественно чтобы определить 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 << должно быть объявлено friend в foo

0 голосов
/ 25 апреля 2020

Вы можете использовать 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);
}
0 голосов
/ 25 апреля 2020

Если я правильно понял ваш вопрос, вы хотите сделать что-то вроде следующего ( псевдокод ):

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). Это делает именно то, что вы хотите, и есть много библиотек, которые делают это для вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...