C语言feof+fgets循环重复计数门禁记录问题求助
门禁记录重复计数问题排查与解决
我有一份2020年办公室门禁记录文本文件,约2万行,每条记录格式固定:
- 索引[0-3]:无效数据
- 索引[4-9]:Badge ID
- 索引[10]:进出标识(0进1出)
- 索引[11-18]:日期
- 索引[19-22]:时分
- 索引[23-26]:Gate ID
编写了C语言代码统计指定Badge ID的出门(entry=1)次数,但结果出现重复计数——比如统计Badge ID 002341时得到342次,实际应为171次。原代码如下:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define NUMLEN 27 int main () { printf("\n-- 32bit version\n"); int entriesCounter = 0; char buff[NUMLEN]; char requiredBadge[7]; char badge[7]; char entry[2]; char day[3]; char month[3]; char year[5]; char hours[3]; char minutes[3]; char gate[4]; FILE *fp; fp = fopen("Storico2020.txt", "r"); if (fp == NULL) { printf("Error open"); exit(1); } printf("\nInsert ID badge for counting accesses: "); scanf("%s", requiredBadge); while(!feof(fp)) { fgets(buff, NUMLEN, fp); // example -> init:0000 | badge:000352 | entry:1 | data:01012019 | time:0030 | gate:023 strncpy(badge, buff+4, 6); badge[6] = '\0'; strncpy(entry, buff+10, 1); entry[1] = '\0'; strncpy(day, buff+11, 2); day[2] = '\0'; strncpy(month, buff+13, 2); month[2] = '\0'; strncpy(year, buff+15, 4); year[4] = '\0'; strncpy(hours, buff+19, 2); hours[2] = '\0'; strncpy(minutes, buff+21, 2); minutes[2] = '\0'; strncpy(gate, buff+23, 3); gate[3] = '\0'; if (strcmp(requiredBadge, badge) == 0 && strcmp(entry, "1") == 0) { printf("\nBadge: %s | in date: %s/%s/%s | gate: %s | hour: %s:%s", badge, day, month, year, gate, hours, minutes); entriesCounter++; } } fclose(fp); printf("\n********** TOTAL ACCESSES OF BADGE ID %s: %d ***************\n" ,requiredBadge, entriesCounter); system("PAUSE"); return 0; }
问题根源
while(!feof(fp))循环条件错误:feof()仅在尝试读取并到达文件末尾时返回真,导致最后一行有效数据读取完成后,循环会额外执行一次。此时fgets()读取失败,buff保留上一次的内容,最后一条符合条件的记录会被重复统计。- 缓冲区长度不足:代码中
NUMLEN定义为27,但注释里的示例行长度远超过27。fgets()每次只能读取26个字符(需预留1位给终止符\0),剩余内容留在输入缓冲区,下一次循环继续读取同一行的剩余部分,同一条记录被拆分成多次处理,最终计数翻倍。
解决步骤
- 修正循环条件:将
while(!feof(fp))改为while(fgets(buff, sizeof(buff), fp) != NULL),当fgets()读取失败(到达末尾或出错)时直接退出循环,避免重复处理。 - 增大缓冲区:将
NUMLEN调整为足够容纳完整行的数值,比如#define NUMLEN 100,确保能读取整条记录。 - 可选验证:检查
buff最后一个字符是否为\n,若不是则说明该行被截断,可添加跳过或报错逻辑。
修正后的代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define NUMLEN 100 // 增大缓冲区以容纳完整记录行 int main () { printf("\n-- 32bit version\n"); int entriesCounter = 0; char buff[NUMLEN]; char requiredBadge[7]; char badge[7]; char entry[2]; char day[3]; char month[3]; char year[5]; char hours[3]; char minutes[3]; char gate[4]; FILE *fp; fp = fopen("Storico2020.txt", "r"); if (fp == NULL) { printf("Error open"); exit(1); } printf("\nInsert ID badge for counting accesses: "); scanf("%s", requiredBadge); // 用fgets返回值判断读取是否成功,替代feof循环 while(fgets(buff, NUMLEN, fp) != NULL) { strncpy(badge, buff+4, 6); badge[6] = '\0'; strncpy(entry, buff+10, 1); entry[1] = '\0'; strncpy(day, buff+11, 2); day[2] = '\0'; strncpy(month, buff+13, 2); month[2] = '\0'; strncpy(year, buff+15, 4); year[4] = '\0'; strncpy(hours, buff+19, 2); hours[2] = '\0'; strncpy(minutes, buff+21, 2); minutes[2] = '\0'; strncpy(gate, buff+23, 3); gate[3] = '\0'; if (strcmp(requiredBadge, badge) == 0 && strcmp(entry, "1") == 0) { printf("\nBadge: %s | in date: %s/%s/%s | gate: %s | hour: %s:%s", badge, day, month, year, gate, hours, minutes); entriesCounter++; } } fclose(fp); printf("\n********** TOTAL ACCESSES OF BADGE ID %s: %d ***************\n" ,requiredBadge, entriesCounter); system("PAUSE"); return 0; }
内容的提问来源于stack exchange,提问作者sirducas
相关产品推荐
相关产品推荐

