c++ - 从输入流中读取具有跳过 block 能力的行

标签 c++ stream

我有一个带有一系列类似字节码指令的输入流

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())实现跳过行为?

奖励:如果有更好的方法来阅读为他们创建新流的行中的前两个词,我也很想知道。

编辑:函数总是顺序的。没有嵌套函数。 IE。 “function”总是紧跟在“return”之前。

最佳答案

如果它是文本,你不能轻易地跳转/跳过(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 等)在别处重构,那么它会更简单一些。


您可能会进行实际的解析(获取像“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等类型结构。

关于c++ - 从输入流中读取具有跳过 block 能力的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59269869/

相关文章:

c++ - SQLite 返回 NULL 行

http - 使用 Apache2 流式传输内容形式管道

c# - 无法通过 TCP/Sockets .net 退出具有多个缓冲区传输的循环

c++ - 尽管设置了 EOF 标志,为什么 std::ws 会阻塞?

c++ - Stack Overflow 深度优先搜索

c++ - 如何计算以下伪代码的封闭形式?

c++ - 使当前套接字服务器设计适应 SSL(C++、GSK、spawnp())

c++ - 在 Qt 中解析元数据时检测方法的来源

node.js - NodeJs : slow req. 管道

java - Java中的内存流