我的输入流带有一系列类似字节码的指令
function foo
push x
pop y
...
return
function bar
...
return
function other
...
即一系列背靠背的函数声明。从一个“功能”到下一个定义“功能”。一个函数中可能有多个“返回”,所以我不能将其用作分隔符。所有指令都必须在函数内部(即,流的第一行始终是“函数”,而最后一行始终是“返回”)。
我想从列表中基本上删除某些功能。我有要保留的功能列表,我考虑过复制到输出流,跳过列表中未列出的任何功能,例如
vector<string> wanted_functions = { "foo", "other" }
ostringstream oss;
bool skip = false;
for (string line; getline(input_stream, line);) {
istringstream iss(line);
string command;
iss >> command;
if (command == "function") {
skip = false;
string function_name;
iss >> function_name;
if (std::find(wanted_function.begin(), wanted_functions.end(), function_name)
== wanted_functions.end()) {
skip = true;
}
if (!skip) oss << line;
}
我尚未测试上述解决方案;看起来可能可行,但我认为它不是很优雅。我觉得流迭代器在这里会很好,但我不知道如何使用它们。如何使用迭代器或本地流方法(例如ignore()或seekg())实现跳过行为?
奖金:如果有一种更好的方法来读取行中的前两个单词,我会想为他们创建一个新的流。
编辑:函数始终是顺序的。没有嵌套函数。即“功能”总是紧接在“返回”之后。
如果是文本,由于没有已知的偏移量,所以您不容易读取/跳过(seekg
)就不容易了(很多二进制文件格式将包含此类信息),但是您可以只需过滤您阅读的内容,问题中的代码几乎可以做到这一点。
istream_iterator<std:string>
将为您提供每个单词/以空格分隔的字符,但您无法确定新行在哪里。您可以使istream_iterator
改为读取行,但是最简单的方法包括对std::string
进行子类化以重新定义operator >>
,但这基本上还是getline
所能得到的,或者您可以使自己的类型包含更有用的信息(如下)。
您可能会使用std::unordered_set<std::string> wanted_functions
,因为这比检查std::vector
(使用std::find
或类似名称)更容易检查是否存在项目。将skip
设置为“不需要的”功能时,其效果也会有些怪异,然后像if (!unwanted)
一样工作。
unordered_set<string> wanted_functions = { "foo", "other" };
bool is_wanted_function = false;
for (string line; getline(input_stream, line);) {
istringstream iss(line);
string command;
iss >> command;
if (command == "function") {
string function_name;
iss >> function_name;
is_wanted_function = wanted_functions.count(function_name) != 0;
}
if (is_wanted_function) {
oss << line << std::endl;
}
}
is_wanted_function
标志的替代方法是使用if (command == "function") {
中的函数,这需要更仔细的管理以读取下一行,以免意外跳过内循环之后的那一行]
unordered_set<string> wanted_functions = { "foo", "other" };
string line;
getline(input_stream, line);
while (input_stream) {
istringstream iss(line);
string command;
iss >> command;
if (command == "function") {
string function_name;
iss >> function_name;
if (wanted_functions.count(function_name)) {
oss << line << std::endl;
while (getline(input_stream, line) && line.rfind("function", 0) != 0) {
oss << line << std::endl;
}
continue; // already have a line
}
}
getline(input_stream, line); // next line
}
因为我不认为这有多大改进,但是如果将实际的解析(iss >> command;
,iss >> function_name
等)重构为其他地方,那么会稍微简单一些。
[您可能会进行实际的解析(获得命令名称(如“函数”,而参数则获得诸如“ foo”),这是它自己的类,可以使istringstream iss(line); iss >> command;
等直接包含在此代码中,使它们整理。
istream_iterator
基本上只使用operator >>
来获取下一项,直到流处于失败状态为止,因此可以与您自己的类型一起使用,尽管您可以在没有istream_iterator
的情况下自己进行大致相同的操作。
class command
{
public:
const std::string &cmd()const { return _cmd; }
const std::string &source_line()const { return _source_line; }
const std::string &arg(size_t i)const
{
if (i < _args.size()) return _args[i];
else throw std::out_of_range("Command does not have this many arguments.");
}
friend std::istream &operator >> (std::istream &is, command &cmd)
{
if (std::getline(is, cmd._source_line))
{
std::stringstream ss(cmd._source_line);
ss >> cmd._cmd;
cmd._args.clear(); // istream_iterator uses the same command object every time
while (true)
{
std::string val;
ss >> val;
if (!ss) break;
cmd._args.push_back(std::move(val));
}
}
return is;
}
private:
std::string _source_line;
std::string _cmd;
std::vector<std::string> _args;
};
int main()
{
using namespace std;
std::stringstream input_stream(
"function foo\n"
"push x\n"
"pop y\n"
"...\n"
"return\n"
"function bar\n"
"...\n"
"return\n"
"function other\n"
"...\n"
"return\n");
std::ostream &oss = std::cout;
std::unordered_set<string> wanted_functions = { "foo", "other" };
std::istream_iterator<command> eos; // end of stream
std::istream_iterator<command> it(input_stream); // iterator
while (it != eos)
{
if (it->cmd() == "function" && wanted_functions.count(it->arg(0)))
{
do
{
oss << it->source_line() << std::endl;
} while (++it != eos && it->cmd() != "function");
}
else ++it; // on true the while loop already advanced
}
}
istream_iterator
当然也带来了与其他基于迭代器的算法和构造函数(std::find
等)的兼容性,并且您可以从中构建一些更复杂的东西。例如,如果您在此之上添加另一层以创建istream_iterator<function>
,则可以使用Boost C ++ filter_iterator
,然后您将拥有一个仅包含所需功能的迭代器。
请注意,如果您需要开始处理任何嵌套构造(例如if (...) { ... } else if (...) { ... }
),则可能会发现解析成树形结构比执行平坦序列更方便。请参见抽象语法树。这在某种程度上取决于您的语法,例如如果仅使用goto if
偏移量/标签而不是while(expr)
,if(expr)
,else if
,else
等类型,则构造。