在处理大量XML文件时,经常会遇到文件格式混乱、难以阅读的问题。为了解决这个问题,编写了一个高效的XML格式化工具,它能够快速地将混乱的XML文件整理成易于阅读的格式。这个工具最初是为了解决工作中遇到的一个问题:需要处理大约40MB的XML文件,这些文件全部在一行上,没有换行和缩进,这使得阅读和编辑变得非常困难。虽然Notepad++有一个插件可以完成这项工作,但它处理一个文件需要大约45分钟,而工具只需要5秒钟。
由于只有XML文件用于测试,可能有些情况没有考虑到。如果发现有些XML文件没有正确地换行或缩进,请发送一个样本文件给。
这个小函数接受两个参数:输入流和输出流。它将读取输入流,格式化内容,然后写入输出流。
编写了一个非常基础的Win32 API输入和输出选择区域,用于简单的使用。最初的版本只需要将EXE文件放入包含XML文件的目录中,双击它,它就会处理该目录中的每个XML文件。
虽然界面显然是仅限Windows的,但实际的函数本身应该是平台无关的。
要调用这个函数,请使用以下代码:
C++tidyXML(inputFile, outputFile);
其中inputFile
和outputFile
是ifstream
和ofstream
的引用。
以下是函数本身。有点懒于写注释。如果不明白为什么某个部分是这样实现的,或者只是想让在特定地方添加注释,请在下面的评论中告诉,会尝试添加更多。但代码应该是相当直观的。
C++void tidyXML(ifstream &input, ofstream &output) {
char currChar;
char nextChar;
int indent = -1;
string currKeyStore = "";
string lastKeyStore = "";
string valueOrJunkStore = "";
bool inKey = false;
bool skipNextIndent = false;
enum keyType {
unset,
infoLine,
entryKey,
exitKey,
emptyValue,
};
keyType lastKeyType = unset, currKeyType = unset;
while (true) {
currChar = input.get();
if (!input.good()) {
output << currKeyStore;
break;
}
// 看看接下来是什么
nextChar = input.peek();
if (!input.good())
nextChar = '\0';
if (currChar == '<') {
inKey = true;
lastKeyType = currKeyType;
lastKeyStore = currKeyStore;
currKeyType = unset;
currKeyStore = "";
// 如果不能在这里确定,需要等到nextChar是'>“才能决定
if (nextChar == '/')
currKeyType = exitKey;
else if (nextChar == '?' || nextChar == '!')
currKeyType = infoLine;
}
if (currKeyType == unset && nextChar == '>') {
// 现在可以确定这是什么了 :)
if (currChar == '/')
currKeyType = emptyValue;
else
currKeyType = entryKey;
}
if (inKey)
currKeyStore += currChar;
else
valueOrJunkStore += currChar;
if (currChar == '>') {
inKey = false;
if (!skipNextIndent)
for (int i = 0; i < indent; ++i)
output << '\t';
output << lastKeyStore;
skipNextIndent = false;
if (lastKeyType == entryKey && currKeyType == exitKey) {
// 值行
skipNextIndent = true;
output << valueOrJunkStore;
} else if (lastKeyType != unset) {
// 所以不要在文件开头添加行
output << endl;
}
valueOrJunkStore = "";
if (lastKeyType == exitKey || lastKeyType == emptyValue)
--indent;
if (currKeyType == entryKey || currKeyType == emptyValue)
++indent;
}
}
}
使用MinGW编译了带有简单Win32界面的示例版本。使用以下命令:
mingw32-g++ --std=c++0x -Wall -fno-builtin -O3 -static *.cpp -lcomdlg32 -o tidyxml.exe
已经静态链接了它,所以它应该可以立即运行,而不需要依赖地狱。
还提供了一个双击构建的build.bat
文件。它也会去掉调试符号。
编写这个的主要痛苦在于,必须总是读取下一个标签,然后才知道如何处理当前的标签。或者更好的说法是,保存当前标签,读取下一个标签,这样就可以决定如何处理保存的标签。
最初的版本充满了布尔值来规划和跟踪所有事情。这些被枚举替换了,使代码更干净、更容易理解。至少是这么认为的。