读取输入的方式

相关函数原型(从控制台获取输入,不考虑宽字符):

1
2
3
4
5
int scanf(const char *format, ... );
int getchar(void);
char *gets(char *str );
char *gets_s(char *str, rsize_t n );
char *fgets(char *str, int count, FILE *stream );
  • scanf
    • 如果解析错误,内容继续留在缓冲区供下次使用;
    • 解析失败返回 0,成功返回解析的参数个数,不会超过占位符个数,读到文件尾返回 EOF(-1);
    • 读取字符串,一次只能读取一个词,不能用 scanf 读取一行;
  • getchar
    • 可以读取到换行符;
    • 常用于暂停程序,或丢弃缓冲区剩余字符;
  • gets
    • 读取一行,遇到换行符,直接丢弃换行符;
    • 会自动在字符串末尾添加\0
    • 返回字符串指针,读取失败返回 null;
  • gets_s
    • 可以设置读取的字符串长度;
    • 读到换行符,将换行符丢弃;
    • 如果读取到最大字符数,还没有读取到换行符或文件结尾,读取并丢弃随后的输入直至遇到换行符或 EOF;
    • 返回字符串指针,读取失败返回 null;
    • c11 的可选函数
  • fgets
    • 可以设置读取的字符串长度;
    • 读到换行符不丢弃存到数组里;
    • 不会自动清除行缓冲区剩余数据;
    • 返回字符串指针,读取失败返回 null;

scanf

该函数可以从标准输入读取内容,返回值为读取的参数个数,例如:

1
2
3
4
5
6
7
#include <stdio.h>

int main()
{
int seed;
printf("%d\n", scanf("%d %d", &seed, &seed));
}

运行程序,输入 两个整数,打印为 2,测试输入 3 个值仍然打印 2,是因为这个”%d %d”指定了只解析两个 int,多余的将留在缓冲区中,如果后面再写一个 scanf,将从缓冲区中继续解析。

现在多加一个scanf

1
2
3
4
5
6
7
8
#include <stdio.h>

int main()
{
int seed;
printf("%d\n", scanf("%d %d", &seed, &seed));
printf("%d\n", scanf("%d %d", &seed, &seed));
}

case1:读取到文件尾部返回 EOF

1
1

输出:

1
2
1
-1

scanf 从缓冲区中解析,返回解析成功的参数个数,因为只有一个 1,所以第一个给 scanf 解析,第一行打印 1,第二行解析的时候因读取到了文件结束表示 EOF 返回 -1。

case2:解析失败返回 0

1
f

输出:

1
2
0
0

这说明,解析失败的内容还留在缓冲区给下次 scanf 用,所以两个 scanf 都返回的 0。

因为无法解析的值会继续留在缓冲区供下次使用,所以如果是循环scanf,程序就会跑飞,让你没有输入的机会,可以使用综上一节提供的示例测试一下,运行后直接输入f

case3:返回值最大为占位符个数

1
1 2 3 4 5 6 7

输出:

1
2
2
2

这表明,返回值最大是占位符的个数,剩下的内容还留在缓冲区。

综上

scanf 判断输入结束,只能在文件输入模式下利用 EOF 判断,例如:

1
2
3
4
5
6
7
8
9
10
11
12
// qwer.c
#include <stdio.h>

int main()
{
int a;
while(scanf("%d", &a) != EOF)
{
printf("%d\n", a);
}
return 0;
}

输入文件 test.txt 内容:

1
2
3
4
1
2
3
4

编译: gcc qwer.c -o main -std=c11
运行:./main < test.txt

getchar

这个函数可以从输入缓冲区仅读取一个字符,返回 int,后面结合 fgets 使用。

gets

在读取字符串时,scanf()和转换说明 %s 只能读取一个单词,可是程序中经常要读取一整行输入。gets函数简单易用,它读取整行输入,直到遇到换行符,然后丢弃换行符,存储其余字符,并在这些字符的末尾添加一个空字符使其成为一个 c 字符串。它经常和 puts 函数配对使用,该函数用于显示字符串,并在末尾添加换行符。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#define STLEN 81
int main()
{
char words[STLEN];
puts("Enter a string, please.");
gets(words); // 典型用法
printf("Your string twice:\n");
printf("%s\n", words);
puts(words);
puts("Done.");
return 0;
}

printf("%s\n", words);puts(words); 效果相同,但是编译的时候会产生警告,因为 gets 读取整行输入,并不知道 words 能存多少,如果输入字符串过长,会导致缓冲区溢出。

例如将 STLEN 设置成5,程序依然可以运行,尝试输入过长的数据就可能会发成溢出,最直观的就是可以看到发生段溢出后程序异常退出。

gets_s

该函数是 c11 才有的,且为拓展函数,使用方式除了可以设置读取的字符数之外和 gets 函数用法一样。

fgets

这个函数除了可以从标准输入读取字符串之外,还可以从文件中读取,而且可以指定读取字符个数,比 gets_s 更加灵活易用,利用 fgets,但是fgets 不会自动丢弃超过字符个数之外的行缓冲区数据,所以要配合 getchar 将剩余的缓冲数据丢弃,否则可能造成程序运行以异常。

s_gets【自定义输入】

为满足以下几点编写自定义输入:

  • 从标准输入读取数据;
  • 能够指定读取字符个数;
  • 丢弃换行符;
  • 丢弃行缓冲区剩余数据;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char *s_gets(char *str, int n)
{
char *ret_var;
int i = 0;
ret_var = fgets(str, n, stdin);
if(ret_var)
{
while(str[i] != '\n' && str[i] != '\0')
{
i++;
}
if(str[i] == '\n')
str[i] = '\0';
else
while(getchar() != '\n')
continue;
}
return ret_var;
}

使用方式

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#define STLEN 8

int main()
{
char words[STLEN];
while(s_gets(words, STLEN) && words[0] != '\n') // 没有输入数据会自动退出程序
{
puts(words);
}
return 0;
}



root@kali ~# cat 重要声明
本博客所有原创文章,作者皆保留权利。> 转载必须包含本声明,保持文本完整,并以超链接形式注明出处【[Techliu](https://scriptboy.cn)】。查看和编写文> 章评论都需翻墙,为了更方便地获取文章信息,可订阅[RSS](https://feeds2.feedburner.com/techliu),如果您还没有 一款喜爱的阅读器,不妨试试[Inoreader.](https://www.inoreader.com)。
r oot@kali ~# Thankyou!

⬆︎TOP