输入输出XML和YAML文件
目的
你将得到以下几个问题的答案:
代码
你可以点击此处下载或直接从OpenCV代码库中找到源文件。samples/cpp/tutorial_code/core/file_input_output/file_input_output.cpp。
以下用简单的示例代码演示如何逐一实现所有目的.
|
|
代码分析
这里我们仅讨论XML和YAML文件输入。你的输出(和相应的输入)文件可能仅具有其中一个扩展名以及对应的文件结构。XML和YAML的串行化分别采用两种不同的数据结构:mappings(就像STL map) 和element sequence(比如 STL vector>。二者之间的区别在map中每个元素都有一个唯一的标识名供用户访问;而在sequences中你必须遍历所有的元素才能找到指定元素。
-
XML\YAML 文件的打开和关闭。在你写入内容到此类文件中前,你必须先打开它,并在结束时关闭它。在OpenCV中标识XML和YAML的数据结构是FileStorage。要将此结构和硬盘上的文件绑定时,可使用其构造函数或者open()函数:
无论以哪种方式绑定,函数中的第二个参数都以常量形式指定你要对文件进行操作的类型,包括:WRITE,READ 或 APPEND。文件扩展名决定了你将采用的输出格式。如果你指定扩展名如.xml.gz,输出甚至可以是压缩文件。
当FileStorage对象被销毁时,文件将自动关闭。当然你也可以显示调用release函数:
-
输入\输出文本和数字。数据结构使用与STL相同的 << 输出操作符。输出任何类型的数据结构时,首先都必须指定其标识符,这通过简单级联输出标识符即可实现。基本类型数据输出必须遵循此规则:
fs 100;读入则通过简单的寻址(通过 [] 操作符)操作和强制转换或 >> 操作符实现:
int itNr; fs["iterationNr"] >> itNr; itNr "iterationNr"]; -
输入\输出OpenCV数据结构。其实和对基本类型的操作方法是相同的:
Mat R <uchar >::eye (T 1); fs // 写 cv::Mat fs << T; fs[// 读 cv::Mat fs[>> T; -
输入\输出 vectors(数组)和相应的maps.之前提到我们也可以输出maps和序列(数组,vector)。同样,首先输出变量的标识符,接下来必须指定输出的是序列还是map。
对于序列,在第一个元素前输出”[“字符,并在最后一个元素后输出”]“字符:
// 文本 - 字符串序列 fs "baboon.jpg"; fs // 序列结束对于maps使用相同的方法,但采用”{“和”}“作为分隔符。
// 文本 - mapping fs 1; fs "}";对于数据读取,可使用FileNode和FileNodeIterator数据结构。FileStorage的[] 操作符将返回一个FileNode数据类型。如果这个节点是序列化的,我们可以使用FileNodeIterator来迭代遍历所有元素。
FileNode n // 读取字符串序列 - 获取节点 ::SEQ) { cerr << endl; 1; } FileNodeIterator it // 遍历节点 ++it) cout << endl;对于maps类型,可以用 [] 操作符访问指定的元素(或者 >> 操作符):
n // 从序列中读取map cout "; "; cout << endl; -
读写自定义数据类型。假设你定义了如下数据类型:
: MyData() id() {} // 数据成员 int A; double X; string id; };添加内部和外部的读写函数,就可以使用OpenCV I/O XML/YAML接口对其进行序列化(就像对OpenCV数据结构进行序列化一样)。内部函数定义如下:
接下来在类的外部定义以下函数:
& x) { x.write(fs); } = MyData()) { if(node.empty()) x = default_value; else x.read(node); }这儿可以看到,如果读取的节点不存在,我们返回默认值。更复杂一些的解决方案是返回一个对象ID为负值的实例。
一旦添加了这四个函数,就可以用 >> 操作符和 << 操作符分别进行读,写操作:
或试着读取不存在的值:
fs[>> m; // 请注意不是 fs << "NonExisting" << m cout << endl;
结果
好的,大多情况下我们只输出定义过的成员。在控制台程序的屏幕上,你将看到:
或YAML文件: