在另一个问题中,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”。谁能解释为什么会发生这种情况?
答案 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个字符。