सी में फ़ाइल से एक पंक्ति पढ़ें और इनपुट की संख्या निकालें

Aug 17 2020

मेरे पास एक फ़ाइल input.dat है । इस फ़ाइल में, 3 लाइनें हैं:

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

मैं सी का उपयोग करके तीन लाइनों में से एक को पढ़ने जा रहा हूं, और तत्वों की संख्या वापस करूंगा। उदाहरण के लिए, मैं दूसरी पंक्ति 5 7 10 12को मेमोरी में पढ़ना चाहता हूं , और दूसरी पंक्ति में मानों की संख्या भी लौटाता हूं , जो है 4। मेरा कोड नीचे है ...

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

जब मैं इस कोड को चलाता हूं, तो यह कभी भी मृत लूप की तरह नहीं रुकता। यहां क्या समस्या है?

जवाब

1 chqrlie Aug 17 2020 at 19:57

लूप while (sscanf(line, "%d ", &number))लाइन में पहले नंबर को पार्स करता रहता है।

आपको strtolइसके बजाय उपयोग करना चाहिए :

#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

आप अपने उपयोग के साथ सही रास्ते के बारे में सोच रहे थे sscanf(), आपके द्वारा याद की गई पहेली का एकमात्र टुकड़ा यह है कि एक ऑफसेट को कैसे लागू किया lineजाए ताकि आप अपने अगले कॉल के साथ लाइन में अगला मान पढ़ें sscanf()। आप रूपांतरण sscanf()का उपयोग करने के लिए प्रत्येक कॉल पर उपभोग किए गए वर्णों की संख्या पर नज़र रखते हुए ऐसा करते हैं "%n"(यह आपके द्वारा वापस लौटाए गए रूपांतरण संख्या में शामिल नहीं होता है sscanf()) उदाहरण के लिए, खुली फ़ाइल-स्ट्रीम से लाइनें पढ़ना fp, आप कर सकते हैं:

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

( नोट: विफल रूपांतरण की strtol()तुलना में बेहतर त्रुटि रिपोर्टिंग प्रदान करता है sscanf())

यदि आप इसे एक उदाहरण के साथ जोड़ते हैं जो प्रोग्राम के पहले तर्क के रूप में दिए गए फ़ाइलनाम से पढ़ता है (या stdinडिफ़ॉल्ट रूप से अगर कोई तर्क नहीं दिया जाता है तो पढ़ता है), आप ऐसा कर सकते हैं:

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

उदाहरण का उपयोग करें / आउटपुट

उस डेटा के साथ जिसे आप फ़ाइलनाम में दिखाते dat/nvals.txtहैं:

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

चीजों को देखें और मुझे बताएं कि क्या आपके पास और प्रश्न हैं।

1 RobinHellmers Aug 17 2020 at 21:41

Chqrlie के जवाब का एक बिट क्लीनर संस्करण । एक स्ट्रिंग के साथ शुरू किया जो कि वास्तव में प्रश्न के बाद के बारे में है fgets()

sscanf() स्ट्रिंग के माध्यम से कदम नहीं होगा, यह हमेशा शुरुआत से पढ़ता है।

strtol()long intप्रारंभिक सफेद-स्थान की अनदेखी करते हुए, स्ट्रिंग की शुरुआत में दिखता है। यह स्कैन करना बंद कर देता है, जहां का पता देता है।

मैनुअल का strtol()कहना है कि किसी भी रूपांतरण त्रुटि के लिए ग़लती से जाँच की जानी चाहिए।

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