2012-08-02 109 views
0

所以我创建了一个简单的测试应用程序。它的目的是读取或写入文本文件。它正在查找字符串“固件:”。这个想法是,一个设备将有一个包传输到它并存储,然后激活。我想存储该软件包的名称(例如:Update1),并在需要时查询并获取软件包名称(从文件读取)。C++:搜索和写入文件以替换字符串

问题1:它似乎没有写入文件。 问题2:当我得到它写入文件,如果行“固件:pkgName”存在的情况下,如何将pkgName替换为新的软件包名称?

该程序运行和编译,所以随意把它扔在一个IDE或文本编辑器,它只是不完全做我想要的。我使用log.txt作为我的示例文件来读取。我相信有一个更简单的方法来做到这一点。我已经有一段时间了,我已经变得难倒了。

感谢您的帮助!

#include <cstdlib> 
#include <iostream> 
#include <string> 
#include <fstream> 

using namespace std; 

int writeFile(string filename, string pkgName); 
int readFile(string filename); 

int main(void) { 
    string filename = "log.txt", userInput, pkgName; 
    system("touch log.txt"); 

    while (true) { 
     cout << "Write or Read?\n>>>"; 
     cin >> userInput; 

     if (userInput == "Write") { 
      cout << "What's your package name?\n>>>"; 
      cin >> pkgName; 
      writeFile(filename, pkgName); 
     } else if (userInput == "Read") { 
      readFile(filename); 
     } else { 
      cout << "Invalid Input"; 
     } 
    } 
} 

int writeFile(string filename, string pkgName) { 
    bool found, exists = true; 
    string line, word; 
    fstream firmwareFile; 
    int pos, size, num = 1; 
    firmwareFile.open(filename.c_str(), ios::in | ios::out | ios::app); 
    //firmwareFile << "This is a test."; 
    if (firmwareFile.is_open()) { 
     while (!found) { 
      getline(firmwareFile, line); 
      pos = line.find("Firmware: "); 
      if (pos != string::npos) { 
       //Found It 
       found = true; 
       exists = true; 
       size = line.size(); 
       word = line.substr(10, ((size - pos) + 10)); 
       cout << word << " is the old package name" << endl; 
       //TODO - Replace that with the new package name 
       firmwareFile.close(); 
      } else if (firmwareFile.eof()) { 
       //Doesn't Exist 
       found = true; 
       exists = false; 
       cout << "No last firmware package available.\n"; 
      } else { 
       //Still Looking 
       found = false; 
       cout << "Searching line #" << num << endl; 
      } 
      num++; 
      if (!exists) { 
       firmwareFile << endl << "Firmware: " << pkgName << endl; 
       cout << "Wrote package name to file\n" << endl; 
      } 
      firmwareFile.close(); 
     } 
    } else { 
     cout << "Unable to open file"; 
    } 
} 

int readFile(string filename) { 
    bool found; 
    string line, word; 
    fstream firmwareFile; 
    int pos, size, num = 1; 
    firmwareFile.open(filename.c_str(), ios::in | ios::out | ios::app); 
    if (firmwareFile.is_open()) { 
     while (!found) { 
      getline(firmwareFile, line); 
      pos = line.find("Firmware: "); 
      if (pos != string::npos) { 
       found = true; 
       size = line.size(); 
       word = line.substr(10, ((size - pos) + 10)); 
       cout << word << endl; 
      } else if (firmwareFile.eof()) { 
       found = true; 
       cout << "No last firmware package available.\n"; 
       firmwareFile << endl << "Firmware: "; 
      } else { 
       cout << "Searching line #" << num << endl; 
      } 
      num++; 
     } 
     firmwareFile.close(); 
    } else { 
     cout << "Unable to open file"; 
     firmwareFile.close(); 
    } 
    firmwareFile.close(); 
} 

这里是我的log.txt文件的内容(一切,但包含行 “固件:” 是微不足道的)

You 
Think 
It's 
Here 
But 
It's 
Not 
So 
That 
Sucks 
Doesn't 
It 
? 
Firmware: Update1 
I 
Hope 
You 
Caught 
It 
! 
+1

如果您想在随机位置写入,则必须以二进制模式打开文件。另一种选择是读取字符串向量中的文件,替换所需字符串,写入新文件,删除旧文件并最终将新文件重命名为旧名称。现在你用'ios :: app'打开流,所以所有的写操作都会追加到文件的末尾。 – jrok 2012-08-02 19:54:07

+0

我面临的唯一问题是我无法删除旧文件,因为这将在配置文件中,我将需要复制整个文件。我不能“简单地”(没有简单的,我知道)删除行并将其追加到最后,或者只替换行上的packageName? – Cr4cKeR 2012-08-02 19:57:38

+0

不,你不能从文件中间擦除数据,只能替换它。你不会删除配置文件,你基本上会复制一些更改的数据。 – jrok 2012-08-02 19:58:26

回答

0

如果你写的,只有相关的行另一个文件可以简化事情发生了变化:

std::ifstream cfg("log.txt"); 
std::ofstream tmp("log.tmp"); 
std::string line; 
std::string pkgName; 

while(std::getline(cfg, line)) { 
    if (line.find("Firmware:") != std::string::npos) { 
     line = "Firmware: " + pkgName; 
    } 
    tmp << line << '\n'; 
} 

现在,重命名和删除文件是特定的平台。如果你想要一个可移植的解决方案,我建议你看看Boost Filesystem库。

+0

我正在使用嵌入式Linux系统,因此我只会使用: 'mv log.tmp log.txt',它会写入它。谢谢!这将完成复制与更改文件的整个过程?似乎我已经结束了一些事情! – Cr4cKeR 2012-08-02 20:14:37

+0

不要忘记一些错误检查,特别是在写入之前检查流是否打开并处于“良好”状态。 – jrok 2012-08-02 20:18:40

+0

我有一切工作,除了一件事。复制不按计划进行。我在文件中找到了字符串,然后我复制了这些内容,但是我需要知道如何在“Firmware:”实例之后的文件开头而不是下一行开始。有没有办法与fstream做到这一点? – Cr4cKeR 2012-08-03 14:24:26

2

嘛。第一个问题,在你的函数写入文件中,你有一个变量found,你在短时间内测试while (!found) { ... }。问题是,当你第一次测试这个变量时,你没有将它设置为任何值。所以你在那里有未定义的行为。看看你的其他代码我认为你应该使用do while while循环do { ... } while (!found);。看起来你在readFile中也有同样的问题。

第二个问题是我害怕的坏消息。除非用长度完全相同的字符串进行替换,否则不能替换文件中的字符串。这只是文件在磁盘上组织方式的限制。实现替换的唯一方法是将整个文件读入内存,在内存中进行替换,然后将整个文件写回磁盘。

2

@jrok回答了你的问题2,但我怀疑你的代码不会每次都写,因为找不到初始化,因此未定义,所以while循环的一半时间根本不会执行。您需要初始化发现和存在,如:bool found = false, exists = true;

+0

我已经切换了循环并初始化了布尔值,但我仍然遇到了这个非写入问题。我怀疑这可能是因为我有ios :: app标志,并且我读到eof。有没有解决的办法?如果我正在搜索的行不在那里,我该如何追加它?我已经移动了firmwareFile <<“固件:”<< pkgName;到几个不同的空间,但它无法写。如果我取消注释firmwareFile <<“这是一个测试”行 - 它写得很好。 – Cr4cKeR 2012-08-02 20:12:09

+0

一旦'eof'返回true,流将处于错误状态,随后的写入操作将失败。你需要用'ios :: clear()'清除错误标志。 – jrok 2012-08-02 20:17:09

0

如果您必须替换文件的内容(不删除并重命名另一个文件的名称),则需要读取整个文件,对内存中的文件进行必要的更改,然后将内存文件的全部内容写出到磁盘文件中,并将其覆盖。

如果您允许删除源文件,则执行这些转换要容易得多。您只需读取源文件,打开一个临时文件并写出来,随时随地做出所需的任何更改。完成处理源文件后,将其删除,并将新文件重命名为与原始源文件相同的名称。