C语言质数查找程序异常:fread读取长度大于fwrite写入长度
解决二进制文件写入/读取时文件大小非8倍数的异常问题
问题描述
我编写了一个质数查找程序,每完成100次迭代后,将新找到的质数以long long类型(8字节)写入文件。但多次运行程序后出现异常:读取到的文件长度不是8的倍数(比如出现3073字节的情况),触发了自定义的文件校验错误。运行环境为Windows 10,初始无目标文件,尝试过修改fopen模式为"ab+",最终问题由@user3121023解决。
完整代码
主文件(main.c)
#include <stdio.h> #include <string.h> #include <stdlib.h> long long *loadPrimeNums(char *fileName); void putPrimeNumsInList(long long *primeNums); void addIntNode(void); void initialize(void); void DestroyIntNodes(void); void writeInFile(void); void run(void); int main () { initialize(); putPrimeNumsInList(loadPrimeNums("primeNums")); run(); writeInFile(); DestroyIntNodes(); _fcloseall(); return 0; }
实现文件(prime.c)
#include <stdio.h> #include <stdlib.h> struct longLongNode { long long value; struct longLongNode *next; }; long long *loadPrimeNums(char *fileName); void putPrimeNumsInList(long long *primeNums); void addLongLongNode(void); void initialize(void); void DestroyIntNodes(void); void writeInFile(void); void run(void); FILE *primeNums_fp; long long currentOffset; struct longLongNode *head, *tail, *lastOldPrimeNum; void run() { struct longLongNode *current; current = head; int isPrime = 1; for (int i = 0; i < 100; i++) { while (current != NULL) { if (currentOffset % current->value == 0) { isPrime = 0; break; } current = current->next; } if (isPrime == 0) { isPrime = 1; } else { addLongLongNode(); } currentOffset++; current = head; } } void writeInFile() { int numberOfNewPrimes = 0; long long *newPrimes; struct longLongNode *current; current = lastOldPrimeNum; while (current->next != NULL) { numberOfNewPrimes++; current = current->next; } if (numberOfNewPrimes > 0) { current = lastOldPrimeNum->next; newPrimes = (long long *)malloc(sizeof *newPrimes * numberOfNewPrimes); for (int i = 0; i < numberOfNewPrimes; i++) { newPrimes[i] = current->value; current = current->next; } fseek(primeNums_fp, 0L, 2); fwrite(newPrimes, sizeof *newPrimes, numberOfNewPrimes, primeNums_fp); printf("Freeing newPrimes: The array that is used to write the new prime numbers to file.\n"); free(newPrimes); } } void initialize() { head = (struct longLongNode *)malloc(sizeof *head); head->value = 2; head->next = NULL; lastOldPrimeNum = head; } void addLongLongNode() { struct longLongNode *temp; temp = (struct longLongNode *)malloc(sizeof *temp); temp->value = currentOffset; temp->next = NULL; tail->next = temp; tail = temp; } long long *loadPrimeNums(char *fileName) { int num; long long *ret; // 关键修改:使用"ab+"二进制模式打开文件 primeNums_fp = fopen("primeNums", "ab+"); fseek(primeNums_fp, 0L, 2); if (ftell(primeNums_fp) != 0) { if (ftell(primeNums_fp) % sizeof *ret == 0) { num = ftell(primeNums_fp) / sizeof *ret; ret = (long long *)malloc(sizeof *ret * (num + 1)); rewind(primeNums_fp); fread(ret, sizeof *ret, num, primeNums_fp); ret[num] = -1; return ret; } else if (ftell(primeNums_fp) != -1) { printf("ERROR!!! ftell(fp): %lld", ftell(primeNums_fp)); _fcloseall(); exit(0); } } return NULL; } void putPrimeNumsInList(long long *primeNums) { struct longLongNode *current, *temp; current = head; tail = head; if (primeNums != NULL) { for (int i = 0; primeNums[i] != -1; i++) { temp = (struct longLongNode *)malloc(sizeof *temp); temp->value = primeNums[i]; temp->next = NULL; current->next = temp; current = temp; } tail = current; lastOldPrimeNum = current; } currentOffset = current->value + 1; printf("Freeing primeNums: The array that contains the initially loaded prime numbers of the file.\n"); free(primeNums); } void DestroyIntNodes() { struct longLongNode *current, *next; current = head; for (int i = 1; current != NULL; i++) { next = current->next; printf("%d: Freeing %lld\n", i, current->value); free(current); current = next; } }
问题原因
Windows系统中,fopen使用"a+"模式时默认是文本模式,文本模式会自动对换行符进行转换:写入时将\n(0x0A)替换为\r\n(0x0D0A),读取时反向转换。而程序写入的是long long类型的二进制数据,若数据中恰好包含值为0x0A的字节,就会被额外插入一个0x0D字节,导致文件大小不再是8的倍数,触发校验错误。
解决方案
将文件打开模式从"a+"改为"ab+",明确指定二进制模式打开文件。二进制模式下系统不会对文件内容做任何转换,保证long long类型数据(8字节)的写入和读取完整性,文件大小始终保持8的倍数。
内容的提问来源于stack exchange,提问作者mkdrive2
相关产品推荐
相关产品推荐

