从输入流中读取具有跳过块功能的行

问题描述 投票:0回答:1

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

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

奖金:如果有一种更好的方法来读取行中的前两个单词,我会想为他们创建一个新的流。

编辑:函数始终是顺序的。没有嵌套函数。即“功能”总是紧接在“返回”之后。

c++ stream
1个回答
0
投票

如果是文本,由于没有已知的偏移量,所以您不容易读取/跳过(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 ifelse等类型,则构造。

© www.soinside.com 2019 - 2024. All rights reserved.