首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C语言中无缘无故地循环使用feof条件和fget进行两次详细说明?

在C语言中无缘无故地循环使用feof条件和fget进行两次详细说明?
EN

Stack Overflow用户
提问于 2022-10-18 11:28:42
回答 2查看 52关注 0票数 1

我有一个文本文件,它有很多记录(大约20k行),编写如下:

代码语言:javascript
复制
00000000090020120200728012
00000010520020120200729012
00000002740020120200729012
00000002710020120200736012
00000001601020120200755012
00000002870020120200758012
00000010690020120200753013
00000001760020120200800013
00000008480020120200800013
00000009370020120200733014
00000001500020120200739014
00000012400020120200743014
00000008720020120200715015
00009100570020120200734017
00000002060020120200734017
00000002050020120200734017
00000003670020120200734017

这些记录包含2020年访问办公室的数据信息,每个记录都可以用这种结构读取(我将以第一行为例):

按字符读取字符串时,记录按以下方式拆分:

  • 0000 (索引0-3 ->无用数据)
  • 000009(索引4-9 -> Badge ID)
  • 0 (索引10 ->用作布尔值,0表示内部访问,1表示退出)
  • 02012020(索引11-18 ->日期格式)

h 1100728(索引19-23 ->小时和访问的分钟)h 211H 112012(索引24-26 -> )h 213f 214

我编写了以下代码,用于计算指定门上指定的徽章ID的记录(在我的例子中,我只需要读取001,002,003门):

代码语言:javascript
复制
#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;
}

但是,这里的问题是,它计算了每一次发现两次的记录,我真的不知道为什么。我输入的条件也是输入== 1,因为我需要在办公室的入口数一次,而不是出口。你能帮帮我吗?例如,如果你数ID徽章002341,它会计算342次访问,但它只需要计算一半。请救救我!

EN

回答 2

Stack Overflow用户

发布于 2022-10-18 13:22:30

问题是缓冲区太小,fgets无法在一次调用中读取所有数字和换行符。fgets最多只读取NUMLEN - 1字符。#define NUMLEN 27允许fgets在一次呼叫中读取数字,但没有空间读取换行符。换行符在第二个fgets调用中读取。使用#define NUMLEN 30将提供足够大的缓冲区,以便在一个fgets调用中读取数字和换行符。

使用!feof(fp)作为while条件可能是一个问题。当读取最后一行时,没有错误,所以循环再次迭代。相反,使用fgets作为while条件。

考虑使用sscanf提取字段。%2s将扫描最多两个字符。

代码语言:javascript
复制
#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;
}
票数 1
EN

Stack Overflow用户

发布于 2022-10-18 13:25:07

您需要更加小心地验证输入。总是(总是)检查扫描返回的值。而且,虽然下面的代码不是我如何做到的,但看到您任意地复制所有这些数据,我感到很痛苦。你不需要这么做。如果您复制数据仅仅是因为您想拥有以空结尾的字符串,但不想破坏缓冲区,则应该重新考虑。例:

代码语言:javascript
复制
#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
*/

请注意,这会稍微调整接口,并将所需的徽章读入命令行参数,而不是从输入流中获取它。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74110214

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档