我试着阅读使用:
char *input1, *input2; scanf("%s[^\n]", input1); scanf("%s[^\n]", input2);
很明显我做错了什么,因为第二个字符串被读为null。我知道不推荐使用scanf(),但我找不到任何其他简单的方法来做同样的事情。
vsaztqbk1#
声明:
char *input1, *input2;
为两个指向char的指针分配内存。注意这只为那些指针分配内存-它们没有初始化,也没有指向任何有意义的东西-而不是它们所指向的东西。然后,对scanf()的调用尝试向内存写入超出界限的内容,并导致未定义的行为。相反,您可以声明具有自动存储持续时间的固定大小的字符数组:
char
scanf()
char input1[SIZE];
这将为数组分配内存,并且对scanf()的调用将有效。或者,可以使用以下内存分配函数之一为指针动态分配内存:
char *input1 = malloc (size);
声明了一个指向char的指针,其内容不确定,但立即被一个指向size内存块的指针覆盖。注意,对malloc()的调用可能失败。它返回NULL作为错误代码,因此请检查它。但是scanf()不应该用作用户输入接口,它不能防止缓冲区溢出,并且会在输入缓冲区中留下一个换行符(这会导致更多的问题)。考虑用fgets代替,它将空终止缓冲区,并且最多读取size - 1个字符。对scanf()的调用可以替换为:
size
malloc()
NULL
fgets
size - 1
fgets (buf, sizeof buf, stdin);
然后可以使用sscanf、strtol等解析字符串。注意,如果有空格,fgets()将保留尾随换行符。您可以使用以下一行程序删除它:
sscanf
strtol
fgets()
buf [strcspn (buf, "\n\r") = '\0`;
这也会处理返回托架(如果有)。或者,如果您希望继续使用scanf()(我建议不要使用),请使用字段宽度来限制输入,并检查scanf()的返回值:
scanf ("%1023s", input1); /* Am using 1023 as a place holder */
也就是说,如果希望读取可变长度的行,则需要使用malloc()动态分配内存,然后根据需要使用realloc()调整其大小。在符合POSIX的系统上,可以使用getline()读取任意长度的字符串,但请注意,它容易受到DOS攻击。
realloc()
getline()
r1wp621o2#
您可以使用m修饰符来格式化说明符。注意它不是标准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);
trnvg8h33#
有两种简单的方法可以从输入流中读取可变长度字符串:
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? */ ... }
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具有未定义的行为。
scanf
"%s"
"%[^\n]"
zzwlnbp84#
有什么简单的方法可以在C中读取可变长度的字符串吗?很遗憾,答案是否输入功能(例如scanf、fgets等)都要求调用者提供输入缓冲区。一旦输入缓冲区满了,函数将(如果使用正确)返回。因此,如果输入比提供的缓冲区的大小更长,函数只读取部分输入。2所以调用者必须添加代码来检查部分输入,并根据需要进行额外的函数调用。Posix系统有getline和getdelim函数可以做到这一点,所以如果你能接受将你的代码限制在符合Posix的系统上,那就是你想要使用的。如果你需要可移植的、符合标准的代码,你需要编写自己的函数。为此,你需要研究realloc、fgets、strcpy、memcpy等函数。这不是一项简单的任务,但也不是"火箭科学"。以前已经做过很多很多次了......如果你在网上搜索,您很可能会找到一个可以直接复制的开源实现(确保遵循复制规则)。
getline
getdelim
realloc
strcpy
memcpy
4条答案
按热度按时间vsaztqbk1#
声明:
为两个指向
char
的指针分配内存。注意这只为那些指针分配内存-它们没有初始化,也没有指向任何有意义的东西-而不是它们所指向的东西。然后,对
scanf()
的调用尝试向内存写入超出界限的内容,并导致未定义的行为。相反,您可以声明具有自动存储持续时间的固定大小的字符数组:
这将为数组分配内存,并且对
scanf()
的调用将有效。或者,可以使用以下内存分配函数之一为指针动态分配内存:
声明了一个指向
char
的指针,其内容不确定,但立即被一个指向size
内存块的指针覆盖。注意,对malloc()
的调用可能失败。它返回NULL
作为错误代码,因此请检查它。但是
scanf()
不应该用作用户输入接口,它不能防止缓冲区溢出,并且会在输入缓冲区中留下一个换行符(这会导致更多的问题)。考虑用
fgets
代替,它将空终止缓冲区,并且最多读取size - 1
个字符。对
scanf()
的调用可以替换为:然后可以使用
sscanf
、strtol
等解析字符串。注意,如果有空格,
fgets()
将保留尾随换行符。您可以使用以下一行程序删除它:这也会处理返回托架(如果有)。
或者,如果您希望继续使用
scanf()
(我建议不要使用),请使用字段宽度来限制输入,并检查scanf()
的返回值:也就是说,如果希望读取可变长度的行,则需要使用
malloc()
动态分配内存,然后根据需要使用realloc()
调整其大小。在符合POSIX的系统上,可以使用
getline()
读取任意长度的字符串,但请注意,它容易受到DOS攻击。r1wp621o2#
您可以使用
m
修饰符来格式化说明符。注意它不是标准C,而是标准POSIX扩展。trnvg8h33#
有两种简单的方法可以从输入流中读取可变长度字符串:
fgets()
和一个足够大的数组来达到最大长度:getline()
将任意长度的字符串读入使用malloc()
分配的数组:不建议使用
scanf
,因为它很难正确使用,而且使用"%s"
或"%[^\n]"
阅读输入而不指定最大长度是有风险的,因为任何足够长的输入都将导致缓冲区溢出和未定义的行为。像您在发布的代码中所做的那样将未初始化的指针传递到scanf
具有未定义的行为。zzwlnbp84#
有什么简单的方法可以在C中读取可变长度的字符串吗?
很遗憾,答案是否
输入功能(例如
scanf
、fgets
等)都要求调用者提供输入缓冲区。一旦输入缓冲区满了,函数将(如果使用正确)返回。因此,如果输入比提供的缓冲区的大小更长,函数只读取部分输入。2所以调用者必须添加代码来检查部分输入,并根据需要进行额外的函数调用。Posix系统有
getline
和getdelim
函数可以做到这一点,所以如果你能接受将你的代码限制在符合Posix的系统上,那就是你想要使用的。如果你需要可移植的、符合标准的代码,你需要编写自己的函数。为此,你需要研究
realloc
、fgets
、strcpy
、memcpy
等函数。这不是一项简单的任务,但也不是"火箭科学"。以前已经做过很多很多次了......如果你在网上搜索,您很可能会找到一个可以直接复制的开源实现(确保遵循复制规则)。