2012-08-02 75 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

如果您必須替換文件的內容(不刪除並重命名另一個文件的名稱),則需要讀取整個文件,對內存中的文件進行必要的更改,然後將內存文件的全部內容寫出到磁盤文件中,並將其覆蓋。

如果您允許刪除源文件,則執行這些轉換要容易得多。您只需讀取源文件,打開一個臨時文件並寫出來,隨時隨地做出所需的任何更改。完成處理源文件後,將其刪除,並將新文件重命名爲與原始源文件相同的名稱。