2017-01-29 41 views
1

我試圖刪除一個文件,然後從一個沒有工作的函數中刪除文件的名稱中重命名臨時文件。請幫我c中的remove()在函數中不起作用

boolean delete_user(char user_name[256]) //only for Admin 
{ 
    boolean status = FALSE;//what does the function return 
    User_Data *ud = NULL; 
    boolean found_user = FALSE; 
    FILE *new_file = NULL; 
    FILE *fp = NULL; 
    char user_name_to_copy[256]; 
    char password_to_copy[256]; 
    char old_file_name[256]; 

    ud = find_user(user_name); 
    if (ud == NULL) { 
     printf("The username wasn't found!\n"); 
     return FALSE; 
    } 
    if (!strcmp(ud->permission_type, "Admin")) { 
     printf("Cant delete an admin."); 
     return FALSE; 
    } else { 
     // the user to delete was found 
     new_file = fopen("duplicate.txt", "wt"); 
     strcpy(old_file_name, ud->permission_type); 
     strcat(old_file_name, "s.txt"); //the name of the file is in plural and ends with .txt 
     fp = fopen(old_file_name, "rt"); 
     while (!feof(fp)) { 
      //copy all the users except the user to delete the new file 
      fscanf(fp, "%s %s\n", user_name_to_copy, password_to_copy); 
      if (strcmp(user_name_to_copy, user_name)) { 
       fprintf(new_file, "%s %s\n", user_name_to_copy, password_to_copy); 
      } 
     } 
     fclose(fp); 
     fclose(new_file); 
     printf(" %d ", remove(old_file_name)); 
     rename("duplicate.txt", old_file_name); 
     remove("duplicate.txt"); 
     return TRUE; 
    } 
} 

這個函數不起作用,當我從另一個函數調用它,但從主函數工作得很好。

+2

你是否已經通過調試器直通了代碼? – Dai

+2

向我們展示如何從** main **調用函數,以及如何從**另一個函數調用** – iwaduarte

+2

請參閱['while(!feof(file))'總是錯誤的](http:// stackoverflow .com/questions/5431941/while-feof-file-is-always-wrong)。這不太可能是你報告的主要問題的原因,但即使他們還沒有引起你的痛苦,你也不應該沉迷於壞習慣。 –

回答

2

有你的代碼中的多個問題:

  • 你不檢查是否fopen()成功打開的文件。

  • 在連接字符串以計算權限文件時,您不會防止潛在的緩衝區溢出。

  • 您應該將數組大小傳遞給fscanf以防止潛在的緩衝區溢出。

  • 您應該使用strerror(errno)來輸出有意義且信息豐富的錯誤消息,其中包含失敗原因。

  • 循環複製許可文件不正確:Why is 「while (!feof (file))」 always wrong?

  • 您刪除,即使你沒有將其重命名,但設法刪除原始文件,重複的文件:這兩個文件可能會丟失。相反,如果重命名操作成功,則刪除重複文件是多餘的。

這裏是你如何能提高代碼:

#include <errno.h> 
#include <string.h> 

boolean delete_user(char user_name[256]) { //only for Admin 
    boolean status = FALSE; // the function return value 
    User_Data *ud = NULL; 
    boolean found_user = FALSE; 
    FILE *new_file = NULL; 
    FILE *fp = NULL; 
    char user_name_to_copy[256]; 
    char password_to_copy[256]; 
    char old_file_name[256]; 

    ud = find_user(user_name); 
    if (ud == NULL) { 
     printf("User '%s' was not found!\n", user_name); 
     return FALSE; 
    } 
    if (!strcmp(ud->permission_type, "Admin")) { 
     printf("Cannot delete user '%s', user has admin status.\n", user_name); 
     return FALSE; 
    } 
    // the user to delete was found 
    new_file = fopen("duplicate.txt", "wt"); 
    if (new_file == NULL) { 
     printf("Cannot open file 'duplicate.txt': %s\n" 
       strerror(errno)); 
     return FALSE; 
    } 
    // the name of the file is in plural and ends with .txt 
    snprintf(old_file_name, sizeof old_file_name, "%ss.txt", ud->permission_type); 
    fp = fopen(old_file_name, "rt"); 
    if (fp == NULL) { 
     printf("Cannot open user file '%s': %s\n" 
       old_file_name, strerror(errno)); 
     return FALSE; 
    } 
    // copy all the users except the user to delete the new file 
    while (fscanf(fp, "%255s %255s\n", user_name_to_copy, password_to_copy) == 2) { 
     if (strcmp(user_name_to_copy, user_name)) { 
      fprintf(new_file, "%s %s\n", user_name_to_copy, password_to_copy); 
     } 
    } 
    fclose(fp); 
    fclose(new_file); 
    if (remove(old_file_name)) { 
     printf("Error removing file '%s': %s\n", 
       old_file_name, strerror(errno)); 
     remove("duplicate.txt"); 
     return FALSE; 
    } 
    if (rename("duplicate.txt", old_file_name)) { 
     printf("Error renaming file 'duplicate.txt' to '%s': %s\n", 
       old_file_name, strerror(errno)); 
     // keep duplicate.txt 
     return FALSE; 
    } 
    // duplicates.txt was successfully renamed, no need to remove it. 
    return TRUE; 
} 

注:

  • 創建在當前目錄下的臨時文件,該文件可能駐留在不同的驅動器上權限文件。 rename()可能無法將重複文件從當前目錄移動到該目錄。如果這是失敗的原因,那麼運行具有適當診斷的代碼。
+0

是否需要'remove(old_file_name)'? – Schwern

+0

@Schwern:是的,它是:*** C11 7.21.4.2 ** ...如果在調用重命名函數之前由new指向的字符串命名的文件存在,則該行爲是實現定義的。* – chqrlie

+0

瘋! [POSIX似乎同意我](http://pubs.opengroup.org/onlinepubs/009695399/functions/rename.html)。 *如果由新參數命名的鏈接存在,它將被刪除並且舊的被重命名爲新的。* – Schwern

1
printf(" %d ", remove(old_file_name)); 
    rename("duplicate.txt", old_file_name); 
    remove("duplicate.txt"); 

該代碼主要是多餘的。 有沒有必要刪除 old_file_namerename將炸過它。 (原來這是POSIX的東西,C標準不能保證它)。沒有必要刪除duplicate.txt,它已被重命名。

if(remove(old_file_name) != 0) { 
     fprintf(stderr, "Could not remove %s: %s", old_file_name, strerror(errno)); 
    } 

    if(rename("duplicate.txt", old_file_name) != 0) { 
     fprintf(stderr, "Could not rename %s to %s: %s", "duplicate.txt", old_file_name, strerror(errno)); 
    } 

你需要做的檢查每個文件操作。這包括fopenfscanf

此外,您可能會倒退文件。這是rename(old, new),但它看起來像你寫的rename(new, old)。如果您沒有將它們顛倒過來,請考慮使代碼更易於理解的變量名稱。


fp = fopen(old_file_name, "rt"); 
    while (!feof(fp)) { 
     //copy all the users except the user to delete the new file 
     fscanf(fp, "%s %s\n", user_name_to_copy, password_to_copy); 
     if (strcmp(user_name_to_copy, user_name)) { 
      fprintf(new_file, "%s %s\n", user_name_to_copy, password_to_copy); 
     } 
    } 

此代碼是有問題的。如上所述,它不能檢查fopenfscanf是否成功。使用fscanf是一個問題,如果它不匹配它不提前文件指針。你可以反覆讀同一行,每次都失敗。一般來說,avoid fscanf and scanf

改爲用fgets來讀取整行,並用sscanf解析。一定要檢查sscanf是否成功,否則你會打印亂碼。請注意,sscanf限制了它將讀取的字符串的大小以防止緩衝區溢出。

FILE *fp = open_file(old_file_name, "rt"); 
    char line[1024]; 
    while (fgets(line, 1024, fp)) { 
     //copy all the users except the user to delete the new file 
     if(sscanf(fp, "%255s %255s\n", user_name_to_copy, password_to_copy) != 2) { 
      fprintf(stderr, "Couldn't understand line '%s'\n", line); 
      continue; 
     } 
     if (strcmp(user_name_to_copy, user_name)) { 
      fprintf(new_file, "%s %s\n", user_name_to_copy, password_to_copy); 
     } 
    } 
    fclose(fp); 

由於檢查打開的文件是否有冗餘,我建議您寫一個小功能來做到這一點。

FILE *open_file(char *filename, char *mode) { 
    FILE *fp = fopen(filename, mode); 
    if(fp == NULL) { 
     fprintf(
      stderr, "Could not open '%s' for '%s': %s\n", 
      filename, mode, strerror(errno) 
     ); 
     exit(1); 
    } 

    return fp; 
} 
+1

取決於操作系統,刪除目標文件可能是必要的。 「重命名」將其刪除並自動重命名爲一步,但Windows可能不會。 C標準說:*** C11 7.21.4.2 ** ...如果在調用重命名函數之前由new指向的字符串命名的文件存在,則該行爲是實現定義的。* – chqrlie

+1

您應該通過數組大小爲'sscanf'以防止潛在的緩衝區溢出。如果'sscanf'失敗,你不應該使用這些數組。只需打印源代碼行似乎是正確的選擇。我的版本會去除文件的其餘部分,這可能是不夠的。 – chqrlie

+1

名稱'old_file_name'具有誤導性,'rename(「duplicate.txt」,old_file_name)'具有正確順序'rename(old,new)'的參數。 – chqrlie