提问人:Prad 提问时间:1/28/2023 更新时间:1/28/2023 访问量:266
有什么简单的方法可以在 C 中读取可变长度的字符串吗?
Any simple way to read a string of variable length in C?
问:
我尝试使用以下方法阅读:
char *input1, *input2;
scanf("%s[^\n]", input1);
scanf("%s[^\n]", input2);
我显然做错了什么,因为第二个字符串被读取为 null。我知道不建议使用 scanf(),但我找不到任何其他简单的方法来做同样的事情。
答:
声明:
char *input1, *input2;
为指向 的两个指针分配内存。请注意,这只为该指针分配了内存,这些指针未初始化,不指向任何有意义的内容,而不是它们所指向的内容。char
然后,调用尝试越界写入内存,并导致未定义的行为。scanf()
相反,您可以声明具有自动存储持续时间的固定大小的字符数组:
char input1[SIZE];
这将为数组分配内存,并且调用将有效。scanf()
或者,您可以使用以下内存分配函数之一为指针动态分配内存:
char *input1 = malloc (size);
这将声明一个指针,其内容不确定,但会立即被指向大小为 的内存块的指针覆盖。请注意,调用可能失败。它以错误代码的形式返回,因此请检查它。char
size
malloc()
NULL
但不应用作用户输入接口。它不能防止缓冲区溢出,并且会在输入缓冲区中留下一个换行符(这会导致更多的问题)。scanf()
请考虑改用。它将以 null 结尾缓冲区并读取大多数字符。fgets
size - 1
调用 to 可以替换为:scanf()
fgets (buf, sizeof buf, stdin);
然后,您可以使用 、 等解析字符串。sscanf
strtol
请注意,如果有空格,将保留尾随换行符。您可以使用此单行来删除它:fgets()
buf [strcspn (buf, "\n\r") = '\0`;
这也照顾了回程车厢(如果有的话)。
或者,如果您希望继续使用(我建议不要这样做),请使用字段宽度来限制输入并检查 的返回值:scanf()
scanf()
scanf ("%1023s", input1); /* Am using 1023 as a place holder */
话虽如此,如果你想读取可变长度的行,你需要用 动态分配内存,然后根据需要调整它的大小。malloc()
realloc()
在符合 POSIX 标准的系统上,您可以使用它来读取任意长度的字符串,但请注意,它容易受到 DOS 攻击。getline()
有 2 种简单的方法可以从输入流中读取可变长度字符串:
- 与足够大的数组一起使用,以达到最大长度:
fgets()
char input1[200];
if (fgets(input1, sizeof input1, stdin)) {
/* string was read. strip the newline if present */
input1[strcspn(input1, "\n")] = '\0';
...
} else {
/* nothing was read: premature end of file? */
...
}
- 在符合 POSIX 标准的系统上,您可以使用将任意长度的字符串读取到分配有以下值的数组中:
getline()
malloc()
char *input1 = NULL;
size_t input1_size = 0;
ssize_t input1_length = getline(&input1, &input1_size, stdin);
if (input1_length >= 0) {
/* string was read. length is input1_length */
if (input1_length > 0 && input1[input1_length - 1] == '\n') {
/* remove the newline if present */
input1[--input1_length] = '\0';
}
...
} else {
/* nothing was read: premature end of file? */
...
}
不建议使用,因为它很难正确使用,并且读取具有或没有指定最大长度的输入是有风险的,因为任何足够长的输入都会导致缓冲区溢出和未定义的行为。像在发布的代码中一样传递未初始化的指针具有未定义的行为。scanf
"%s"
"%[^\n]"
scanf
您可以使用修饰符来设置说明符的格式。请注意,它不是标准的 C,而是标准的 POSIX 扩展。m
char *a, *b;
scanf("%m[^\n] %m[^\n]", &a, &b);
// use a and b
printf("*%s*\n*%s*\n", a, b);
free(a);
free(b);
有什么简单的方法可以在 C 中读取可变长度的字符串吗?
不幸的是,答案是否定的
C 标准指定的输入函数(例如 、 等)都需要调用方提供输入缓冲区。一旦输入缓冲区已满,函数将(正确使用时)返回。因此,如果输入长度超过提供的缓冲区的大小,则函数将仅读取部分输入。因此,调用方必须添加代码来检查部分输入,并根据需要执行其他函数调用。scanf
fgets
Posix系统具有可以做到这一点的功能。因此,如果您可以接受将代码限制为符合 Posix 的系统,那就是您想要使用的。getline
getdelim
如果您需要可移植的、符合标准的代码,则需要编写自己的函数。为此,您需要研究 、 、 等函数。这不是一项简单的任务,但也不是“火箭科学”。以前已经做过很多很多次了......如果你在网上搜索,你很可能会找到一个可以复制的开源实现(确保遵循这样做的规则)。realloc
fgets
strcpy
memcpy
评论
scanf()
%s[^\n]
fgets
scanf
sscanf
scanf
"%s"
"%[]"
"%[^\n]"
" %[^\n]"