如何在不询问用户字符串大小的情况下仅使用 getchar 将可变大小的字符串作为输入?
How do I take a variable sized string as input using only getchar without asking user for string size before?
我只需要使用 getchar()
和 malloc()
收集用户输入,将其存储在一个字符串中(其大小未知)。我以前做过,但忘了我是怎么做到的,现在我的字符串有问题,只打印第一个字母,这意味着我的 get_string 函数没有从标准输入或指针没有指向它或者它只是没有用 printf 正确打印。
char *get_string(void);
int main(void)
{
printf("Input string: ");
char *p = get_string();
printf("Output string: %s\n", p);
}
char *get_string(void)
{
int c = 0;
char *str = NULL;
char *buff = NULL;
for(int i = 0; c != '\n'; i++)
{
if(i > 0) // skips on first iteration (no char collected yet)
{
if(i > 1) // skips on second iteration (1st char collected)
{
buff = malloc(i + 1);
for(int j = 0; j < i - 1; j++)
buff[j] = str[j];
free(str);
}
str = malloc(i + 1); // allocate space for string
if(i > 1) // no need to copy string from buffer
{
for(int j = 0; j < i - 1; j++)
str[j] = buff[j];
free(buff);
}
str[i - 1] = c; // place char into string
str[i] = '[=10=]'; // terminate string with '[=10=]'
printf("%s\n", str); // print contents on each iteration
}
c = getchar();
}
return (str);
}
如果我 运行 在 main 中使用返回的字符串进行 printf,则不会打印任何内容。如果我 运行 循环内的 printf 它只在第一次迭代(第一个字母)上打印。
我得到的:
$ > gcc get_string.c -o get_string
$ > ./get_string
Input string: Hello World!
H
Output string:
我的期望:
$ > gcc get_string.c -o get_string
$ > ./get_string
Input string: Hello World!
H
He
Hel
Hell
Hello
...
Output string: Hello World!
此外,如果您知道更好(和更短)的方法来解决这个问题,请分享。
我认为这是你需要做的:
char *get_string( )
{
char* buffer = (char*)malloc(sizeof(char));
char c;
int size = 0;
c = getc(stdin);
buffer[size++] = c;
while( c != '\n')
{
c = getc(stdin);
buffer = (char*)realloc(buffer, (size+1)*sizeof(char));
if(buffer != NULL) // Check if space was re allocated
buffer[size++] = c;
else // If re allocation failed
return NULL;
}
return buffer;
}
您首先创建一个大小为 1 的缓冲区并从标准输入读取第一个字符。
然后,白色下一个字符不是 \n
:
读取下一个字符。
为下一个字符重新分配space(注意如果重新分配失败,realloc
可以returnNULL
,你必须检查) .
将当前字符添加到缓冲区。
你会想要使用 realloc
来扩展输入缓冲区,尽管你不想对每个单独的字符都这样做(这是一个相对昂贵的操作,并且可能导致字符串被移动在内存中)。一个常见的技巧是在到达缓冲区末尾时将缓冲区的大小加倍,这样当您读取字符时,缓冲区大小从 16 变为 32 再到 64,等等,从而最大限度地减少 realloc
调用的次数。权衡是一个小的内部碎片——你可能会在 128 个字符的缓冲区中存储 65 个字符。但平均而言,这应该不是什么大问题。这是一个例子:
#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
我只需要使用 getchar()
和 malloc()
收集用户输入,将其存储在一个字符串中(其大小未知)。我以前做过,但忘了我是怎么做到的,现在我的字符串有问题,只打印第一个字母,这意味着我的 get_string 函数没有从标准输入或指针没有指向它或者它只是没有用 printf 正确打印。
char *get_string(void);
int main(void)
{
printf("Input string: ");
char *p = get_string();
printf("Output string: %s\n", p);
}
char *get_string(void)
{
int c = 0;
char *str = NULL;
char *buff = NULL;
for(int i = 0; c != '\n'; i++)
{
if(i > 0) // skips on first iteration (no char collected yet)
{
if(i > 1) // skips on second iteration (1st char collected)
{
buff = malloc(i + 1);
for(int j = 0; j < i - 1; j++)
buff[j] = str[j];
free(str);
}
str = malloc(i + 1); // allocate space for string
if(i > 1) // no need to copy string from buffer
{
for(int j = 0; j < i - 1; j++)
str[j] = buff[j];
free(buff);
}
str[i - 1] = c; // place char into string
str[i] = '[=10=]'; // terminate string with '[=10=]'
printf("%s\n", str); // print contents on each iteration
}
c = getchar();
}
return (str);
}
如果我 运行 在 main 中使用返回的字符串进行 printf,则不会打印任何内容。如果我 运行 循环内的 printf 它只在第一次迭代(第一个字母)上打印。
我得到的:
$ > gcc get_string.c -o get_string
$ > ./get_string
Input string: Hello World!
H
Output string:
我的期望:
$ > gcc get_string.c -o get_string
$ > ./get_string
Input string: Hello World!
H
He
Hel
Hell
Hello
...
Output string: Hello World!
此外,如果您知道更好(和更短)的方法来解决这个问题,请分享。
我认为这是你需要做的:
char *get_string( )
{
char* buffer = (char*)malloc(sizeof(char));
char c;
int size = 0;
c = getc(stdin);
buffer[size++] = c;
while( c != '\n')
{
c = getc(stdin);
buffer = (char*)realloc(buffer, (size+1)*sizeof(char));
if(buffer != NULL) // Check if space was re allocated
buffer[size++] = c;
else // If re allocation failed
return NULL;
}
return buffer;
}
您首先创建一个大小为 1 的缓冲区并从标准输入读取第一个字符。
然后,白色下一个字符不是 \n
:
读取下一个字符。
为下一个字符重新分配space(注意如果重新分配失败,
realloc
可以returnNULL
,你必须检查) .将当前字符添加到缓冲区。
你会想要使用 realloc
来扩展输入缓冲区,尽管你不想对每个单独的字符都这样做(这是一个相对昂贵的操作,并且可能导致字符串被移动在内存中)。一个常见的技巧是在到达缓冲区末尾时将缓冲区的大小加倍,这样当您读取字符时,缓冲区大小从 16 变为 32 再到 64,等等,从而最大限度地减少 realloc
调用的次数。权衡是一个小的内部碎片——你可能会在 128 个字符的缓冲区中存储 65 个字符。但平均而言,这应该不是什么大问题。这是一个例子:
#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