c++ - 从输入流中读取行并能够跳过块
问题描述
我有一个带有一系列类似字节码的指令的输入流
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
当您将其设置为“不需要的”功能时,最终也会稍微奇怪地工作,然后执行 like 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
等)在其他地方被重构,那么它会更简单一些。
您可能会进行实际的解析(获取像“function”这样的命令名称,以及像“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
等类型构造。
推荐阅读
- python - 使用 eval(str()) 而不是 deepcopy() 对列表列表进行深度复制是一种不好的做法吗?
- kubernetes - Kubernetes 入口控制器 - 错误:ImagePullBackOff
- android - RxJava take(1) 完成而 Observable.just() 没有完成
- git - 涂抹错误:涂抹过滤器 LFS 在 Git 拉取期间失败
- linq-to-sql - EF Core - 包括多个子表
- c - 使用 ANSI C 在 ESP32 中运行子进程的管道
- swift - 多个枚举类型列出所有案例
- android - 无法在android中膨胀导航视图
- javascript - 如何使用Javascript在给定字符串中第n次出现一组子字符串?
- php - 使用 Sage 将 if 语句添加到 WP 查询