我只是一名年轻的计算机科学专业的学生,目前我对从 stdin 读取字符串的最佳做法感到有些困惑。我知道有很多方法可以做到这一点,有些比其他更安全,等等...... 我目前需要一个防止缓冲区溢出的函数,并在字符串的末尾添加一个空终止符(\ 0)。我发现 fgets 非常有用,但是......它会停止用\ n或EOF读取!如果我希望用户一次输入多行,该怎么办?还有其他功能可以帮助我做到这一点吗? 对不起,如果这个问题对你们中的某些人来说似乎很愚蠢,但请理解我! 任何帮助将不胜感激。
答案 0 :(得分:4)
#define INITALLOC 16 /* #chars initally alloced */
#define STEP 8 /* #chars to realloc by */
#define END (-1) /* returned by getline to indicate EOF */
#define ALLOCFAIL 0 /* returned by getline to indicate allocation failure */
int getline(char **dynline)
{
int i, c;
size_t nalloced; /* #chars currently alloced */
if ((*dynline = malloc(INITALLOC)) == NULL)
return ALLOCFAIL;
nalloced = INITALLOC;
for (i = 0; (c = getchar()) != EOF; ++i) {
/* buffer is full, request more memory */
if (i == nalloced)
if ((*dynline = realloc(*dynline, nalloced += STEP)) == NULL)
return ALLOCFAIL;
/* store the newly read character */
(*dynline)[i] = c;
}
/* zero terminate the string */
(*dynline)[i] = '\0';
if (c == EOF)
return END;
return i+1; /* on success, return #chars read successfully
(i is an index, so add 1 to make it a count */
}
此函数动态分配内存,因此调用者需要free
内存。
此代码并不完美。如果在重新分配时出现故障,NULL
会覆盖以前的完美数据,导致内存泄漏和数据丢失。
答案 1 :(得分:3)
如果遇到换行符并且fgets
返回,则可以根据需要多次再次运行它,以便根据需要读取多行。循环对此很有用。
如果遇到EOF,你已到达文件的末尾(/ stream)并且没有必要再次运行它,因为没有什么可以阅读。
显示从stdin读取整个字符串到EOF的逻辑示例如下。
有很多方法可以做到这一点,这只是一个,但它显示了一般逻辑。
结果缓冲区随着输入的读取而增长,并且没有界限 - 所以如果永远不会达到EOF,最终会耗尽内存,程序将退出。一个简单的检查可以避免这种情况,或者根据您的应用程序,您可以处理数据,而不需要存储数据。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LINE_BUFFER_SIZE 256
// Each time this is exhausted, the buffer will be increased in size by this amount again.
#define INITIAL_BUFFER_SIZE 2048
int main (int argc, char **argv) {
char *result = malloc(INITIAL_BUFFER_SIZE);
if (!result) {
// Out of memory.
return 1;
}
size_t totalBytesRead = 0;
size_t bytesAllocated = INITIAL_BUFFER_SIZE;
char buf[LINE_BUFFER_SIZE];
while (fgets(buf, LINE_BUFFER_SIZE, stdin)) {
size_t bytesRead = strlen(buf);
size_t bytesNeeded = totalBytesRead + bytesRead + 1;
if (bytesAllocated < bytesNeeded) {
char *newPtr = realloc(result, bytesAllocated + INITIAL_BUFFER_SIZE);
if (newPtr) {
result = newPtr;
bytesAllocated += INITIAL_BUFFER_SIZE;
}
else {
// Out of memory.
free(result);
return 1;
}
}
memcpy(result + totalBytesRead, buf, bytesRead);
totalBytesRead += bytesRead;
}
result[totalBytesRead] = '\0';
// result contains the entire contents from stdin until EOF.
printf("%s", result);
free(result);
return 0;
}
答案 2 :(得分:1)
在POSIX系统上,您有getline。它能够在堆分配的内存中读取任意宽的行(直到耗尽资源)。
你也可以反复拨打fgetc ...(顺便说一下,你应该为你确定正好什么是字符串)
在Linux上,您可以使用tty从终端读取可编辑行(即stdin
时为GNU readline)。
要阅读某些类型的字符串,您可以使用fscanf,例如%50s
或%[A-Z]
等......
您可以使用fread
读取数组(字节或其他二进制数据)您可以稍后阅读整行和parse(也许使用sscanf
)。您可以阅读多行并在heap memory中构建一些字符串(例如,在拥有它的系统上使用asprintf或strdup)。