How to read unlimited characters in C
Asked Answered
E

3

6

How to read unlimited characters into a char* variable without specifying the size?

For example, say I want to read the address of an employee that may also take multiple lines.

Eades answered 24/3, 2010 at 5:3 Comment(2)
The answers below demonstrate the mechanics of the problem, and I'd encourage you to study them One common implementation is getline.Anzovin
You must start by ensuring that your hardware has unlimited memory!Elderberry
D
8

You have to start by "guessing" the size that you expect, then allocate a buffer that big using malloc. If that turns out to be too small, you use realloc to resize the buffer to be a bit bigger. Sample code:

char *buffer;
size_t num_read;
size_t buffer_size;

buffer_size = 100;
buffer = malloc(buffer_size);
num_read = 0;

while (!finished_reading()) {
    char c = getchar();
    if (num_read >= buffer_size) {
        char *new_buffer;

        buffer_size *= 2; // try a buffer that's twice as big as before
        new_buffer = realloc(buffer, buffer_size);
        if (new_buffer == NULL) {
            free(buffer);
            /* Abort - out of memory */
        }

        buffer = new_buffer;
    }
    buffer[num_read] = c;
    num_read++;
}

This is just off the top of my head, and might (read: will probably) contain errors, but should give you a good idea.

Discophile answered 24/3, 2010 at 5:12 Comment(12)
@Codeka - you should avoid x = realloc(x, newsize); If realloc fails, you lose the original pointer and you'll leak memory. That said, one exception to this rule is it's okay if your policy on an alloc failure is to end the process.Spates
But, caution... if the realloc fails you have leaked the previous buffer pointer. One should do something like void *sav=ptr; if((ptr=realloc(ptr,newsiz))==null) { free(sav); }Ripen
Thanks guys, that's correct. I'll update my example... it's been a while since I used straight C :)Discophile
if its available, you can keep using asprintf() on the same allocated address to automatically grow while you append what was just read to what's already in the allocated buffer. I'm not posting that as an answer, as it isn't a standard function, but is widely available. It usually 'just handles' problems that would arise from realloc() failing, at least the GNU implementation does.Kienan
But still if the user enters only few characters, say you have allocated 100 bytes but the user entered only 10 bytes, remaining will be wasted right.Eades
pritviraj: You can follow it up with a realloc(buffer, num_read); call to trim the allocated buffer to the exact size required.Nonlegal
Also, mixing declarations in code isn't always portable. As its just an optimization in this case, I suggest moving *new_buffer next to *buffer's declaration. The OP did not indicate a platform or standard.Kienan
In general you don't want to abort if user input causes you to run out of memory. Most users want an error message, not losing all their data. Instead of Abort - out of memory it should say return error code.Smoothen
Is this the below code is proper: if any changes please correct it. 'int i = 0; char c; char *str = (char *)malloc(sizeof(char)); while((c = getchar()) != '\n') { str[i++] = c; if(i >= strlen(str)-1) str = (char *)realloc(str, strlen(str) + 2); } str[i] = '\0'; return str;'Eades
Sorry for posting the code in wrong format. I am new to this site please can you tell me how to post the code snippet.Eades
@Tim Post: The declaration of new_buffer is at the start of a compound statement, so it is completely portable to all versions of standard C.Nonlegal
@prithviraj: Code snippets should go within backticks (or in questions, indented by 4 spaces). That code is wrong, BTW - you cannot use strlen() for that purpose, you need to use a variable to keep track of the currently allocated size (like the buffer_size in this answer).Nonlegal
S
1

Just had to answer Ex7.1, pg 330 of Beginning C, by Ivor Horton, 3rd edition. Took a couple of weeks to work out. Allows input of floating numbers without specifying in advance how many numbers the user will enter. Stores the numbers in a dynamic array, and then prints out the numbers, and the average value. Using Code::Blocks with Ubuntu 11.04. Hope it helps.

/*realloc_for_averaging_value_of_floats_fri14Sept2012_16:30  */

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1

int main(int argc, char ** argv[])
{
    float input = 0;
    int count=0, n = 0;
    float *numbers = NULL;
    float *more_numbers;
    float sum = 0.0;

    while (TRUE)
    {
        do
        {
            printf("Enter an floating point value (0 to end): ");
            scanf("%f", &input);
            count++;
            more_numbers = (float*) realloc(numbers, count * sizeof(float));
            if ( more_numbers != NULL )
            {
                numbers = more_numbers;
                numbers[count - 1] = input;
            }
            else
            {
                free(numbers);
                puts("Error (re)allocating memory");
                exit(TRUE);
            }
        } while ( input != 0 );

        printf("Numbers entered: ");
        while( n < count )
        {
            printf("%f ", numbers[n]);  /* n is always less than count.*/
            n++;
        }
        /*need n++ otherwise loops forever*/
        n = 0;
        while( n < count )
        {
            sum += numbers[n];      /*Add numbers together*/
            n++;
        }
        /* Divide sum / count = average.*/
        printf("\n Average of floats = %f \n", sum / (count - 1));
    }
    return 0;
}

/* Success Fri Sept 14 13:29 . That was hard work.*/
/* Always looks simple when working.*/
/* Next step is to use a function to work out the average.*/
/*Anonymous on July 04, 2012*/
/* http://www.careercup.com/question?id=14193663 */
Stamper answered 14/9, 2012 at 15:56 Comment(1)
Good try! Couple of performance suggestions - try to avoid many reallocs, they involve copying everything around. Instead realloc by a factor of 2 or 4, and keep count of both the available space and the used space. Also, average can be calculated on runtime without prestoring anything.Feint
C
0

How about just putting a 1KB buffer (or 4KB) on the stack, reading into that until you find the end of the address, and then allocate a buffer of the correct size and copy the data to it? Once you return from the function, the stack buffer goes away and you only have a single call to malloc.

Clavicorn answered 24/3, 2010 at 15:57 Comment(5)
What happens when the address is bigger than the 1k or 4k buffer on the stack?Smoothen
@gabe: How do you write a 4KB address on an envelope?Clavicorn
Not knowing the size of an input string and trying to read it into a fixed-size buffer is the source of untold security problems in C code.Smoothen
@gabe: fgets has a parameter for buffer size. There are definitely functions in the Standard C Library to avoid (like gets). Using length-limited functions and fixed-size buffers seems pretty safe to me.Clavicorn
Fixed-size buffers are safe if you use I/O functions that have parameters for buffer size. The issue is what happens when the data you want doesn't fit in your buffer. The question was "how to read unlimited characters". Will the program fail because part of the address is still left sitting in the input stream?Smoothen

© 2022 - 2024 — McMap. All rights reserved.