【问题标题】:"Updating" a line in a file by overwriting it通过覆盖来“更新”文件中的一行
【发布时间】:2012-06-19 22:36:03
【问题描述】:

我正在尝试编写一个函数,当调用该函数时,它将在我的文本文件中搜索与参数匹配的内容,然后通过覆盖文件来“更新”它。

这是我目前的功能:

编辑:这是新的更新代码,仍然无法正常工作:/

这是它发送到 UpdateGem 的行:(示例 sTempTxt:“AB:5.55”)

ostringstream stream; 
stream << std::setprecision(3)  << fEindgem;
string sTempTxt = sVak + ":" + stream.str() + "\n";
UpdateGem(sTempTxt);

void UpdateGem(string text)
{
     ifstream f;
     f.open("GEM.txt");
     string sGEMS[100];
     string temp[3];
     splitstring(text, ":", temp[0], temp[1]);
     bool OverWrite;
     int count = 0;
     string Delete, line;

     while(true)
     {
         getline(f, sGEMS[count], '\n');
         if(f.eof()) break;
         splitstring(sGEMS[count], ":", temp[1], temp[2]);
         if (temp[0] == temp[1])
         {
              OverWrite = 1;
              Delete = sGEMS[count];
         }
         count++;
     }

// Don't set count to 0, since we need it
// count = 0;
   ofstream f2;
   f2.open("GEM2.txt"/*,std::ios_base::app*/);
   if (OverWrite) 
   {
      f.seekg(0, std::ios::beg);
      for (int i = 0; i < count; ++i) 
      {
         if (sGEMS[i] != Delete) f2 << sGEMS[i];
      }
   }
     f.close();
     f2.close();
     remove("GEM.txt");
     rename("GEM2.txt", "GEM.txt");

     ofstream file;
     file.open("GEM.txt",std::ios_base::app);
     file << text;
     file.close();     


}

它必须替换的行采用 NAME:NUMBER 的形式,其中数字可以不同,所以我使用 splitstring 函数将名称与找到的行的名称进行比较,然后完全擦除该行并通过稍后重新添加来“更新”它。但是,我当前的代码只将更新的行写入文件,而不是旧的...

【问题讨论】:

  • 如果替换文本与被替换文本的大小不完全相同,则在文件中间覆盖是一个令人担忧的过程。
  • 您有两个 while 循环在 f 上执行 getline。如果您在第一个循环中到达 eof,则第二个循环不会读取任何行。您应该在循环之间的某处重置流的内部获取指针。 f.seekg(0, std::ios::beg);
  • 能否请您解释一下,用外行的话来说,我还是个初学者..
  • Write in the middle of a binary file without overwriting any existing content 的答案中有代码可能会让您对插入文件中间有所了解。该问题被标记为 C,因此答案也是 C,但它可能会帮助您了解有关将信息插入文件中间的一些想法。缩短文件更简单,但也需要小心。相比之下,用相同数量的新数据覆盖文件的一部分是微不足道的。这就是为什么固定长度的记录数据文件很受欢迎的原因。它们也很简单。

标签: c++ file fstream copying rewriting


【解决方案1】:

从您的程序看来,您假设“GEM.txt”中的文本不会超过 100 行。但是,在您对文件的初始扫描中,您不记得读入了多少行。因此,为了弥补这一点,您尝试通过重新读取原始“GEM.txt”文件来记住多少行,并将您保存的行写入“GEM2.txt”。

正如 jrok 正确指出的那样,在您的第二个循环中,您正在从第一个循环中遇到 feof 时开始读取“GEM.txt”。因此,您的第二个循环永远不会进入while 循环,因为getline 调用认为没有什么可获取的(因为f 在文件末尾)。所以 jrok 给了你在第二个 while 循环之前添加的代码行:

f.seekg(0, std::ios::beg);

但是,由于您已经将所有行保存在一个数组中,您可以直接将sGEMS 数组写入新文件。

// Don't set count to 0, since we need it
// count = 0;
ofstream f2;
f2.open("GEM2.txt"/*,std::ios_base::app*/);
if (Overwrite) {
    for (int i = 0; i < count; ++i) {
        if (sGEMS[i] != Delete) f2 << sGEMS[i] << std::endl;
    }
}
//...

如果文件超过 100 行,使用向量会更安全,而不是使用数组。而且,您将不再需要 count,因为它是由 vector 的大小跟踪的。

std::vector<std::string> sGEMS;
//...
while (true) {
    getline(f, line);
    if (f.eof()) break;
    sGEMS.push_back(line);
    //...
}

如果您希望“GEM.txt”文件非常大,您可能需要重新考虑如何处理该文件。特别是,您应该只记住要跳过的行号,并按照您最初尝试的方式将行从“GEM.txt”复制到“GEM2.txt”。

最后,在代码的最后写入中,您打开文件进行追加,而不是输出。 C++ 认为这意味着您想要丢弃文件的内容。相反,请将ios_base::out 标志添加到您的公开通话中。

ios_base::openmode mode = ios_base::out|ios_base::app;
file.open("GEM.txt",mode);
file << text << std::endl;
file.close();

但是,您可以不这样做,而是在关闭它之前将其添加到 f2 的末尾。

根据我的建议,UpdateGem 的最终形式应该是:

void UpdateGem(string text) {
    ifstream f;
    ofstream f2;
    vector<string> sGEMS;
    string temp[3];
    bool OverWrite;
    string Delete, line;

    splitstring(text, ":", temp[0], temp[1]);
    sGEMS.reserve(100);
    f.open("GEM.txt");
    while (true) {
        getline(f, line);
        if (f.eof()) break;
        sGEMS.push_back(line);
        splitstring(line, ":", temp[1], temp[2]);
        if (temp[0] == temp[1]) {
            OverWrite = 1;
            Delete = line;
        }
    }
    f.close();
    f2.open("GEM2.txt"/*,std::ios_base::app*/);
    if (OverWrite) {
        for (int i = 0; i < sGEMS.size(); ++i) {
            if (sGEMS[i] != Delete) f2 << sGEMS[i] << std::endl;
        }
    }
    f2 << text << std::endl;
    f2.close();
    remove("GEM.txt");
    rename("GEM2.txt", "GEM.txt");
}

我使用以下main 程序测试了代码:

int main () { UpdateGem("Test:0001"); }

并像这样实现splitstring

bool splitstring (string s, string match, string &a, string &b)
{
    int x = s.find(match);
    if (x != string::npos) {
        a = s.substr(0, x);
        b = s.substr(x+match.size());
        return true;
    }
    return false;
}

当输入以下输入“GEM.txt”时:

a:x
b:x
Test:1234
c:x

程序将“GEM.txt”的内容修改为:

a:x
b:x
c:x
Test:0001

【讨论】:

  • 感谢您的帮助!等我回家后我会试试这个。此外,可以安全地假设该文件永远不会包含超过 100 行,因为只有 X 数量的类型可以存在,并且永远不会大于 100。我也不知道向量是什么。我正在通过使用我在 PAWN 的经验和偶尔的谷歌搜索来学习自己编码:)
  • 好的,我已经试过了,但不是向其中添加新行,而是完全删除旧数据,只添加新行。
  • 我已经接受了,因为您的回答是迄今为止最好的。现在尝试简单的改变。编辑:刚刚尝试删除 f.seekg(0, std::ios::beg);并使用您的循环,但它仍然无法正常工作。
  • 它没有用,现在我正在尝试你的,它也不起作用:/
  • 不,没想到,但是我的代码仍然无法正常工作。请你把它完整地写出来,因为我觉得我又做错了什么。我将在主帖中更新代码。再次感谢您的帮助! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-07
  • 2012-11-12
  • 2021-08-02
  • 2014-12-28
相关资源
最近更新 更多