使用ofstream编辑C++文本文件

3

我目前正在尝试操作文本文件的文本内容(不使用向量):

(这是工程论文作业,我不被允许使用向量)

变量numRows可以是任何整数(我选择了3只是为了举例),它是输出文本文件“example.txt”中的元素的方阵。

以下是我的代码:

#include <iostream>
#include <fstream>
using namespace std;

int main () {

int numRows = 3;

  ofstream myfile ("example.txt");
  if (myfile.is_open())
  {
    for (int i = 0; i < numRows; i++) {
    for (int j = 0; j < numRows; j++) {
    myfile << "Test\t";
    } 
    myfile << "\n";
    }
    myfile.close();
  }

  else cout << "Unable to open file";

  return 0;
}

文本文件如下:
Test    Test    Test    
Test    Test    Test    
Test    Test    Test    

我的问题是,如果我完成了所有操作后,如何再次打开并编辑它?例如,我想将第2行第2列编辑为“Hello”:

Test    Test    Test    
Test    Hello   Test    
Test    Test    Test    

我该如何做?我需要能够操作存储在该文本文件中的字符串,但由于我已经通过制表符分隔了每个字符串,所以不确定该怎么做。

感谢您抽出时间来帮助!

2个回答

3
在通常的文件系统中,您无法在任意位置插入字符。如果要原地“编辑”文件,则应使用每个记录占用固定空间的格式。当然,每个记录的大小将受到此固定量的限制。例如,您可以使用每个记录10个字节,将最大大小限制为10。如果记录较短,则必须使用某些不可能发生的字节(例如,制表符)填充它,或者在记录的固定位置使用适当数量的字节来指定所使用字符串的大小。对于最多255个字节,将使用额外的一个字节。
由于记录具有固定大小,因此可以跳转到修改后的记录位置:其起始位置为(row * columns + column) * record_size。然后,只需编写新值(如果选择保存大小,则还需在记录中指定字符串的大小)。
如果需要支持没有上限的可变长度字符串,或者需要使用紧凑表示,则上述方法无法工作。那么,您将需要完全重写该文件,具体操作如下:
1.打开输入文件以进行读取。 2.打开临时文件以进行写入。 3.读取每个未更改的记录并将其写入临时文件。 4.读取已更改的记录并对其不做任何处理。 5.将新值写入临时文件。 6.将输入文件的其余部分复制到临时文件中。 7.关闭两个文件。 8.将临时文件重命名为源文件名。
显然,如果需要同时执行多个编辑操作,则需要根据需要继续复制/替换。尽管如此,该方法的流程基本保持不变。
我可以轻松地编写您的家庭作业代码,但上述步骤已经从该任务中剔除了所有思考。此外,我不需要练习编程,因此不提供代码。

1
“编辑”这样的文件并不是非常实用,因为大多数文件系统(和标准文件I/O API)只支持覆盖文件内容 - 你不能用不同长度的文本替换某些文本,并使文件相应地调整剩余文件内容的位置。因此,最好将文件读入内存中的一些变量中,在那里进行编辑,然后在您需要时将更新后的内容写出(即覆盖整个文件)。要读取文件,可以使用std::ifstream。要将文件存储在内存中,std::vector<std::vector<std::string>> - 其中外部向量表示行/行,内部向量表示该行/行中的列值 - 似乎是给定您的问题描述的自然匹配,但您可以考虑std::vector<std::string>,其中每个字符串表示包括制表符的完整行/行。
如果您知道每个字符串(例如"Text""Hello")的宽度上限,并且愿意使用空格而不是尾随制表符来填充每个字符串,那么您可以覆盖文本和空格,而无需调整文件大小(只要您不改变行/列的数量或添加/删除列)。为此,您需要在std :: fstream"查找"到要替换值的位置。

进一步讨论评论,如果您坚持直接更改文本文件,则一个简单的选择是在读取原始文件时编写更新的文件,然后将该更新的输出文件移动到原始文件上。实际生成更新的输出文件可能类似于:

#include <fstream>
#include <iostream>

void edit_file(int line_to_change, int column_to_change, const std::string& change_to)
{
    if (std::ifstream in{"txt_in"})
        if (std::ofstream out{"txt_out"})
        {
            int line = 1, column = 1;
            std::string word;
            char whitespace;
            while (in >> word && in.get(whitespace))
            {
                if (line == line_to_change && column == column_to_change)
                    word = change_to;
                out << word << whitespace;
                if (whitespace == '\n') // newline...
                {
                    ++line;
                    column = 1;
                }
                else // presumably a tab...
                    ++column;
            }
        }
        else
            std::cerr << "unable to open output file\n";
    else
        std::cerr << "unable to open input file\n";
}

int main()
{
    edit_file(2, 2, "Hello");
}

注意事项:

  • 使用in.get(whitespace)代替>>,因为后者会跳过空白符,而我们需要读取空白符以区分制表符和换行符。

  • 将输出发送到一个独立的文件可以避免麻烦,当你插入比文件之前包含的更长的"单词"时,如果天真地完成,会覆盖你即将要写入的输入,如果正确完成,则需要在内存中进行任意数量的缓冲。


问题在于我不能在这个项目中使用向量,并且我需要能够在不同的时间点操作行和列。我该如何做到这一点,可能稍后再考虑制表符等问题? - Jared
@Jared:使用new[]可以手动分配动态内存,这在功能上等同于向量,但初学者在这样做时往往会犯很多错误。另一种选择是在运行时读取和修改文件,边读边写入新值,如果您需要进行长序列的更新或处理大型文件,则性能方面不是非常可扩展,但也许您并不需要。 - Tony Delroy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接