提问人:sirducas 提问时间:10/18/2022 更新时间:10/19/2022 访问量:148
While 循环在 C 语言中具有 feof 条件和 fgets 无缘无故地详细说明了两次?
While loop in C Language with feof condition and fgets elaborate twice for no reason?
问:
我有一个文本文件,里面有很多记录(大约 20k 行),是这样写的:
00000000090020120200728012
00000010520020120200729012
00000002740020120200729012
00000002710020120200736012
00000001601020120200755012
00000002870020120200758012
00000010690020120200753013
00000001760020120200800013
00000008480020120200800013
00000009370020120200733014
00000001500020120200739014
00000012400020120200743014
00000008720020120200715015
00009100570020120200734017
00000002060020120200734017
00000002050020120200734017
00000003670020120200734017
这些记录包含 2020 年访问办公室的数据信息,每条记录都可以用这种结构读取(我将以第一行为例):
逐个字符读取字符串,记录按以下方式拆分:
- 0000(索引 [0-3] -> 无用数据)
- 000009(索引 [4-9] -> 徽章 ID)
- 0(索引 [10] -> 的作用类似于布尔值,0 表示输入 - 访问 - ,1 表示输出 - 退出 -)
- 02012020(索引 [11-18] -> 日期格式)
- 0728(索引 [19-23] -> 小时和分钟的访问)
- 012(索引 [24-26] ->仅提供有关门 ID 的信息)
我有这个代码,我写了这个代码,用于计算指定门上指定徽章 ID 的记录(在我的情况下,我只需要读取 001、002、003 门):
#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;
}
但是,这里的问题是它计算了找到 TWICE 的每一条记录,我真的不知道为什么。我在 if 条件中也输入了条目 == 1,因为我需要计算一次办公室的入口,而不是出口。 你可以帮我吗?例如,如果将 ID 徽章计算002341则它计算了 342 次访问,但它只需要计算一半。请帮帮我!
答:
问题是缓冲区太小,无法在一次调用中读取所有数字和换行符。 将读取大多数字符。 让我们在一次调用中读取数字,但没有空间读取换行符。在第二次调用中读取换行符。使用将提供足够大的缓冲区,以便在一次调用中读取数字和换行符。
用作 while 条件可能是一个问题。当读取最后一行时,没有错误,因此循环再次迭代。而是用作 while 条件。
请考虑使用 提取字段。 最多扫描两个字符。fgets
fgets
NUMLEN - 1
#define NUMLEN 27
fgets
fgets
#define NUMLEN 30
fgets
!feof(fp)
fgets
sscanf
%2s
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define NUMLEN 30
int main () {
printf("\n-- 32bit version\n");
int entriesCounter = 0;
char const *filename = "Storico2020.txt";
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 = NULL;
fp = fopen( filename, "r");
if (fp == NULL) {
perror ( filename);
exit(1);
}
printf("\nInsert ID badge for counting accesses: ");
scanf("%6s", requiredBadge);
while( fgets(buff, NUMLEN, fp)) {
// example -> init:0000 | badge:000352 | entry:1 | data:01012019 | time:0030 | gate:023
if ( 8 != sscanf ( buff + 4, "%6s%1s%2s%2s%4s%2s%2s%3s"
, badge
, entry
, day
, month
, year
, hours
, minutes
, gate)) {
fprintf ( stderr, "bad record %s\n", buff);
continue;
}
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;
}
在验证输入时需要更加小心。always(always)检查 scanf 返回的值。而且,尽管下面的代码不是我这样做的方式,但看到您肆意复制所有这些数据,我感到很痛苦。你不需要。如果复制数据只是因为想要以 null 结尾的字符串,但又不想破坏缓冲区,则应重新考虑。例如:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void
show(const char *header, const char *str, size_t len)
{
fputs(header, stdout);
fwrite(str, 1, len, stdout);
}
int
main(int argc, char **argv)
{
char buff[1024];
unsigned line = 0;
unsigned entriesCounter = 0;
const char * const badge = buff + 4;
const char * const entry = buff + 10;
const char * const day = buff + 11;
const char * const month = buff + 13;
const char * const year = buff + 15;
const char * const hours = buff + 19;
const char * const minutes = buff + 21;
const char * const gate = buff + 23;
char * const end = buff + 26;
if( argc < 2 ){
fprintf(stderr, "missing badge argument\n");
exit(EXIT_FAILURE);
}
const char *requiredBadge = argv[1];
const char *path = argc > 2 ? argv[2] : "stdin";
FILE *fp = argc > 2 ? fopen(argv[2], "r") : stdin;
if( fp == NULL ){
perror(path);
exit(1);
}
while( *end = '\0', fgets(buff, sizeof buff, fp) != NULL ){
line += 1;
if( *end != '\n' ){
fprintf(stderr, "Unexpected input in line %u\n", line);
continue;
}
if( strncmp(requiredBadge, badge, entry - badge) == 0
&& *entry == '1'
) {
printf("%5d: ", ++entriesCounter);
show("Badge: ", badge, entry - badge);
show(" | in date: ", day, month - day);
show("/", month, year - month);
show("/", year, hours - year);
show(" | gate: ", gate, end - gate);
show(" | hour: ", hours, minutes - hours);
show(":", minutes, gate - minutes);
putchar('\n');
}
}
}
/* Sample input:
00000010520020120200729012
00000002740020120200729012
00000002711020120200736012
00000002710020120200736012
00000002711020120200736012
00000001601020120200755012
00000002870020120200758012
00000010690020120200753013
00000001760020120200800013
00000008480020120200800013
00000009370020120200733014
*/
请注意,这会稍微调整界面,并将所需的徽章作为命令行参数读取,而不是从输入流中获取它。
评论