2011-12-18 24 views
0

我有一個datas.txt文件:Ç - 刪除/ Modifiying一條直線從一個文件

格式:姓名姓催債

bir bir 100 2 
iki iki 200 2 
eray alakese 100 5 
john doe 2000 10 

我學習C和我知道剛纔簡單的文件功能(的fscanf,fprinf,FOPEN等)

我會

  1. 詢問用戶名和姓與scanf然後將它們分配到名稱變量。
  2. 它將搜索文件,然後分配債務和支付債務支付變量(fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);
  3. 刪除或修改此行

這是我的源代碼 。

scanf("%s", &name); 
    scanf("%s", &surname); 
    file = fopen("datas.txt", "r"); 
    /* this fscanf() is working as expected. There is no problem. */ 
    fscanf(file, "%s %s %d %d", name, surname, &debt, &payment); 

    /* modify and delete actions here */ 
    fclose(file); 

例子:

  1. 我要刪除 「李四」 的紀錄。
  2. 我想減少「李四」的債務$ 100
+1

這一般來說相當困難。如果您想在中間進行更改,則不會繞過移動文件的大部分內容。內存映射加'memmove'可能是最簡單的選擇。爲什麼不使用數據庫(比如sqlite)? – 2011-12-18 13:39:57

+0

你測試了這個代碼嗎?在我看來,'fscanf'這行不會做你想做的事情。也就是說,它會覆蓋'name'和'surname'。 – Staven 2011-12-18 13:45:06

回答

3

不能刪除/修改[*]一個文本文件中的各行;唯一的解決方法是1)創建一個新的臨時文件,2)將內容複製到但不包括要修改/刪除的行,3)輸出修改後的行,4)複製原始文件的其餘部分5)用臨時文件替換舊文件。

[*]只有修改後的行長度與原始行長度相同時,才能進行修改。

編輯:PS:使用fgets,其次是sscanf(或其他一些標記行的方式)會爲你節省很多的傷心。

0

通常要做的事情是讀取所有文件並將其全部寫回臨時文件,然後刪除原始文件並重命名臨時文件。

/* pseudo-code!! */ 
fopen(); 
while (fscanf(source, ...)) { 
    /* massage data */ 
    fprintf(temporary, ...); 
} 
fclose(); 
remove(source); 
rename(temporary, source); 
0

爲了刪除或改變一條線,你必須「移動」它後面的所有東西。例如,考慮這兩個文件:

bir bir 100 2   bytes 0-14 
iki iki 200 2   bytes 15-29 
eray alakese 100 5  bytes 30-49 
john doe 2000 10  bytes 50-67 

bir bir 100 2   bytes 0-14 
iki iki 200 2   bytes 15-29 
john doe 2000 10  bytes 30-57 <-- byte offsets have changed 

這當然是可以做到的,但它是相當複雜的,一般支持(你必須做很多的追求和講述)。更常用的方法是有效地複製文件:從輸入文件讀入並將所有內容打印到輸出文件中,進行所需的修改。 (例如,要「刪除」一行,您只需不打印該行。)然後,在最後關閉這兩個文件後,您將「重命名」輸出文件以覆蓋輸入文件。這是命令行實用程序(如sedperl)在指示「就地」修改文件時使用的方法。

1

這有點難,因爲從Unix繼承而來的C文件模型(它們大部分是開發代碼的)實際上並沒有將文件定義爲行列表。相反,它將一行定義爲以換行符結尾的字符串,以及將文件(大致)定義爲存儲的可能長度有限的字節字符串,您可以在其中跳至不同的部分。這相當模糊,但忍受着我。

當我們嘗試將我們的想法 - 「修改此行」,「刪除該行」 - 轉換爲文件操作時,問題變得更加清晰。我們可以通過停在一個換行符來閱讀一行,但是根本沒有命令可以將它切分爲多個部分;只設置結束(ftruncate())。因此,要更改行的大小,我們需要複製後面的所有數據。它可以完成,但是重新創建文件通常更容易。比較實現memmove()的微妙之處。

傳統的做法有兩種,取決於您可以忍受的副作用。

一種是在另一個文件中寫入更新的版本,然後重新命名()到位。這樣做的好處是新文件將在您使用該文件時完成,但缺點是它可能無法精確匹配舊文件,只要權限等等,並且對於其他程序不可見已經打開了舊的。如果兩個程序以這種方式修改文件,則這是一個競爭條件,因爲其中一個更改會被另一個覆蓋。

另一種是完全加載數據並將修改後的版本寫入到位。這意味着文件本身保留在適當的位置,權限和所有內容,但是在保存期間會有一段時間,它是新舊內容的混合。文本編輯往往會這樣做,而往往將舊內容另存爲一個單獨的文件,以防出現問題。

還有一些工具可以管理副作用,例如版本化的文件系統,文件鎖定,甚至爲並行更改準備的庫(metakit可以想到)。大多數時候我們會使用已經存在的工具,比如sed -i。

-1

我通常處理這種事情的方式是編寫一個函數,它可以「讀入」數據並將其存儲到某個結構中。然後是一個將結構中的數據寫入文件的函數。

這樣你就可以操縱數組中的數據。這也使得你的程序可以更容易地擴展,比如排序,或者只是在文件的頂部寫入而無法完成的額外數學。

例如嘗試編寫可以讀取到一個結構的功能,如:

struct Client 
{ 
    char name[255]; 
    double owes; 
    double paid; 
} 

然後你做什麼使這些結構的陣列和操縱這些。 您將學到很多關於結構,動態內存分配的知識,而且您肯定會遇到一些有助於您學習的有趣問題。

我的建議是還跳過C和去C++ ...學習使用輸入輸出流,而不是*的printf這個東西/ * scanf函數的功能和載體很可能將更好地爲您從長遠看

+0

爲什麼downvotes?我錯過了什麼? 我認爲這是一個更具可擴展性的解決方案來處理數據 – Fuzz 2011-12-21 04:21:28