Самый простой способ загрузить переменные из текстового файла - PullRequest
0 голосов
/ 02 апреля 2019

У меня есть программа, в которую я хочу загрузить переменные из текстового файла, чтобы использовать их в качестве переменных по умолчанию.

Текстовый файл должен выглядеть следующим образом:

Name=No Name

Age=8

Gender=male

etc.

Есть либолее простой способ, а если нет, то как мне сделать это в месте с вопросительными знаками?

Мой код выглядит следующим образом:

int Age;
std::string Name;
bool male;

if(f.is_open())
{
    while (!f.eof())
    {

        getline(f, line);
        if (line.find("Name=") == std::string::npos)
        {
            Name=?????;
            continue;
        }
        else if (line.find("Gender=") == std::string::npos)
        {
            if(????? == "true"); then
               male=true;
            else
               male=false;

            continue;
        }
        else if (line.find("Age=") == std::string::npos)
        {
            Age=?????;
            continue;
        }
        //etc. ...
}
f.close();

Ответы [ 4 ]

1 голос
/ 02 апреля 2019

Есть ли более простой способ?

Вы можете использовать библиотеку сериализации, такую ​​как cereal или Boost , как предложил @JesperJuhl.

Однако я настоятельно рекомендую сделать шаг назад и пересмотреть ваш подход. Вы просите об улучшении, но у вас пока нет хорошего решения, потому что Почему iostream :: eof внутри условия цикла считается неправильным?

Как я уже писал здесь , я буду использовать std::getline() в качестве условия цикла вместо ios::eof(), чтобы анализировать файл строка за строкой.

Как мне это сделать в месте с вопросительными знаками?

Затем для каждой строки я ее токенизирую на основе разделителя (знак равенства в вашем случае), чтобы извлечь два токена, имя переменной и ее значение по умолчанию. Подробнее об этом читайте в Разбор (разбиение) строки в C ++ с использованием разделителя строк (стандарт C ++)

После этого я бы использовал подход if-else (вместо него можно использовать оператор switch), чтобы проверить имя переменной и присвоить ее значение по умолчанию фактическим переменным программы.

Пример полного кода:

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

int main(void) {
  std::string defaultName, gender;
  int age;

  std::ifstream infile("mytextfile.txt");
  std::string line, varName, defaultValue;
  std::string delimiter = "=";
  while (std::getline(infile, line)) {
    varName = line.substr(0, line.find(delimiter));
    defaultValue = line.substr(line.find(delimiter) + 1);
    if(varName == "Name") {
      defaultName = defaultValue;
      continue;
    } else if(varName == "Age") {
      age = std::stoi(defaultValue);
      continue;
    } else if(varName == "Gender") {
      gender = defaultValue;
      continue;
    } else {
      std::cout << "Unknown entry: " << line << std::endl;
    }
  }

  std::cout << defaultName << ", " << age << ", " << gender << std::endl;

  return 0;
}

Выход:

No Name, 8, male
0 голосов
/ 10 мая 2019

Я попытался упростить решение @Ted Lyngmo: ... Я думаю, что это не самый быстрый и не самый лучший способ, но он более простой и короткий:

#include <sstream>

class loadVars
{
public:
    std::string file;
    loadVars() { }

    //Input ->
    loadVars(std::string Text) {
        this->setFile(Text);
    }

    loadVars(std::istream& is) {
        this->setFile(is);
    }

    friend void operator>>(std::istream& is, loadVars& lv) {
        lv.file = std::string((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
    }

    void setFile(std::string Text) {
        this->file = Text;
    }

    void setFile(std::istream& is) {
        this->file = std::string((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
    }
    //<-

    std::string extract_to_first(std::string to) {
        std::string line;
        std::stringstream s_string = std::stringstream(this->file);

        while (std::getline(s_string, line)) {
            if(line.find("=") != std::string::npos) {
                if(line.substr(0,line.find("=")) == to) {
                    return line.substr(line.find("=")+1);
                }
            }
        }
        return "-1";
    }

};
0 голосов
/ 02 апреля 2019

Если вы чувствуете необходимость написать это самостоятельно, а не использовать готовую библиотеку, вы можете использовать std::unordered_map<> и добавить поддержку потоковой передачи и извлечения. Вот пример с комментариями в коде:

#include <string>
#include <unordered_map>

class KeyValue { //        Key          Value    
    std::unordered_map<std::string, std::string> m_kv{};

public:
    // at() is used to get a reference to a Value given the supplied Key. It uses
    // the function with the same name in the unordered_map.

    inline std::string& at(const std::string& Key) { return m_kv.at(Key); }
    inline const std::string& at(const std::string& Key) const { return m_kv.at(Key); }

    // The "as<T>" function below is used to extract values from the map.
    // The exact version of the function that will be used depends on the type
    // you want to extract from the string. Explicit specializations of the function
    // are declared outside the class.

    // A generic conversion function to anything that can be constructed from a std::string
    template<typename T>
    T as(const std::string& Key) const {
        return at(Key);
    }

    // A function to extract directly into a variable using the proper as<T>
    template<typename T>
    void extract_to(T& var, const std::string& Key) const {
        var = as<T>(Key);
    }

    // A friend function to read from an input stream (like an open file) and
    // populate the unordered_map.
    friend std::istream& operator>>(std::istream&, KeyValue&);
};

// Explicit specializations of KeyValue::as<T>()

// floats
template<>
float KeyValue::as(const std::string& Key) const {
    return std::stof(at(Key));
}

template<>
double KeyValue::as(const std::string& Key) const {
    return std::stod(at(Key));
}

template<>
long double KeyValue::as(const std::string& Key) const {
    return std::stold(at(Key));
}
// signed integers
template<>
int KeyValue::as(const std::string& Key) const {
    return std::stoi(at(Key));
}

template<>
long KeyValue::as(const std::string& Key) const {
    return std::stol(at(Key));
}

template<>
long long KeyValue::as(const std::string& Key) const {
    return std::stoll(at(Key));
}
// unsigned integers
template<>
unsigned KeyValue::as(const std::string& Key) const {
    return std::stoul(at(Key));
}

template<>
unsigned long KeyValue::as(const std::string& Key) const {
    return std::stoul(at(Key));
}

template<>
unsigned long long KeyValue::as(const std::string& Key) const {
    return std::stoull(at(Key));
}
// bool
template<>
bool KeyValue::as(const std::string& Key) const {
    const std::string& val = at(Key);
    if(val=="true" || val=="1") return true;
    else if(val=="false" || val=="0") return false;
    throw std::range_error("\"" + Key + "\" is neither true nor false");
}   

// the friend function that extracts key value strings from a stream
std::istream& operator>>(std::istream& is, KeyValue& kv) {
    std::string line;

    // read one line at a time
    while(std::getline(is, line)) {
        auto pos = line.find('=');
        if(pos == std::string::npos || pos == 0) {
            // if '=' was not found (or found at pos 0), set the failbit on the stream
            is.setstate(std::ios::failbit);
        } else {
            // if '=' was found, put the Key and Value in the map by
            // using substr() to split the line where the '=' was found
            kv.m_kv.emplace(line.substr(0, pos), line.substr(pos + 1));
        }
    }
    return is;
}

Имея это в виду, вы можете прочитать файл и заполнить переменные, которые вы предпочтительно поместили в class / struct. Пример:

#include <fstream>

struct Variables {
    std::string Name{};
    unsigned int Age{};
    std::string Gender{};
    double PI{};
    bool Hungry{};
    bool Sad{};

    Variables(const std::string& filename) {
        std::ifstream is(filename);
        if(is) {
            KeyValue tmp;
            is >> tmp; // stream the whole file into tmp

            // extract values
            tmp.extract_to(Name, "Name");
            tmp.extract_to(Age, "Age");
            tmp.extract_to(Gender, "Gender");
            tmp.extract_to(PI, "PI");
            tmp.extract_to(Hungry, "Hungry");
            tmp.extract_to(Sad, "Sad");
        } else throw std::runtime_error("Could not read \""+filename+"\".");
    }
};

Пример файла данных (vars.dat):

Name=No name
Age=8
Gender=male
PI=3.14159
Hungry=true
Sad=false

... и основной пример ::

#include <iostream>

int main() {
    try {
        Variables var("vars.dat"); // open file and populate variables

        std::cout << std::boolalpha
            << "Name:   " << var.Name << "\n"
            << "Age:    " << var.Age << "\n"
            << "Gender: " << var.Gender << "\n"
            << "PI:     " << var.PI << "\n"
            << "Hungry: " << var.Hungry << "\n"
            << "Sad:    " << var.Sad << "\n";

    } catch(const std::exception& ex) {
        std::cerr << ex.what() << "\n";
    }
}
0 голосов
/ 02 апреля 2019

Я бы не стал изобретать это.Как и предполагалось, библиотеки для сериализации существуют.Рассмотрим Boost.PropertyTree в качестве примера, и Boost может быть полезным для изучения в целом.

...