Вы захотите использовать realloc
для расширения входного буфера, хотя вы не захотите делать это для каждого отдельного символа (это относительно дорогая операция и может привести к перемещению строки в памяти) . Распространенный трюк состоит в том, чтобы удвоить размер буфера по мере того, как вы достигнете его конца, чтобы при чтении символов размер буфера увеличивался с 16 до 32 до 64, et c., Минимизируя количество вызовов realloc
. Компромисс - небольшая внутренняя фрагментация - вы можете сохранить 65 символов в 128-символьном буфере. Но в среднем это не должно быть большой проблемой. Вот пример:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define START_SIZE 16 // some size that should handle most cases
/**
* Get the next line from the specified input stream. Return characters up to
* (but not including) the next newline character or EOF. Return the size of the
* allocated buffer as well.
*/
char *getline( FILE *stream, size_t *size )
{
*size = START_SIZE;
size_t i = 0;
/**
* Initial allocation, buf can store a string up to START_SIZE - 1 characters.
* If initial allocation fails, return NULL.
*/
char *buf = malloc( sizeof *buf * *size );
if ( !buf )
{
fprintf( stderr, "Failure to allocate initial buffer\n" );
return NULL;
}
/**
* Read from the input stream until we see a newline or EOF. Newline will
* *not* be stored in the returned string.
*/
for ( int c = fgetc( stream ); c != '\n' && c != EOF; c = fgetc( stream ))
{
/**
* Have we hit the end of the input buffer yet (allowing for the terminator)?
*/
if ( i + 1 == *size )
{
/**
* Yes. Double the size of the buffer using realloc.
* If realloc cannot satisfy the request, it will return
* NULL and leave the contents of buf unchanged. Therefore,
* we want to make sure we assign the result to
* a temporary variable and check it, otherwise we
* could potentially lose our reference to the
* previously allocated memory, leading to a memory leak.
*/
char *tmp = realloc( buf, sizeof *buf * (*size * 2));
if ( tmp )
{
buf = tmp;
*size *= 2;
}
else
{
fprintf( stderr, "Unable to extend buf, returning what we have so far\n");
return buf;
}
}
buf[i++] = c;
buf[i] = 0; // zero terminate the string as we go
}
return buf;
}
int main( void )
{
size_t bufsize;
printf( "Gimme a string: ");
char *str = getline( stdin, &bufsize );
printf( "You entered: \"%s\"\n", str );
printf( "length = %zu, buffer size = %zu\n", strlen( str ), bufsize);
free( str );
return 0;
}
И несколько примеров запускаются:
john@marvin:~/Development/getline$ gcc -o getline -std=c11 -pedantic -Wall -Werror getline.c
john@marvin:~/Development/getline$ ./getline
Gimme a string: this
You entered: "this"
length = 4, buffer size = 16
john@marvin:~/Development/getline$ ./getline
Gimme a string: this is a test
You entered: "this is a test"
length = 14, buffer size = 16
john@marvin:~/Development/getline$ ./getline
Gimme a string: this is a test of
You entered: "this is a test of"
length = 17, buffer size = 32
john@marvin:~/Development/getline$ ./getline
Gimme a string: this is a test of the emergency broadcast system.
You entered: "this is a test of the emergency broadcast system."
length = 49, buffer size = 64
john@marvin:~/Development/getline$ ./getline
Gimme a string: this is a test of the emergency broadcast system. in the event of an actual emergency, you would be dead by now.
You entered: "this is a test of the emergency broadcast system. in the event of an actual emergency, you would be dead by now. "
length = 115, buffer size = 128