C中意外的输出复制文件

时间:2009-01-23 21:49:29

标签: c file-io

在另一个问题中,accepted answer显示了一种将文件内容读入内存的方法。

我一直在尝试使用此方法读取文本文件的内容,然后将其复制到新文件中。但是,当我将缓冲区的内容写入新文件时,文件末尾总会有一些额外的垃圾。以下是我的代码示例:

inputFile = fopen("D:\\input.txt", "r");
outputFile = fopen("D:\\output.txt", "w");

if(inputFile)
{
    //Get size of inputFile
    fseek(inputFile, 0, SEEK_END);
    inputFileLength = ftell(inputFile);
    fseek(inputFile, 0, SEEK_SET);

    //Allocate memory for inputBuffer   
    inputBuffer = malloc(inputFileLength);

    if(inputBuffer)
    {
        fread (inputBuffer, 1, inputFileLength, inputFile);
    }

    fclose(inputFile);

    if(inputBuffer)
    {
        fprintf(outputFile, "%s", inputBuffer);
    }

    //Cleanup
    free(inputBuffer);
    fclose(outputFile);
}

输出文件始终包含输入文件的精确副本,但随后在末尾附加了文本“MPUTERNAM2”。谁能解释为什么会发生这种情况?

8 个答案:

答案 0 :(得分:7)

你可能会更开心

int numBytesRead = 0;
if(inputBuffer)
{
  numBytesRead = fread (inputBuffer, 1, inputFileLength, inputFile);
}

fclose(inputFile);

if(inputBuffer)
{
  fwrite( inputBuffer, 1, numBytesRead, outputFile );
}

它不需要以空字符结尾的字符串(因此可以在包含零的二进制数据上正常工作)

答案 1 :(得分:4)

您没有为缓冲区中的终止空字符分配足够的空间(并且您也忘记实际设置它),因此您的fprintf实际上已经过度读入其他内存。你的缓冲区与文件的大小完全相同,并且填充了它的内容,但是,fprintf会读取查找终止空值的参数,而不是那里,直到后面的几个字符,巧合的是,有一个字符。

修改
你实际上混合了两种类型的io,fread(与fwrite配对)和fprintf(与fscanf配对)。您可能应该使用要写入的字节数来执行fwrite;或者相反,使用fscanf,它会使你的字符串空终止(尽管,这不会允许你的字符串中的空值)。

答案 2 :(得分:4)

因为您正在编写缓冲区,就好像它是一个字符串一样。字符串以NULL结尾,您读取的文件不会。

你可以NULL终止你的字符串,但更好的解决方案是使用fwrite()而不是fprintf()。这也可以让你复制包含NULL字符的文件。

除非您知道输入文件总是很小,否则您可能会考虑在循环中读/写,以便您可以复制大于内存的文件。

答案 3 :(得分:2)

分配内存以适应文件实际上是一种非常糟糕的方式,特别是在这里完成的方式。如果malloc()失败,则不会将任何数据写入输出文件(并且它会以静默方式失败)。换句话说,由于地址空间的限制,您无法在32位平台上复制大于几千兆字节的文件。

使用较小的内存块(已分配或​​在堆栈上)并以块的形式读/写文件实际上要好得多。无论如何,读取和写入都将被缓冲,只要您使块相对较大,对C运行时库的函数调用的开销就很小。

您应该始终以二进制模式复制文件,因为没有翻译机会,所以速度更快。

类似的东西:

FILE *fin = fopen ("infile","rb");  // make sure you check these for NULL return
FILE *fout = fopen ("outfile","wb");
char buff[1000000];  // or malloc/check-null if you don't have much stack space.
while ((count = fread (buff, 1, sizeof(buff), fin)) > 0) {
    // Check count == -1 and errno here.
    fwrite (buff, 1, count, fout); // and check return value.
}
fclose (fout);
fclose (fin);

这是来自记忆,但提供了如何做的一般概念。你应该总是进行copiuos错误检查。

答案 4 :(得分:1)

fprintf期望inputBuffer以空值终止,但事实并非如此。所以它正在读取inputBuffer的结尾并打印任何内容(进入新文件),直到找到空字符。

在这种情况下,你可以malloc一个额外的字节,并将null作为inputBuffer中的最后一个字符。

答案 5 :(得分:1)

除了其他人所说的内容之外:您还应该以二进制模式打开文件 - 否则,您可能会在Windows(或其他非POSIX系统)上获得意外结果。

答案 6 :(得分:1)

您可以使用

fwrite (inputBuffer , 1 , inputFileLength , outputFile );

而不是fprintf,以避免零终止字符串问题。它还与fread“匹配得更好”:)

答案 7 :(得分:0)

尝试使用fgets,它会在字符串末尾为您添加null。同样如上所述,你需要为null终止符多一个空格。

字符串“Davy”表示为包含D,a,v,y,\ 0(不带逗号)的数组。基本上你的数组至少需要sizeofstring + 1来保存null终止符。此外,fread不会自动添加终结符,这就是为什么即使你的文件比你获得垃圾的最大长度短的原因..

注意另一种懒惰的方法就是使用calloc将字符串设置为0.但是你最多只能读取inputFileLength-1个字符。