Best way of input in C console application: Differences between different types of input string in a console application C 控制台应用程序中的最佳输入方式
Best way of input in C console application: Differences between different types of inputting string in a console application C
您好,我一直在做研究,我在 C 控制台应用程序中发现了三种最常见的不同类型的输入:
- scanf("%[^\n]s", *char): 选取一个字符串,直到它找到一个断行。由于缓冲区溢出问题,建议不要使用。
- gets(*char): 选取一个字符串,直到它找到一个断行。它相当于第一个。也有缓冲区溢出问题,建议不要使用。
- fgets(*char, LENGTH, *FILE): 从文件 FILE 中取出一个长度为 LENGTH 个字符的字符串。它是三者中最安全的,也是最值得推荐的。
话虽如此,我将展示一个使用这三个函数的示例 (MCVE) 以及它们的作用:
#include <stdio.h>
#include <string.h>
int main(void)
{
char hello[7]; //Will store word 'hello'; Length is 6 = 5+(1)+1 = strlen("hello")+(BreakLineChar)+NullTermination - BreakLineChar will appear (or not) depending input type
int i;
// SCANF()
printf("Input HELLO: ");
scanf("%[^\n]s",hello);
fflush(stdin);
printf("Length: %d\n", strlen(hello));
for (i = 0; i < strlen(hello); i++)
{
printf("%i: %c\n", i, hello[i]);
}
printf("\n\n");
// GETS()
printf("Input HELLO: ");
gets(hello);
fflush(stdin);
printf("Length: %d\n", strlen(hello));
for (i = 0; i < strlen(hello); i++)
{
printf("%i: %c\n", i, hello[i]);
}
printf("\n\n");
// FGETS()
printf("Input HELLO: ");
fgets(hello,sizeof(hello),stdin);
fflush(stdin);
printf("Length: %d\n", strlen(hello));
for (i = 0; i < strlen(hello); i++)
{
printf("%i: %c\n", i, hello[i]);
}
printf("\n\n");
return 0;
}
这段代码,有下一个输出:
Input HELLO: hello
Length: 5
0: h
1: e
2: l
3: l
4: o
Input HELLO: hello
Length: 5
0: h
1: e
2: l
3: l
4: o
Input HELLO: hello
Length: 6
0: h
1: e
2: l
3: l
4: o
5:
Process returned 0 (0x0) execution time : 5.757 s
Press any key to continue.
所以,正如我们所看到的,可以从中得出三个结论:
- scanf: 拾取用户输入直到 '\n' 字符(不拾取字符 '\n')。所以字符串
hello
将是:
hello[0] = 'h'
hello[1] = 'e'
hello[2] = 'l'
hello[3] = 'l'
hello[4] = 'o'
hello[5] = '[=18=]'
- 得到:相当于上面的点。所以字符串
hello
将是:
hello[0] = 'h'
hello[1] = 'e'
hello[2] = 'l'
hello[3] = 'l'
hello[4] = 'o'
hello[5] = '[=18=]'
- fgets: 获取用户输入直到'\n'字符(也获取字符'\n')。所以字符串
hello
将是:
hello[0] = 'h'
hello[1] = 'e'
hello[2] = 'l'
hello[3] = 'l'
hello[4] = 'o'
hello[5] = '\n'
hello[6] = '[=33=]'
我的结论正确吗?要添加任何信息吗?
谢谢。
你的结论是正确的。但是,在您引用的三种输入法中,只有 fgets
是安全的:
scanf
变成没有限制的字符串会溢出输入缓冲区;请参阅下面的修复程序。
gets
已弃用,因为它不可能保证缓冲区溢出安全 (why?)。
要修复 scanf
,请指定缓冲区的大小,并为空终止符保留 space:
scanf("%6[^\n]", hello);
请注意,当您使用 [^\n]
格式说明符时,末尾的 s
不是必需的。
在这个答案中,我将汇总所有获得的答案,以及我继续进行的研究工作(我会在获得新信息时更新答案):
gets
是不安全的,反对它的是缓冲区溢出
通过控制台输入最安全的方法是fgets
,但它有一个缺点:该命令也读取'\n'字符。要解决这个问题,我们可以执行以下操作:
#include <stdio.h>
#include <string.h>
#define MAX_SIZE_STRING 5
int main(void)
{
char string[MAX_SIZE_STRING+1]; // MAX_SIZE_STRING + 1 due to a string needs '[=10=]' char at the end
int i;
printf("Input a string: ");
fgets(string, MAX_SIZE_STRING, stdin);
// Remove '\n' character inside the string:
string[strcspn(string, "\n")] = '[=10=]';
}
scanf
是一个非常强大的函数,必须了解透才能用好。说到 scanf
,我将展示一个如何使用 scanf
的示例,它控制您需要的一切(大小,停止阅读时,避免 bufferOverflow,...):
#include <stdio.h>
#include <string.h>
#define MAX_SIZE_STRING 5
#define STR_(X) #X
#define STR(X) STR_(X)
int main(void)
{
char string[MAX_SIZE_STRING+1]; // MAX_SIZE_STRING + 1 due to a string needs '[=11=]' char at the end
int i;
printf("Input a string: ");
scanf(" %" STR(MAX_SIZE_STRING) "[^\t\n]%*[^\n]", string);
// EXPLANATION OF THE COMMNAD:
// First blanck-space: It is used for empty string. If user entries only blank-spaces, it will no stored in the var until user input any char
// STR(TAM_MAX): Use of preprocessor for adding a max length to the input
// [^\t\n]: Pick up until find a '\t' or a '\n'
// So, at the end, this command is equal to: scanf(" %5[^\t\n]s", string);
// %*[^\n] remove end/break line (\n) so, by this way, buffer overflow is avoied
}
您好,我一直在做研究,我在 C 控制台应用程序中发现了三种最常见的不同类型的输入:
- scanf("%[^\n]s", *char): 选取一个字符串,直到它找到一个断行。由于缓冲区溢出问题,建议不要使用。
- gets(*char): 选取一个字符串,直到它找到一个断行。它相当于第一个。也有缓冲区溢出问题,建议不要使用。
- fgets(*char, LENGTH, *FILE): 从文件 FILE 中取出一个长度为 LENGTH 个字符的字符串。它是三者中最安全的,也是最值得推荐的。
话虽如此,我将展示一个使用这三个函数的示例 (MCVE) 以及它们的作用:
#include <stdio.h>
#include <string.h>
int main(void)
{
char hello[7]; //Will store word 'hello'; Length is 6 = 5+(1)+1 = strlen("hello")+(BreakLineChar)+NullTermination - BreakLineChar will appear (or not) depending input type
int i;
// SCANF()
printf("Input HELLO: ");
scanf("%[^\n]s",hello);
fflush(stdin);
printf("Length: %d\n", strlen(hello));
for (i = 0; i < strlen(hello); i++)
{
printf("%i: %c\n", i, hello[i]);
}
printf("\n\n");
// GETS()
printf("Input HELLO: ");
gets(hello);
fflush(stdin);
printf("Length: %d\n", strlen(hello));
for (i = 0; i < strlen(hello); i++)
{
printf("%i: %c\n", i, hello[i]);
}
printf("\n\n");
// FGETS()
printf("Input HELLO: ");
fgets(hello,sizeof(hello),stdin);
fflush(stdin);
printf("Length: %d\n", strlen(hello));
for (i = 0; i < strlen(hello); i++)
{
printf("%i: %c\n", i, hello[i]);
}
printf("\n\n");
return 0;
}
这段代码,有下一个输出:
Input HELLO: hello
Length: 5
0: h
1: e
2: l
3: l
4: o
Input HELLO: hello
Length: 5
0: h
1: e
2: l
3: l
4: o
Input HELLO: hello
Length: 6
0: h
1: e
2: l
3: l
4: o
5:
Process returned 0 (0x0) execution time : 5.757 s
Press any key to continue.
所以,正如我们所看到的,可以从中得出三个结论:
- scanf: 拾取用户输入直到 '\n' 字符(不拾取字符 '\n')。所以字符串
hello
将是:
hello[0] = 'h'
hello[1] = 'e'
hello[2] = 'l'
hello[3] = 'l'
hello[4] = 'o'
hello[5] = '[=18=]'
- 得到:相当于上面的点。所以字符串
hello
将是:
hello[0] = 'h'
hello[1] = 'e'
hello[2] = 'l'
hello[3] = 'l'
hello[4] = 'o'
hello[5] = '[=18=]'
- fgets: 获取用户输入直到'\n'字符(也获取字符'\n')。所以字符串
hello
将是:
hello[0] = 'h'
hello[1] = 'e'
hello[2] = 'l'
hello[3] = 'l'
hello[4] = 'o'
hello[5] = '\n'
hello[6] = '[=33=]'
我的结论正确吗?要添加任何信息吗?
谢谢。
你的结论是正确的。但是,在您引用的三种输入法中,只有 fgets
是安全的:
scanf
变成没有限制的字符串会溢出输入缓冲区;请参阅下面的修复程序。gets
已弃用,因为它不可能保证缓冲区溢出安全 (why?)。
要修复 scanf
,请指定缓冲区的大小,并为空终止符保留 space:
scanf("%6[^\n]", hello);
请注意,当您使用 [^\n]
格式说明符时,末尾的 s
不是必需的。
在这个答案中,我将汇总所有获得的答案,以及我继续进行的研究工作(我会在获得新信息时更新答案):
gets
是不安全的,反对它的是缓冲区溢出通过控制台输入最安全的方法是
fgets
,但它有一个缺点:该命令也读取'\n'字符。要解决这个问题,我们可以执行以下操作:#include <stdio.h> #include <string.h> #define MAX_SIZE_STRING 5 int main(void) { char string[MAX_SIZE_STRING+1]; // MAX_SIZE_STRING + 1 due to a string needs '[=10=]' char at the end int i; printf("Input a string: "); fgets(string, MAX_SIZE_STRING, stdin); // Remove '\n' character inside the string: string[strcspn(string, "\n")] = '[=10=]'; }
scanf
是一个非常强大的函数,必须了解透才能用好。说到scanf
,我将展示一个如何使用scanf
的示例,它控制您需要的一切(大小,停止阅读时,避免 bufferOverflow,...):#include <stdio.h> #include <string.h> #define MAX_SIZE_STRING 5 #define STR_(X) #X #define STR(X) STR_(X) int main(void) { char string[MAX_SIZE_STRING+1]; // MAX_SIZE_STRING + 1 due to a string needs '[=11=]' char at the end int i; printf("Input a string: "); scanf(" %" STR(MAX_SIZE_STRING) "[^\t\n]%*[^\n]", string); // EXPLANATION OF THE COMMNAD: // First blanck-space: It is used for empty string. If user entries only blank-spaces, it will no stored in the var until user input any char // STR(TAM_MAX): Use of preprocessor for adding a max length to the input // [^\t\n]: Pick up until find a '\t' or a '\n' // So, at the end, this command is equal to: scanf(" %5[^\t\n]s", string); // %*[^\n] remove end/break line (\n) so, by this way, buffer overflow is avoied }