我有以下代码尝试读取文本文件,进行备份,并将读取文件字符串传递给进一步的处理例程。我看到的行为非常出乎意料。
01: rewind(PPLFile);
02: fseek(PPLFile, 0, SEEK_END);
03: unsigned long fsize = ftell(PPLFile);
04: char *string = (char*)calloc(fsize + 1, sizeof(char));
05: rewind(PPLFile);
06: fread(string, sizeof(char), fsize, PPLFile);
07: FILE* PPLBackup;
08: char* fileSuffix = ".backup";
09: char* PPLBackupLocation = (char*)calloc(strlen(basePath) + strlen(fileSuffix) + 1, sizeof(char));
10: strcpy(PPLBackupLocation, basePath);
11: strcat(PPLBackupLocation, fileSuffix);
12: PPLBackup = fopen(PPLBackupLocation, "w");
13: fprintf(PPLBackup, fileContent);
14: fclose(PPLBackup);
15: free(PPLBackupLocation);
文件句柄*PPLFile
以前已使用 w+
或 a+
标志打开,具体取决于最初写入文件的先前代码中的某些情况。
我认为的概念很简单:
- 将指针移到文件末尾并感知其位置以建立文件大小(以字节为单位(
- 创建字节大小加 1 的空字符串(对于空终止符(
- 将整个文件内容倒带并读取到字符串中,直至检测到的文件大小
- 创建备份文件名和文件指针
- 将从原始文件读取的整个字符串写入新的备份文件。
- 关闭备份文件并继续处理读取字符串
有两个令人不安的症状:
- 包含原始文件的 *char 字符串有更多的零(文本文件中的数千个 ~=20MB(,因此最后一个非零值可能是从末尾开始的 3000 个字节。似乎比原始文件小。
- 写入备份文件时,生成的文件比原始文件大一点,末尾附加了一个看似随机的原始文件的额外样本,又是几千字节。
很简单,到底是怎么回事?
这一行不好:
13: fprintf(PPLBackup, fileContent);
如果文件包含像%s
这样的字符序列,那么fprintf
将在堆栈上查找其他数据。
您应该改用fwrite
:
13: fwrite(fileContent, fsize, 1, PPLBackup);
或者至少这样做:
13: fprintf(PPLBackup, "%s", fileContent);
为什么你用fread((阅读而用fprintf((而不是fwrite((写作?以及您使用 fprintf(( 的方式,您将受到文件中第一个%
登录(格式字符串漏洞(的摆布。
此外,请确保两个文件以相同的模式(文本模式与二进制模式(打开。通常,fread((/fwrite(( 倾向于与二进制模式一起使用。
这个完整的程序在 Linux 上对我有用。 它有克拉斯的改动。 注意我必须使用 fsize 作为全局,因为传递要复制的文件的 char * 内容会中断,如果以后被 strlend - 出于显而易见的原因
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
unsigned long fsize=0L;
char *get_file(FILE *PPLFile) {
rewind(PPLFile);
fseek(PPLFile, 0, SEEK_END);
fsize = ftell(PPLFile);
char *string = (char*)calloc(fsize + 1, sizeof(char));
rewind(PPLFile);
fread(string, sizeof(char), fsize, PPLFile);
return string;
}
void write_file(char *basePath, char *fileContent) {
FILE* PPLBackup;
char* fileSuffix = ".backup";
char* PPLBackupLocation = (char*)calloc(strlen(basePath) + strlen(fileSuffix) + 1, sizeof(char));
strcpy(PPLBackupLocation, basePath);
strcat(PPLBackupLocation, fileSuffix);
PPLBackup = fopen(PPLBackupLocation, "w");
fwrite(fileContent, fsize, 1, PPLBackup);
fclose(PPLBackup);
free(PPLBackupLocation);
}
多个问题
-
正如@Klas Lindbäck&@Medinoc所提到的,不要使用
fprintf()
。fwrite()
好多了。 -
@Klas Lindbäck所说,请确保以二进制模式打开文件读取和写入
fopen(PPLBackupLocation, "wb")
。 -
应评估
fread()
的返回值,如果良好,则用于fwrite()
。 -
calloc(fsize + 1, sizeof(char))
不需要添加 1。