c - 如何仅使用 getchar 将可变大小的字符串作为输入而不询问用户之前的字符串大小?
问题描述
我只需要收集用户输入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] = '\0'; // terminate string with '\0'
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!
此外,如果您知道更好(和更短)的方法来解决这个问题,请分享。
解决方案
您将希望使用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
推荐阅读
- spring - Spring DSL Solace Integration:如何设置每次轮询的最大消息
- apache - Symfony 和 NTLM - 缺少 Apache 标头授权
- javascript - 使用 FullCalendar 意外锁定滚动
- azure - 针对 .net framework 4.7.2 的应用程序能否在 .net framework 4.7.1 上安全运行?
- android - 生成签名的apk android studio、keystore 和keytool 错误
- python-3.x - tf.spectral.dct 和 scipy.fftpack.dct 是等价的吗?
- python - 定时器无法连接到pyqt5中的插槽
- python - 如何在python中对无序离散数据进行热编码?
- angular - 错误:找不到模块'@angular-cli/plugins/karma'
- php - 为什么我应该在支付请求后设置睡眠功能来获取贝宝交易 ID?