如何在 yaml 文件中发出或写入地图的地图? C++
How to emit or write a map of maps in yaml file? c++
所以我有几个虚拟护目镜..每个都有不同的校准参数。我决定将这些参数保存到一个 yaml 文件中(作为配置文件).. 每个护目镜都有自己的 serial/identification 编号 ... 根据这个编号,我 select 使用哪一个。
如果眼镜没有预存信息。我校准它并将这些参数添加到文件中
所以现在我正在尝试写入如下所示的 yaml 文件:
Headset:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
GyroOffset:
GyroX:
Value: -0.013776619
GyroY:
Value: -0.016475508
GyroZ:
Value: -0.0114268782
这就是我实际得到的:
Headset2:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
? GyroOffset:
GyroX:
Value: -0.013776619
? GyroY:
Value: -0.016475508
: GyroZ:
Value: -0.0114268782
我不明白我做错了什么! .. 这是我写入 yaml 文件的函数:
void ParseInputDeviceYaml::addCalibrationToConfigFile(const char* identificationNumber, const float* in)
{
try {
std::ofstream updatedFile;
updatedFile.open(m_filename.toStdString(), std::ios::app);
std::map<std::string, std::string> IDNumber;
std::map<std::string, std::map<std::string, float>> gyroXOffset;
std::map<std::string, std::map<std::string, float>> gyroYOffset;
std::map<std::string, std::map<std::string, float>> gyroZOffset;
IDNumber["IdentificationNumber"] = identificationNumber;
gyroXOffset["GyroX"]["Value"] = *in;
gyroYOffset["GyroY"]["Value"] = *(in + 1);
gyroZOffset["GyroZ"]["Value"] = *(in + 2);
YAML::Emitter newNode;
newNode << YAML::BeginMap;
newNode << YAML::Key << "Headset2";
newNode << YAML::Value << YAML::BeginMap << YAML::Key << "IdentificationNumber" << YAML::Value << identificationNumber << YAML::EndMap;
newNode << YAML::BeginMap << YAML::Key << "GyroOffset" << YAML::Value << gyroXOffset << gyroYOffset << gyroZOffset << YAML::EndMap;
newNode << YAML::EndMap;
updatedFile << newNode.c_str() << "\n";
updatedFile.close();
} catch (std::exception& e) {
LOG4CPLUS_FATAL(m_logger, e.what());
throw std::runtime_error(QObject::tr("Writing gyroscope offsets ").toStdString());
}
}
主要问题似乎是您建立了很多错误信息。我会尝试清除一些东西:
- 是否使用序列与能否修改现有值或向文件添加新值完全无关。问题是您使用
std::ios::app
附加到文件,这将始终创建一个新条目。相反,您应该将文件加载到 YAML 节点,修改该节点的内容,然后写回整个节点。
- 没有你给出的序列的 YAML 文件肯定不会做你认为的事情,因为你将
? GyroOffset
放在与 Headset2:
相同的深度,使其成为 [=18= 的同级文件].另请注意,在同一映射中混合隐式 (foo:
) 和显式 (? foo
) 键是一种极端情况,可能会混淆某些实现。 YAML 文件可能看起来像这样:
Headset2:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
GyroOffset:
GyroX:
Value: -0.012388126
GyroY:
Value: -0.0155748781
GyroZ:
Value: -0.0115196211
为了使您的代码更具可读性,我建议使用助手 classes 来访问您的值。假设上面的代码是整个 YAML 文件,它可能看起来像这样:
struct Value {
YAML::Node data;
// access existing node
explicit Value(YAML::Node data): data(data) {
assert(data.IsMapping());
}
// create new node
explicit Value(float value) {
data["Value"] = value;
}
float get() { return data["Value"].as<float>(); }
void set(float value) { data["Value"] = value; }
};
struct GyroOffset {
YAML::Node data;
explicit GyroOffset(YAML::Node data): data(data) {
assert(data.IsMapping());
}
GyroOffset(float x, float y, float z) {
data["GyroX"] = Value(x).data;
data["GyroY"] = Value(y).data;
data["GyroZ"] = Value(z).data;
}
Value gyroX() { return Value(data["GyroX"]); }
Value gyroX() { return Value(data["GyroY"]); }
Value gyroZ() { return Value(data["GyroZ"]); }
};
struct Headset {
YAML::Node data;
Headset(YAML::Node data): data(data) {
assert(data.IsMapping());
}
Headset(const char *id) {
data["IdentificationNumber"] = id;
// initialize with zero values
data["GyroOffset"] = GyroOffset(0, 0, 0).data;
}
std::string id() { return data["IdentificationNumber"].as<std::string>(); }
void setId(const char *value) { data["IdentificationNumber"] = value; }
GyroOffset gyroOffset() { return GyroOffset(data["GyroOffset"]); }
}
现在,找到给定标识号的 GyroOffset 看起来像这样(我展示了一个简单的函数,因为我不知道你的 class' 字段,因为你没有展示它们):
// write found values to output of found
bool findHedasetGyroOffset(Yaml::Node &input /* the file as shown above */, const char *id, GyroOffset &output) {
for (auto it = input.begin(); it != input.end(); ++it) {
Headset hs(it->second);
if (hs.id() == id) {
output = hs.gyroOffset();
return true;
}
}
return false;
}
由于 YAML::Node
基本上是一个引用,当您更改返回的 GyroOffset
中的值时,原始数据会发生变化。然后您可以将根节点写回到文件中(而不是追加它)并有一个更新的文件。
添加新耳机如下所示:
void addCalibrationToConfigFile(Yaml::Node &file, const char* identificationNumber, const float* in) {
Headset newHs(identificationNumber);
auto go = newHs.gyroOffset();
go.gyroX().set(*in);
go.gyroY().set(*(in + 1));
go.gyroZ().set(*(in + 2));
// note that this will overwrite an existing Headset2
file["Headset2"] = newHs.data;
}
虽然我尝试遵循您显示的结构,但我感觉映射中的实际键不应该是 Headset2
,而是 IdentificationNumber:
b630cc42-9a03-42da-a039-0e023cf5b090:
Name: Headset2
GyroOffset:
GyroX:
Value: -0.012388126
GyroY:
Value: -0.0155748781
GyroZ:
Value: -0.0115196211
由于您是根据 ID 进行查找的,所以这会更有意义。此外,创建一个新的配置实际上是有效的(目前,由于硬编码的 "Headset2"
值,它总是会覆盖该耳机(如果存在)。
当心,我写的代码只是为了演示,并没有测试它;可能有错误。
所以我有几个虚拟护目镜..每个都有不同的校准参数。我决定将这些参数保存到一个 yaml 文件中(作为配置文件).. 每个护目镜都有自己的 serial/identification 编号 ... 根据这个编号,我 select 使用哪一个。 如果眼镜没有预存信息。我校准它并将这些参数添加到文件中
所以现在我正在尝试写入如下所示的 yaml 文件:
Headset:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
GyroOffset:
GyroX:
Value: -0.013776619
GyroY:
Value: -0.016475508
GyroZ:
Value: -0.0114268782
这就是我实际得到的:
Headset2:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
? GyroOffset:
GyroX:
Value: -0.013776619
? GyroY:
Value: -0.016475508
: GyroZ:
Value: -0.0114268782
我不明白我做错了什么! .. 这是我写入 yaml 文件的函数:
void ParseInputDeviceYaml::addCalibrationToConfigFile(const char* identificationNumber, const float* in)
{
try {
std::ofstream updatedFile;
updatedFile.open(m_filename.toStdString(), std::ios::app);
std::map<std::string, std::string> IDNumber;
std::map<std::string, std::map<std::string, float>> gyroXOffset;
std::map<std::string, std::map<std::string, float>> gyroYOffset;
std::map<std::string, std::map<std::string, float>> gyroZOffset;
IDNumber["IdentificationNumber"] = identificationNumber;
gyroXOffset["GyroX"]["Value"] = *in;
gyroYOffset["GyroY"]["Value"] = *(in + 1);
gyroZOffset["GyroZ"]["Value"] = *(in + 2);
YAML::Emitter newNode;
newNode << YAML::BeginMap;
newNode << YAML::Key << "Headset2";
newNode << YAML::Value << YAML::BeginMap << YAML::Key << "IdentificationNumber" << YAML::Value << identificationNumber << YAML::EndMap;
newNode << YAML::BeginMap << YAML::Key << "GyroOffset" << YAML::Value << gyroXOffset << gyroYOffset << gyroZOffset << YAML::EndMap;
newNode << YAML::EndMap;
updatedFile << newNode.c_str() << "\n";
updatedFile.close();
} catch (std::exception& e) {
LOG4CPLUS_FATAL(m_logger, e.what());
throw std::runtime_error(QObject::tr("Writing gyroscope offsets ").toStdString());
}
}
主要问题似乎是您建立了很多错误信息。我会尝试清除一些东西:
- 是否使用序列与能否修改现有值或向文件添加新值完全无关。问题是您使用
std::ios::app
附加到文件,这将始终创建一个新条目。相反,您应该将文件加载到 YAML 节点,修改该节点的内容,然后写回整个节点。 - 没有你给出的序列的 YAML 文件肯定不会做你认为的事情,因为你将
? GyroOffset
放在与Headset2:
相同的深度,使其成为 [=18= 的同级文件].另请注意,在同一映射中混合隐式 (foo:
) 和显式 (? foo
) 键是一种极端情况,可能会混淆某些实现。 YAML 文件可能看起来像这样:
Headset2:
IdentificationNumber: b630cc42-9a03-42da-a039-0e023cf5b090
GyroOffset:
GyroX:
Value: -0.012388126
GyroY:
Value: -0.0155748781
GyroZ:
Value: -0.0115196211
为了使您的代码更具可读性,我建议使用助手 classes 来访问您的值。假设上面的代码是整个 YAML 文件,它可能看起来像这样:
struct Value {
YAML::Node data;
// access existing node
explicit Value(YAML::Node data): data(data) {
assert(data.IsMapping());
}
// create new node
explicit Value(float value) {
data["Value"] = value;
}
float get() { return data["Value"].as<float>(); }
void set(float value) { data["Value"] = value; }
};
struct GyroOffset {
YAML::Node data;
explicit GyroOffset(YAML::Node data): data(data) {
assert(data.IsMapping());
}
GyroOffset(float x, float y, float z) {
data["GyroX"] = Value(x).data;
data["GyroY"] = Value(y).data;
data["GyroZ"] = Value(z).data;
}
Value gyroX() { return Value(data["GyroX"]); }
Value gyroX() { return Value(data["GyroY"]); }
Value gyroZ() { return Value(data["GyroZ"]); }
};
struct Headset {
YAML::Node data;
Headset(YAML::Node data): data(data) {
assert(data.IsMapping());
}
Headset(const char *id) {
data["IdentificationNumber"] = id;
// initialize with zero values
data["GyroOffset"] = GyroOffset(0, 0, 0).data;
}
std::string id() { return data["IdentificationNumber"].as<std::string>(); }
void setId(const char *value) { data["IdentificationNumber"] = value; }
GyroOffset gyroOffset() { return GyroOffset(data["GyroOffset"]); }
}
现在,找到给定标识号的 GyroOffset 看起来像这样(我展示了一个简单的函数,因为我不知道你的 class' 字段,因为你没有展示它们):
// write found values to output of found
bool findHedasetGyroOffset(Yaml::Node &input /* the file as shown above */, const char *id, GyroOffset &output) {
for (auto it = input.begin(); it != input.end(); ++it) {
Headset hs(it->second);
if (hs.id() == id) {
output = hs.gyroOffset();
return true;
}
}
return false;
}
由于 YAML::Node
基本上是一个引用,当您更改返回的 GyroOffset
中的值时,原始数据会发生变化。然后您可以将根节点写回到文件中(而不是追加它)并有一个更新的文件。
添加新耳机如下所示:
void addCalibrationToConfigFile(Yaml::Node &file, const char* identificationNumber, const float* in) {
Headset newHs(identificationNumber);
auto go = newHs.gyroOffset();
go.gyroX().set(*in);
go.gyroY().set(*(in + 1));
go.gyroZ().set(*(in + 2));
// note that this will overwrite an existing Headset2
file["Headset2"] = newHs.data;
}
虽然我尝试遵循您显示的结构,但我感觉映射中的实际键不应该是 Headset2
,而是 IdentificationNumber:
b630cc42-9a03-42da-a039-0e023cf5b090:
Name: Headset2
GyroOffset:
GyroX:
Value: -0.012388126
GyroY:
Value: -0.0155748781
GyroZ:
Value: -0.0115196211
由于您是根据 ID 进行查找的,所以这会更有意义。此外,创建一个新的配置实际上是有效的(目前,由于硬编码的 "Headset2"
值,它总是会覆盖该耳机(如果存在)。
当心,我写的代码只是为了演示,并没有测试它;可能有错误。