从文本文件加载变量的最简单方法

时间:2019-04-02 16:04:10

标签: c++ fstream

我有一个程序,希望从文本文件中加载变量以将其用作默认变量。

文本文件应如下所示:

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 个答案:

答案 0 :(得分:1)

  

有没有更简单的方法?

您可以使用@JesperJuhl建议的cerealBoost之类的序列化库。

但是,我强烈建议您退后一步,并回顾您的方法。您正在寻求改进,但此时您还没有一个好的解决方案,因为Why is iostream::eof inside a loop condition considered wrong?

正如我写过here一样,我将使用std::getline()作为循环条件,而不是ios::eof(),以便逐行解析文件。

  

在带有问号的地方该怎么办?

然后,对于每一行,我将基于定界符(在您的情况下为等号)对它进行标记化,以提取两个标记,即变量名及其默认值。在Parse (split) a string in C++ using string delimiter (standard 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

答案 1 :(得分:0)

如果您需要自己编写而不是使用现成的库,则可以使用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";
    }
}

答案 2 :(得分:0)

我试图简化@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";
    }

};

答案 3 :(得分:-1)

我不会重新发明这个。如建议的那样,存在用于序列化的库。以Boost.PropertyTree为例,Boost可以帮助一般学习。