[英]C - Deleting / Modifiying A line From A File
我有一個datas.txt文件:
格式:姓氏債務償還
bir bir 100 2
iki iki 200 2
eray alakese 100 5
john doe 2000 10
我正在學習C並且我只知道簡單的文件功能(fscanf,fprinf,fopen等)
生病
scanf
詢問用戶名和姓氏,然后將其分配給name和surname變量。 fscanf(file, "%s %s %d %d", name, surname, &debt, &payment);
) 這是我的源代碼。
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)將內容復制到您要修改/刪除的行中,但不包括該行,3)輸出修改后的行,4)復制其余的原始文件,5)用臨時文件替換舊文件。
[*]僅當修改后的行與原始行的長度相同時,才可以進行修改。
編輯:PS:使用fgets,然后使用sscanf(或標記該行的某種其他方式)將為您節省很多痛苦。
這有點難做,因為C的文件模型是從Unix繼承的(它們很大程度上是共同開發的),實際上並沒有將文件定義為行列表。 取而代之的是,它將行定義為以換行符結尾的字節字符串,並將文件(大致)定義為可能限制長度的字節存儲字符串,您可以在其中跳到不同的部分。 那是相當模糊的,但請忍受。
當我們嘗試將我們的想法(“修改此行”,“刪除該行”)轉換為文件操作時,問題變得更加明顯。 我們可以通過在換行處停止來讀取一行,但是根本沒有命令將其切成小節。 僅用於設置結尾(ftruncate())。 因此,要更改行的大小,我們需要復制其后的所有數據。 可以做到,但是重新創建文件通常會更容易。 比較實現memmove()的精妙之處。
執行此操作的傳統方法有兩個變體,具體取決於您可以忍受的副作用。
一種是將更新的版本寫入另一個文件,然后將其重命名()到位。 這樣做的好處是,新文件將在您放置到位時完成,但是不利之處在於,在權限等方面,它可能與舊文件不完全匹配,並且其他程序看不到它。已經打開了舊的。 如果兩個程序這樣修改文件,則這是競爭條件,因為其中一個更改被另一個覆蓋。
另一方法是完全加載數據,並將修改后的版本記下來。 這意味着文件本身將保留在原位,權限和所有內容,但是在保存文件的過程中將保留新舊內容的混合時間。 文本編輯器通常會這樣做,通常是在將舊內容另存為單獨文件的情況下,以防出現問題。
也有一些工具來管理副作用,例如版本化的文件系統,文件鎖定,甚至是為並行更改准備的庫(想到的就是metaakit)。 大多數時候,我們將使用已經存在的工具,例如sed -i。
通常要做的是讀取所有文件並將其全部寫回到臨時文件,然后刪除原始文件並重命名臨時文件。
/* pseudo-code!! */
fopen();
while (fscanf(source, ...)) {
/* massage data */
fprintf(temporary, ...);
}
fclose();
remove(source);
rename(temporary, source);
為了刪除或更改一行,您必須“移動”其后的所有內容。 例如,考慮以下兩個文件:
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
當然可以做到這一點,但是總體上來說要支持它非常復雜(您必須進行大量的查找和判斷)。 更為常用的方法是有效地復制文件:從輸入文件讀入並將所有內容打印到輸出文件,進行所需的修改。 (例如,要“刪除”一行,您只需不打印該行。)然后,最后,關閉兩個文件后,您“重命名”輸出文件以覆蓋輸入文件。 這是命令行工具(例如sed
和perl
在被指示“就地”修改文件時使用的方法。
我通常這樣處理的方式是編寫一個可以“讀取”數據並將其存儲到某種結構的函數。 然后是將數據從結構寫入文件的函數。
這樣,您就可以操作數組中的數據。 這也使您的程序更具擴展性,可以執行諸如排序或僅通過覆蓋文件頂部而無法完成的額外數學運算之類的事情。
例如,嘗試編寫一個可以讀入以下結構的函數:
struct Client
{
char name[255];
double owes;
double paid;
}
然后,您要做的就是將這些結構組成數組並進行操作。 您將學到很多有關結構,動態內存分配的知識,並且無疑會遇到一些有趣的問題,這些問題將對您有所幫助。
我的建議是也跳過C,轉而使用C ++ ...從長遠來看,使用iostreams代替* printf / * scanf函數和向量來學習這些知識可能會更好
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.