1

So I'm writing a practice program that will take integers as input from stdin, load them into an array, sort the array, and then output the results.

I've been struggling trying to figure out how IO in C works. Here's what I have so far, please let me know if you see any issues/have any suggestions. Ideally I'd like to avoid the use of a buffer, but I can't seem to find another way to allow variably sized input

Input format: "10 20 30 11 666 1 235" ... etc

    // check if an input file is specified
    // If one is specified, then sort those numbers, otherwise pull from stdin
    if(inputFile == "stdin"){

            // Load stdin into a buffer
            char buffer[100];
            if(fgets(buffer, sizeof(buffer), stdin) == NULL) {
                    Handle_EOForIOError();
            }


            // Get a count of the numbers to create the array
            int numIntegers = 0;
            int num;
            while(sscanf(&buffer[0], "%d ", &num) == 1){
                    numIntegers++;
            }


            // Initialize the array with the proper size
            numbers = (int*)malloc(numIntegers*sizeof(int));

            // Load the integers into the array
            int i = 0;
            while(sscanf(&buffer[0], "%d ", &numbers[i]) == 1){
                    i++;
            }

    }
user3386109
  • 34,287
  • 7
  • 49
  • 68
JayB
  • 397
  • 6
  • 21

1 Answers1

2

The problem is that sscanf doesn't keep a pointer into the buffer. So every time the code calls sscanf it's just getting the first number from the buffer. The result is an infinite loop.

To fix the problem, you can use the %n conversion specifier. %n returns the number of characters that were used by the conversions. That number (which I call delta in the code below) can be used to update an index into the buffer. Here's how it works, each call to sscanf returns a delta. That delta is added to an index, and the index is used in the expression &buffer[index] to point to the next number in the buffer.

Here's what the resulting code look like

// Load stdin into a buffer
char buffer[100];
if(fgets(buffer, sizeof(buffer), stdin) == NULL)
    Handle_EOForIOError();

// Get a count of the numbers to create the array
int numIntegers = 0;
int index = 0;
int num, delta;
while(sscanf(&buffer[index], "%d%n", &num, &delta) == 1)
{
    numIntegers++;
    index += delta;
}

// Initialize the array with the proper size
int *numbers = malloc(numIntegers*sizeof(int));

// Load the integers into the array
index = 0;
for ( int i = 0; i < numIntegers; i++ )
{
    if (sscanf(&buffer[index], "%d%n", &numbers[i], &delta) != 1)
        ComplainBitterlyAndExit();
    index += delta;
}
user3386109
  • 34,287
  • 7
  • 49
  • 68
  • Do you know of a way to use fgets or something like it in a dynamic way? without a buffer? – JayB Feb 05 '15 at 07:31
  • @JayB With `fgets`, you always need a buffer (I typically use a 2000 byte buffer). You can certainly `malloc` the buffer if you like. Is there any particular reason for not wanting to have a buffer? – user3386109 Feb 05 '15 at 08:30
  • Other functions to consider are `getline` and `fgetln`, but I'm not sure how portable those are. – user3386109 Feb 05 '15 at 08:36
  • I'm writing something that could potentially have to handle up to 1,000,000 integers through stdin, so keeping a 4MB buffer at all times just seems like a waste unless its needed – JayB Feb 05 '15 at 08:39
  • @JayB Are they all on one line, or can the input be broken into reasonably sized lines? – user3386109 Feb 05 '15 at 08:47
  • @JayB I'm afraid I'm not sure what the ultimate goal is. Memory is cheap and plentiful. You could easily `malloc` a 10MB buffer, fill it with `fgets` and then `free` it when you're done with it. But without knowing what you intend to do with all those numbers, it's hard to give a specific answer. – user3386109 Feb 05 '15 at 09:11