8

I need to read an input file like :

1
19 20 41 23
2
41 52 43
3
90 91 941
4
512
5

6
51 61

Each odd line is an integer. Each even line is unknown number of integers.

It is very easy in C++

while( cin >> k ){
............
}

I'm not so used to C, so I couldnt make it in C. Any ways to do it?

Telemachus
  • 19,459
  • 7
  • 57
  • 79
huhuhuuu
  • 109
  • 1
  • 1
  • 4
  • 1
    Do you have to pay attention to lines, or is this just a series of integers coming in? In your example, it looks like the odd lines are sequence numbers and the even lines represent something else. – David Thornley Feb 03 '10 at 22:09
  • david I need to pay attention to the lines. that is why I couldnt do it. I need to stop getting new values at the end of each even line. Because I have a struct with attributes id and list. each odd line is an id and each even line is a list. I want to fill a struct with 2 lines of data and move to another struct and fill it with another 2 lines of data etc... – huhuhuuu Feb 03 '10 at 22:14
  • Read in an entire line at a time and then just parse it as a string – Joe Phillips Feb 03 '10 at 22:39

6 Answers6

12

Running your input file through:

#include <stdio.h>

int main() {
        int k;
        while (scanf("%d", &k) == 1) {
                printf("read number: %d\n", k);
        }
        return 0;
}

Results in:

read number: 1
read number: 19
read number: 20
read number: 41
read number: 23
read number: 2
read number: 41
read number: 52
read number: 43
read number: 3
read number: 90
read number: 91
read number: 941
read number: 4
read number: 512
read number: 5
read number: 6
read number: 51
read number: 61

This is the C analog of the code you reference in your original question.

Sean Bright
  • 118,630
  • 17
  • 138
  • 146
10

The way I would do it is to break it down into two operations: read a line, then read the integers in that line. Here is a lazy implementation using the standard C library:

char line[1024], *p, *e;
long v;
while (fgets(line, sizeof(line), stdin)) {
    p = line;
    for (p = line; ; p = e) {
        v = strtol(p, &e, 10);
        if (p == e)
            break;
        // process v here
    }
}
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 2
    Thanks a lot. I'll try it now. But what if I cannot assume a fixed maximum line length? – huhuhuuu Feb 03 '10 at 23:24
  • 2
    What if the input stream contains more than 1024 - 2 characters and `fgets()` splits up the input stream at a numerical literal? You will end up with two numerical values instead of one. For instance, from 12345 you could get 123 in the last iteration of the for-loop and 45 in the first iteration of the for-loop after the next call of `fgets()`. Hence, I'd prefer `scanf()`and `fscanf()` for this task, as shown by Sean. – Robin Klose Nov 20 '14 at 15:55
  • 1
    @RobinKlose: The `scanf()` solution posted by Sean does not solve the problem, since it doesn't distinguish line breaks from other whitespace. – Dietrich Epp Nov 20 '14 at 18:19
  • @DietrichEpp: Ok, I see your point. The questioner needed to keep track of the lines. But do you see a simple way to get rid of the problem I've described above, though? – Robin Klose Nov 23 '14 at 22:44
  • @RobinKlose: I know of no complete, simple, and portable solutions in C. The above solution is incomplete. Using `getchar()` to parse integers without an intermediate buffer is complex. Using `getline()` is not portable. And finally, you can do this in Python pretty easily but that's not C. Perhaps the best thing to do, if you can't guarantee lines shorter than some big number, and you can't use Python instead, is to write your own version of `getline()`. – Dietrich Epp Nov 23 '14 at 23:39
  • 1
    Note that `getline()` is part of the POSIX standard, it just isn't available on Windows. – Dietrich Epp Nov 23 '14 at 23:41
3

I would break the program in different tasks.

The first step is to be able to read a pair of lines, the first line which tells you the number of numbers to read, and then the second line to read the actual numbers. For this, a function called something like read_set might be useful. It should be able to return the numbers read, and signal end of file as well as errors. For this, we can define a data structure such as:

struct numbers {
    long *data; /* or choose a type depending upon your needs */
    size_t len;
};

and then we can declare our function with the prototype:

int read_set(FILE *fp, struct numbers *num);

The function will allocate memory for num->data, and set num->len to the correct value. It returns 0 for success, and a set of error conditions otherwise. We might get fancy and use an enum for return statuses later. For now, let's say that 0 = success, 1 = end of file, and everything else is an error.

The caller then calls read_set() in a loop:

struct numbers numbers;
int status;
while ((status = read_set(fp, &numbers)) == 0) {
    /* process numbers->data, and then free it */
}
if (status == 1) {
    /* hit end of file, everything is OK */
} else {
    /* handle error */
}

For implementing read_set(): it has to read two lines. There are many implementations of reading a full line in C, so you can use any of them, and read a line first, then sscanf()/strtoul() it for one number (check its return value!). Once you have the number of numbers, n, you can read the next line in memory, and do:

num->data = malloc(n * sizeof *num->data);
num->len = n;

You can then repeatedly call sscanf() or strtol() to store numbers in num->data. You should put in checks to make sure exactly n numbers are on that line.

Note that you can write read_set() in other ways too: read a line character by character, and parse the numbers as you read them. This has the advantage of going over the data only once, and not needing a big buffer to store the whole input line in memory, but the disadvantage is doing low-level stuff yourself and reading data character-by-character may be slow.

Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
1

I would do one of:

  • fgetc() to read individual characters and parse them yourself (accumulate digits until you hit whitespace and you have an integer to convert with atoi(); if the whitespace is a newline, then it terminates a list of integers)

  • fgets() to read a line at a time, and then parse the string (again, look for whitespace separating the values) that it returns.

Jason Williams
  • 56,972
  • 11
  • 108
  • 137
1

I came up with a solution like this:

#include <stdio.h>

int main(void) 
{
    int index = 0;
    char ch;
    int arr[1024];
    
    while(scanf("%d%c", &arr[index++], &ch)!=EOF)
    {
        if(ch=='\n')
        {
            // One line is read into arr
            // number of integers in this line = index
            
            // Rest of your code which can proces arr[]
            
            // Example
            int i;
            for(i = 0; i < index; i++) 
            {
                printf("%d ", arr[i]);
            }
            printf("\n");
            
            // Set the index back to 0 for the upcoming line
            index = 0;
        }
    }
    
    return 0;
}
0

have a look at getc(3) or scanf(3)

Cogsy
  • 5,584
  • 4
  • 35
  • 47