0

我一直在解析文件和使用 seekg() 时遇到问题。每当在文件中达到某个字符时,我想循环直到满足条件。循环在第一次迭代时工作正常,但是当它循环返回时,文件似乎跳过了一个字符并导致循环无法按预期运行。

具体来说,如果循环全部包含在文件的一行中,则循环可以正常工作,但当文件中的循环中至少有一个换行符时,循环会失败。

我应该提到我正在 Windows 上处理这个问题,我觉得问题出在 Windows 如何以\r\n.

使用seekg(-2, std::ios::cur)after looping 解决了当开始循环条件后紧跟换行符时的问题,但不适用于同一行中包含的循环。

代码的结构是让解释器类保存文件指针和相关变量,例如当前行和列。此类还具有如下定义的功能映射:

// Define function type for command map
    typedef void (Interpreter::*function)(void);

    // Map for all the commands
    std::map<char, function> command_map = {
        {'+', increment_cell},
        {'-', decrement_cell},
        {'>', increment_ptr},
        {'<', decrement_ptr},
        {'.', output},
        {',', input},
        {'[', begin_loop},
        {']', end_loop},
        {' ', next_col},
        {'\n', next_line}
    };

它遍历每个字符,在以下函数中确定它是否具有功能:

// Iterating through the file
void Interpreter::run() {
    char current_char;
    if(!this->file.eof() && this->file.good()) {
        
        while(this->file.get(current_char)) {

            // Make sure character is functional command (ie not a comment)
            if(this->command_map.find(current_char) != this->command_map.end()) {

                // Print the current command if in debug mode
                if(this->debug_mode && current_char != ' ' && current_char != '\n') {
                    std::cout << this->filename << ":" << this->line << ":" 
                              << this->column << ": " << current_char << std::endl;
                }

                // Execute the command
                (this->*(command_map[current_char]))();
            }

            // If it is not a functional command, it is a comment. The rest of the line is ignored
            else{
                std::string temp_line = "";
                std::getline(file, temp_line);
                this->line++;
                this->column = 0;
            }
            this->temp_pos = file.tellg();
            this->column++;
        }
    }

    else {
        std::cout << "Unable to find file " << this->filename << "." << std::endl;
        exit(1);
    }

    file.close();
}

循环的开始(由 '[' 字符表示)将开始循环位置设置为this->temp_pos

void Interpreter::begin_loop() {
    this->loop_begin_pointer = this->temp_pos;
    this->loop_begin_line = this->line;
    this->loop_begin_col = this->column;
    this->run();
}

当到达循环结束时(由 ']' 字符表示),如果不满足结束循环的条件,则将文件光标位置设置回循环的开头:

void Interpreter::end_loop() {
    // If the cell's value is 0, we can end the loop
    if(this->char_array[this->char_ptr] == 0) {
        this->loop_begin_pointer = -1;
    }
    // Otherwise, go back to the beginning of the loop
    if(this->loop_begin_pointer > -1){
        this->file.seekg(this->loop_begin_pointer, std::ios::beg);
        this->line = this->loop_begin_line;
        this->column = this->loop_begin_col;
    }
}

我能够输入调试信息,并且可以显示堆栈跟踪以进一步明确问题。

单行循环的堆栈跟踪 ( ++[->+<]):

+ + [ - > + < ] [ - > + < ] done.

这按预期工作。

多行循环:

++[
-
>
+<]

堆栈跟踪:

+ + [ - > + < ] > + < ] <- when it looped back, it "skipped" '[' and '-' characters.

由于永远不会满足结束条件,因此永远循环(即第一个单元格的值永远不会为 0,因为它永远不会递减)。

奇怪的是,以下工作:

++[
-
>+<]

它遵循与第一个示例相同的堆栈跟踪。这个工作和最后一个例子不工作是什么让我很难解决这个问题。

如果需要有关程序应该如何工作或其输出的更多信息,请告诉我。抱歉,这篇文章太长了,我只想尽可能清楚。

编辑 1:该类的文件对象为std::ifstream file;. 在构造函数中,它以 开头 this->file.open(filename),其中filename作为参数传入。

4

1 回答 1

1

对于一个文件流,seekg最终是fseek从C标准库中定义的。C标准有这样的说法:

7.21.9.2/4对于文本流,要么offset应为零,要么offset应为先前成功调用ftell与同一文件关联的流上的函数返回的值,并且whence应为SEEK_SET.

因此,对于以文本模式打开的文件,您无法对偏移量进行任何算术运算。您可以倒退到开头、结尾位置,或者返回到您之前所在的位置并使用tellg(最终调用ftell)捕获。其他任何事情都会表现出未定义的行为。

于 2021-11-25T04:38:59.617 回答