C'deki dosyadan bir satır okuyun ve girdi sayısını çıkarın

Aug 17 2020

Bir dosya var input.dat . Bu dosyada 3 satır vardır:

1 2 3
5 7 10 12
8 9 14 13 15 17

C'yi kullanarak üç satırdan birini okuyacağım ve elemanların sayısını döndüreceğim. Örneğin, 2. satırı 5 7 10 12hafızaya okumak ve 2. satırdaki değerlerin sayısını da döndürmek istiyorum 4. Kodum aşağıda ...

#include <stdio.h>
#include <stdlib.h>

#define STRING_SIZE 2000

int main() {
    FILE *fp = fopen("in.dat", "r");
    char line[STRING_SIZE];
    int lcount = 0, nline = 1, sum = 0, number;

    if (fp != NULL) {
        while (fgets(line, STRING_SIZE, fp) != NULL) {
            if (lcount == nline) {
                while (sscanf(line, "%d ", &number)) {
                    sum++;
                }
                break;
            } else {
                lcount++;
            }
        }
        fclose(fp);
    }
    exit(0);
}

Bu kodu çalıştırdığımda, asla bir ölü döngü gibi durmuyor. Buradaki sorun nedir?

Yanıtlar

1 chqrlie Aug 17 2020 at 19:57

döngü while (sscanf(line, "%d ", &number)), satırdaki ilk sayıyı çözümlemeye devam eder.

Bunun strtolyerine şunu kullanmalısınız :

#include <stdio.h>
#include <stdlib.h>

#define STRING_SIZE 2000

int main() {
    FILE *fp = fopen("in.dat", "r");
    char line[STRING_SIZE];
    int lcount = 0, nline = 1;

    if (fp != NULL) {
        while (fgets(line, STRING_SIZE, fp) != NULL) {
            if (lcount == nline) {
                char *p = line, *q;
                int count = 0;
                for (;;) {
                    long val = strtol(p, &q, 0);    // parse an integer
                    if (q == p) {
                        // end of string or not a number
                        break;
                    }
                    // value was read into val. You can use it for whatever purpose
                    count++;
                    p = q;
                }
                printf("%d\n", count);
                break;
            } else {
                lcount++;
            }
        }
        fclose(fp);
    }
    return 0;
}
1 DavidC.Rankin Aug 17 2020 at 22:05

Doğru yolda düşünüyordunuz sscanf(), bulmacanın eksik olan tek parçası, bir linesonraki aramanızla satırdaki bir sonraki değeri okuyabilmeniz için bir ofsetin nasıl uygulanacağıdır sscanf(). Bunu sscanf(), "%n"dönüşümü kullanmak için her çağrıda tüketilen karakterlerin sayısını takip ederek yaparsınız (bu, tarafından döndürülen dönüşüm sayınıza eklenmez sscanf()) Örneğin, açık dosya akışından satır okumak fp, şunları yapabilirsiniz:

#define MAXC  1024      /* if you need a constant, #define one (or more) */
...
    char line[MAXC] = "";   /* buffer to hold each line */
    ...
    while (fgets (line, MAXC, fp)) {    /* reach each line in file */
        int offset = 0,                 /* offset in line for next sscanf() read */
            nchr = 0,                   /* number of char consumed by last read */
            val,                        /* integer value read with sscanf() */
            nval = 0;                   /* number of values read in line */
        /* conververt each integer at line + offset, saving no. of chars consumed */
        while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
            printf (" %d", val);        /* output value read */
            offset += nchr;             /* update offset with no. chars consumend */
            nval++;                     /* increment value count */
        }
        printf ("  -  %d values\n", nval);  /* output no. values in line */
    }

( Not: başarısız bir dönüştürmeden strtol()daha iyi hata raporlama sağlar sscanf())

Bunu, programa ilk argüman olarak sağlanan dosya adından okuyan (veya stdinherhangi bir argüman verilmemişse varsayılan olarak okuyan) bir örnekle bir araya getirirseniz, şunları yapabilirsiniz:

#include <stdio.h>

#define MAXC  1024      /* if you need a constant, #define one (or more) */

int main (int argc, char **argv) {

    char line[MAXC] = "";   /* buffer to hold each line */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (line, MAXC, fp)) {    /* reach each line in file */
        int offset = 0,                 /* offset in line for next sscanf() read */
            nchr = 0,                   /* number of char consumed by last read */
            val,                        /* integer value read with sscanf() */
            nval = 0;                   /* number of values read in line */
        /* conververt each integer at line + offset, saving no. of chars consumed */
        while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
            printf (" %d", val);        /* output value read */
            offset += nchr;             /* update offset with no. chars consumend */
            nval++;                     /* increment value count */
        }
        printf ("  -  %d values\n", nval);  /* output no. values in line */
    }

    if (fp != stdin)                    /* close file if not stdin */
        fclose (fp);
}

Örnek Kullanım / Çıktı

Dosya adında gösterdiğiniz verilerle şunları dat/nvals.txtelde edersiniz:

$ ./bin/fgetsnvals dat/nvals.txt
 1 2 3  -  3 values
 5 7 10 12  -  4 values
 8 9 14 13 15 17  -  6 values

Her şeyi gözden geçirin ve başka sorunuz olursa bana bildirin.

1 RobinHellmers Aug 17 2020 at 21:41

Chqrlie'nin cevabının biraz daha temiz bir versiyonu . Bir dizeyle başladı, çünkü soru aslında bundan sonra fgets().

sscanf() dizede adım atmaz, her zaman baştan okur.

strtol()long intdizenin başında, başlangıçtaki beyaz boşluğu yok sayarak bir a arar . Taramayı durdurduğu yerin adresini geri verir.

El kitabı, strtol()errno'nun herhangi bir dönüştürme hatası için kontrol edilmesi gerektiğini söylüyor.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define STRING_SIZE 2000

int main(void)
{
    char line[STRING_SIZE] = "5 7 10 12";

    char* start = line;
    char* end;

    int count = 0;

    while(1)
    {
        /**
         * strtol() look for long int in beginning of the string
         * Ignores beginning whitespace
         * 
         * start: where to strtol() start looking for long int
         * end: where strtol() stops scanning for long int
         */
        errno = 0; // As strol() manual says

        strtol(start, &end, 0);

        if (errno != 0)
        {
            printf("Error in strtol() conversion.\n");
            exit(0);
        }

        if (start == end) break; // Quit loop

        start = end;
        count++;
    }
    

    printf("count: %d\n", count);

    return 0;
}