C++解析复杂的文件,其中每行指定一个命令
C++ parsing complicated file where each line specifies a command
所以我需要一些关于如何在C++中很好地解析文本文件的想法。我正在解析的文件具有以下格式:
Command_A list of arguments
Command_B list of arguments
etc etc
现在我正在使用ifstream打开文件,然后我有一系列超长的if-else语句来确定对每种类型的命令做什么。事实证明,这有点笨拙(尤其是因为有些命令是用于解析其他文件的……所以我为不同的文件嵌套了多个ifstream的if-else(。
我一直在寻找另一种方法,但不确定什么是最好的方法。我曾考虑使用std::映射,其中键是命令字符串,值是函数指针,但我不熟悉在映射中存储函数指针(尤其是如果不同的函数具有不同的返回类型等(。
以下基本上就是我目前正在做的事情。我循环浏览该文件并使用"getline"获取当前行。然后我使用字符串流来解析该命令。然后我使用一个很长的if-else列表来确定调用哪个函数。文件中的每一行都有一个参数列表,所以我使用字符串流来解析这些参数,然后将这些参数传递给我调用的函数。
这里的问题是双重
1( 我有一个非常非常大的数量如果elses(大约50(
2( 有些命令要求我解析新文件,因此我必须在当前ifstream中打开另一个ifstream。(参见命令_c(
因此,我正在寻找一种更简单、更高效、更美观的方法来做到这一点。
/*Open file and verify validity*/
std::ifstream parseFile(filename.c_str());
if(!parseFile.good())
{
cerr<<"ERROR: File is either corrupt or does not exist."<<endl;
exit(1); //Terminate program
}
//Loop over file line by line
std::string line;
while(!parseFile.eof())
{
std::getline(parseFile, line);
std::stringstream ss;
std::string command;
ss.str(line);
ss >> command;
if(command == "COMMAND_A")
{
float x,y,z;
ss >> x >> y >> z;
FunctionA(x,y,z);
}
else if(command == "COMMAND_B")
{
float a,b,c,d,e,f;
ss >> a >> b >> c >> d >> e >> f;
FunctionB(a,b,c,d,e,f);
}
else if(command == "Command_C")
{
string nextFile;
ss >> nextFile;
ParseFile(nextFile); //This is not recursive...this is another function
}
else if(...)
{
...
}
// etc, etc (this continues on for a long time)
}
parseFile.close();
您可以有一个命令映射并注册一组函数:
#include<fstream>
#include<functional>
#include<iostream>
#include<map>
#include<sstream>
int main() {
typedef std::function<bool (std::istringstream&)> command_function;
typedef std::map<std::string, command_function> command_map;
command_map map;
// register commands
map.insert(command_map::value_type("print", [](std::istringstream& s) {
std::string line;
if( ! getline(s, line)) return false;
std::cout << line << 'n';
return true;
}));
map.insert(command_map::value_type("add", [](std::istringstream& s) {
double a;
double b;
if( ! (s >> a >> b)) return false;
std::cout << "a + b = " << a + b << 'n';
return true;
}));
// sample data
std::istringstream file(
"print Hello Worldn"
"add 1 2n");
// command parsing
std::string line;
while(getline(file, line)) {
std::istringstream line_stream(line);
std::string command;
if(line_stream >> command >> std::ws) {
auto pos = map.find(command);
if(pos != map.end())
pos->second(line_stream);
}
}
return 0;
}
我已经编写了许多类型的解析器,我发现编写一个相当通用的函数通常是一个好主意,该函数使用一行并生成一个字符串列表(例如std::vector<std::string>
,然后将该列表中的第一个元素处理为"我们下一步要做什么",并让每个命令随心所欲地使用参数(例如,转换为float,用作文件名等(。
然后可以将其与基于表的系统相结合,在该系统中,函数[或对象]与字符串相关联。例如CCD_ 2。
然后你会得到这样的东西:
class CommandA : BaseCommand
{
public:
virtual int Run(const std::vector<std::string>& argv);
};
table["CommandA"] = new CommandA;
table["CommandB"] = new CommandB;
...
std::vector<std::string> argv = parseLine(line);
if (table.find(argv[0]) != table.end())
{
int result = table[argv[0]].second->Run(argv);
if (result < 0)
{
... do error handling here...
}
}
当然,有很多不同的方法可以做到这一点,这只是一个可能的解决方案。
是的,将函数放在映射中。做到这一点的关键是std::function<void()>
。不幸的是,void()
意味着它持有的函数不接受任何参数,也不返回任何值。显然,您的函数有参数。因此,我们所做的是存储每个函数都有一个std::stringstream&
(行(,解析出它们需要的参数,然后调用该函数。最简单的方法就是使用内联lambda。接受字符串流而不返回任何内容的Lambda看起来是这样的:[](std::stringstream& ss) {code}
。
此外,我使用此功能可以轻松检索您的参数:
template<class T>
T get(std::stringstream& ss)
{
T t;
ss<<t;
if (!ss) // if it failed to read
throw std::runtime_error("could not parse parameter");
return t;
}
这是地图:
std::unordered_set<std::string, std::function<void(std::stringstream&))> cmd_map=
"COMMAND_A", [](std::stringstream& ss)
{FunctionA(get<float>(ss), get<float>(ss), get<float>(ss));},
"COMMAND_B", [](std::stringstream& ss)
{FunctionB(get<float>(ss), get<float>(ss), get<float>(ss), get<float>(ss), get<float>(ss), get<float>(ss));},
"COMMAND_C", [](std::stringstream& ss)
{FunctionA(get<string>(ss));},
这里是解析器:
//Loop over file line by line
std::string line;
while(std::getline(parseFile, line)) //use this instead of eof
{
std::stringstream ss(line);
std::string command;
ss >> command;
auto it = cmd_map.find(command);
if (it != cmd_map.end())
{
try
{
(*it)(); //call the function
} catch(std::runtime_error& err) {
std::cout << "ERROR: " << err.what() << 'n';
}
} else {
std::cout << "command " << command << " not found";
}
}
parseFile.close();
- 有没有办法让我编写一个可以在Windows和Linux上运行的命令行游戏?
- 如何通过另一个对象中的命令正确地从一个对象返回数据
- 如何使用 c++ 在命令行中创建一个简单的字符控制器?
- 使用 (cin) 用户输入将其粘贴到 std::system 中,并在另一个终端中运行带有输入的命令
- 编写一个将 LLVM IR 文件作为命令行参数的程序
- 如果在命令行c++中给出了一个文件,那么如何读取该文件
- 有没有办法为 c++ 制作一个 makefile,每次使用 make 命令时都会运行该程序?
- 在一个命令中使用 2 个重载运算符会导致错误
- 从命令行构建一个.sln / .vcxproj项目,并使用Visual C 的免费版本构建
- C++ 仅使用一个命令逐行从终端读取文件流
- 来自另一个 IOS 应用程序的 IOS 应用程序命令
- C++ 将目录复制到另一个目录(Linux cp 命令)
- 使用命令行构建一个Visual Studio C 项目
- 2命令行参数中的数字数字仅被视为一个数字
- 尝试分叉一个过程,第一个命令有效,但第二总是给出错误
- 是否有一个windbg命令来检索转储的module_name
- 如何在 C++ 中使用一个 System() 函数运行 2 个命令
- 使用启动命令启动命令时,将命令传递到另一个 cmd 窗口
- 如何编写一个程序,我可以像 Ubuntu 中的命令一样添加信息(例如:程序 -u "Hello World" )
- 用一个glDrawArrays命令绘制几个不同颜色的三角形