Baca baris dari file di C dan ekstrak jumlah input

Aug 17 2020

Saya memiliki file input.dat . Di file ini, ada 3 baris:

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

Saya akan membaca salah satu dari tiga baris menggunakan C, dan mengembalikan jumlah elemen. Misalnya, saya ingin membaca baris 5 7 10 12ke -2 ke dalam memori, dan juga mengembalikan jumlah nilai di baris ke-2, yaitu 4. Kode saya ada di bawah ...

#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);
}

Ketika saya menjalankan kode ini, tidak pernah berhenti seperti loop mati. Apa masalah yang terjadi di sini?

Jawaban

1 chqrlie Aug 17 2020 at 19:57

loop while (sscanf(line, "%d ", &number))terus mem-parsing angka pertama dalam baris.

Anda harus menggunakan strtolsebagai gantinya:

#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

Anda berpikir di sepanjang jalan yang benar dengan penggunaan Anda sscanf(), satu-satunya bagian dari teka-teki yang Anda lewatkan adalah bagaimana menerapkan offset linesehingga Anda membaca nilai berikutnya di baris dengan panggilan berikutnya ke sscanf(). Anda melakukannya dengan melacak jumlah karakter yang digunakan pada setiap panggilan untuk sscanf()menggunakan "%n"konversi (ini tidak menambah jumlah konversi Anda yang dikembalikan oleh sscanf()) Misalnya membaca baris dari open file-stream fp, Anda dapat melakukan:

#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 */
    }

( Catatan: strtol() memberikan pelaporan kesalahan yang lebih baik daripada sscanf()pada konversi yang gagal)

Jika Anda menggabungkannya dengan contoh yang membaca dari nama file yang diberikan sebagai argumen pertama untuk program (atau membaca dari stdinsecara default jika tidak ada argumen yang diberikan), Anda dapat melakukan:

#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);
}

Contoh Penggunaan / Output

Dengan data yang Anda tampilkan di nama file, dat/nvals.txtAnda akan mendapatkan:

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

Periksa semuanya dan beri tahu saya jika Anda memiliki pertanyaan lebih lanjut.

1 RobinHellmers Aug 17 2020 at 21:41

Versi jawaban chqrlie yang sedikit lebih bersih . Dimulai dengan string karena itulah pertanyaan sebenarnya tentang setelahnya fgets().

sscanf() tidak akan melewati string, itu selalu membaca dari awal.

strtol()mencari a long intdi awal string, mengabaikan ruang kosong awal. Memberikan kembali alamat tempat berhenti memindai.

Manual strtol()mengatakan errno yang harus diperiksa untuk setiap kesalahan konversi.

#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;
}