我有一个程序,希望从文本文件中加载变量以将其用作默认变量。
文本文件应如下所示:
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();
答案 0 :(得分:1)
有没有更简单的方法?
您可以使用@JesperJuhl建议的cereal或Boost之类的序列化库。
但是,我强烈建议您退后一步,并回顾您的方法。您正在寻求改进,但此时您还没有一个好的解决方案,因为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可以帮助一般学习。