在 C 中将文本附加到文件的开头不起作用

问题描述

所以伪代码是:

文件已存在 = 读取文件的全部内容

String newcontent = "Blah Blah 这是第一行" + existing

f.write(newcontent);

实际代码

void Prepend(char *string,char *file_name)
{   

    char buf[100];
    FILE *fp1,*fp2;
    Append(string,"temp_file");
    
    fp1 = fopen(file_name,"a+");
    fp2 = fopen("temp_file","a+");
    
    if (!fp1 && !fp2) {
        printf("Unable to open/"
               "detect file(s)\n");
        exit(1);
    }
 
    fprintf(fp2,"\n");
 

    while (!feof(fp1)) {
        fgets(buf,sizeof(buf),fp1);
    }
 
    rewind(fp2);
 

    while (!feof(fp2)) {
        fgets(buf,fp2);
    }
      fclose(fp1);
      fclose(fp2);
      remove(temp_file);
}

/*-----------------------------------------------------------------------------*/

void Append(char *string,char *file_name)

{   

    FILE *file = fopen(file_name,"a");
    
    if(file==NULL)
    {
        printf("Error opening file.");
    }
    else 
    {
    
        fputs(string,file);    
        
    }
    
    fclose(file);
}

我知道有一种方法可以使用 fseek 和其他与指针相关的东西,但我认为使用“简单方法”并创建一个新的临时文件删除它会更好更快之后就可以了。

好吧,它也不起作用。

解决方法

这里有一个小例子,只使用两个而不是三个文件。

void prepend(const char *filename,const char *str)
{
    // open the orig file (read only)
    FILE *orig = fopen(filename,"r");

    // open tmp file (read / write)
    FILE *tmp = fopen("temp_file","w+");

    // write string into tmp file (prepend)
    fwrite(str,1,strlen(str),tmp);

    // file size of orig
    fseek(orig,0L,SEEK_END);
    long orig_size = ftell(orig);
    rewind(orig); // set pos to 0

    // transfer bytes from orig to tmp (append)
    char *buf = malloc(orig_size);
    fread(buf,orig_size,orig);
    fwrite(buf,tmp);
    free(buf);

    // close orig and delete it
    fclose(orig);
    remove(filename);

    // close and rename tmp file as orig
    fclose(tmp);
    rename("temp_file",filename);
}

注意:为了保持简短,不涉及错误检查。

,

由于您使用的是临时文件,因此不需要辅助缓冲区,只需附加文本,然后将旧文件内容移动到新文件中即可。

您应该注意的一些事项

  • if (!fp1 && !fp2) 仅当两个文件都无法打开时才为真,最好单独检查结果,以便我们知道失败的地方。您可以将 && 替换为 ||,但除非您重新检查文件指针值,否则它失败的原因将不明确。
  • 您似乎从未将数据从 fp1 写入 fp2 我只看到对 fgets() 的调用,您将 fp1 读入 buf 然后您读取fp2 进入 buf,两次都不处理数据。如果文件大于 100 字节,最后 100 字节或更少的数据将存储在 buf 中。
  • 这不一定是最糟糕的事情,但最好只打开具有所需权限的文件。打开一个你只需要在“a+”模式下读取的文件有点奇怪
  • 在创建新文件之前,最好先检查该文件名是否不存在。您最好在系统的临时目录或程序的专用 tmp 目录中创建文件。为其名称使用唯一的字符串也是一个好主意。也许类似于“[我的程序]-[原始文件名]-[.tmp]”而不是这样的通用名称。

应该工作的方法的简短示例,带有错误检查(不一定是最好的错误检查,但可以让您了解可能发生错误的位置):

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

int Prepend(char *string,char *file_name)
{
    FILE *fp1 = NULL,*fp2 = NULL;
    struct stat fs;
    if (stat("temp_file",&fs) == 0) {
        printf("Error,there is already a file named \"temp_file\" in the current working directory\n");
        return -1;
    }
    if ((fp1 = fopen(file_name,"r")) == NULL) {
        printf("Error : %s\n",strerror(errno));
        printf("Failed to Open file %s\n",file_name);
        return -1;
    }
    if ((fp2 = fopen("temp_file","w")) == NULL {
        printf("Error : %s\n",strerror(errno));
        printf("Unable to create Temporary file\n");
        fclose(fp1);
        return -1;
    }
    if (fputs("Prepended String\n",fp2) == EOF) {
        printf("Failed to write string to file\n");
        fclose(fp1);
        fclose(fp2);
        if (remove("temp_file") != 0)
            printf("Error : %s,while removing temp_file\n",strerror(errno));
        return -1;
    }
    int c;
    while((c = fgetc(fp1)) != EOF) {
        if(fputc(c,fp2) == EOF) {
            printf("Error while transfering data from %s to %s\n",file_name,"temp_file");
            fclose(fp1);
            fclose(fp2);
            if (remove("temp_file") != 0)
                printf("Error : %s,strerror(errno));

            return -1;
        }
    }
    fclose(fp1);
    if (remove(file_name) != 0) {
        printf("Error : %s,while removing %s\n",strerror(errno),file_name);
        fclose(fp2);
        return -1;
    }


    fclose(fp2);
    if (rename("temp_file",file_name) != 0) {
        printf("Error : %s,while renaming temp_file\n",strerror(errno));
        return -1;
    }
    return 0;
}

fclose() 也可以在出错时返回 EOF,并设置 errno 以指示错误。但是,对流的任何进一步访问(包括对 fclose() 的另一次调用)都会导致未定义的行为。因此您可以打印错误,但您无能为力。