文章目录
1 int getc (FILE* __F) && int getchar (void)
标准库中的函数实现
可以看见 getc 与 getchar 的唯一区别就是 getchar 默认 __F 参数为 stdin
int getc (FILE* __F)
{
return (--__F->_cnt >= 0)
? (int) (unsigned char) *__F->_ptr++
: _filbuf (__F);
}
int getchar (void)
{
return (--stdin->_cnt >= 0)
? (int) (unsigned char) *stdin->_ptr++
: _filbuf (stdin);
}
功能
从标准输入流(getc还包括从指定文件)中读取一个字符
输入结束标志
换行即结束
返回值
- 若输入成功:返回用户输入的第一个字符的ASCII码;
例如:什么都不输入直接按 “Enter” 键,因 “Enter” 键包含 ‘\r’ 和 ‘\n’ 两个字符,又遇到 ‘\n’ 结束,故返回 ‘\r’ 即 10 - 其他:则返回EOF;例如输入组合按键 ctrl+z
2 char *gets(char *str)
功能
从标准输入流中读取一串字符,并把它存储在str所指向的字符串中
输入结束标志
换行即结束
返回值
若输入成功,则返回str;其他情况,返回NULL
说明
遇换行符结束后,最终输入的值是:在输入有效字符的基础上丢弃换行符,末尾添加 nul (’\0’字符)
缺陷
gets可以从标准输入无限读取,不会判断上限,以回车结束读取,而gets()函数只知道buffer的首地址,并不知道有多少个元素。若输入字符串过长,则会导致溢出,访问其他地址空间,若这些空间未被使用,则暂时不会出现问题,若这些空间已被使用,则会覆盖原有内容,导致不可估量的后果。因此,不建议使用此函数,为了避免上述缺陷,建议使用 fgets() 代替 gets()
3 char *fgets(char *str, int n, FILE *stream)
char *fgets( char *str, int n, FILE *stream )
{
register int c;
register char *cs;
cs = str;
while( --n>0 && (c = getc(stream))!= EOF )
{
if( (*cs++ = c) =='\n' )
break;
}
*cs ='\0';
return (c == EOF && cs == str) ? NULL : str;
}
功能
获取一行长度为 n 的字符串(包含 ‘0’ 符)
输入结束标志
换行即结束
返回值
若输入成功,则返回str;其他情况,返回NULL
参数 int n
1.输入字符串的长度 < n-1 :保留换行符,在最后添加 ‘0’
例:fgets(str,4,stdin); 输入:12 则实际为:12\n + ‘0’
这种情况下得到的字符串中包含了换行符 ‘\n’,故在输出时会进行自动换行
2.输入字符串的长度 >= n-1:保留 n-1 个字符,在最后添加 ‘0’;下一次调用会继续读该行的剩余部分
例:fgets(str,4,stdin); 输入:123 则实际为:123 + ‘0’
参数 char *str
参数 str 可以是一个字符数组或字符指针,若是字符指针,一定要进行初始化,分配内存空间
char str1[100];
可以进行传递
char *str2;
不能进行传递,并没有为它分配内存空间
char *str3 = (char*)malloc(100*sizeof(char));
可以进行传递,已分配动态内存空间
参数 FILE *stream
1.从标准设备读数据:该参数为 stdin
2.从文件流读取数据:fopen()、fwrite()、fread()函数使用说明与示例
4 int scanf(const char *format, ...)
函数实现
(想了解具体文件请访问:关于输入输出的3个文件)
#define INBUFSIZE 1024
static char g_pcInBuf[INBUFSIZE];
int scanf(const char * fmt, ...)
{
int i = 0;
unsigned char c;
va_list args;
//获取输入的内容
while(1)
{
c = getc(stdin);
//此处可以看到输入流中遇见 '\r', '\n' 即结束
if((c == 0x0d) || (c == 0x0a))
{
g_pcInBuf[i] = '\0';
break;
}
else
{
g_pcInBuf[i++] = c;
}
}
va_start(args,fmt);
i = vsscanf(g_pcInBuf,fmt,args); //将g_pcInBuf缓冲区反格式化为参数列表
va_end(args);
return i;
}
功能
格式输入函数,从标准输入 stdin 读取指定格式化输入;
可变参数列表
请看:可变参数列表
返回值
成功:成功匹配和赋值的个数;其他:EOF
刨析 vsscanf(g_pcInBuf,fmt,args)
scanf格式字符串中的统一格式 :%[*][width][qualifier]type],如%3d,包含对width, type的格式说明
下面函数省略 * 和 qualifier,以及对具体匹配部分的具体转换,函数实现如下:(包含解析)
// buf:输入缓冲
// fmt:缓冲区格式
// args:可变参数列表首地址
int vsscanf(const char * buf, const char * fmt, va_list args)
{
/*...*/
//!nul
while (*fmt && *str)
{
//跳过格式中的任何空白,格式中的空白匹配输入中任何数量的空白(即空格,回车...)
if (isspace(*fmt))
{
while (isspace(*fmt)) //检测空白字符
++fmt;
while (isspace(*str))
++str;
}
//任何非转换的内容都必须完全匹配
if (*fmt != '%' && *fmt)
{
if (*fmt++ != *str++)
break;
continue;
}
//到达转换符%位置
if (!*fmt)
break;
++fmt;
/*...*/
//获取字段宽度
field_width = -1;
if (isdigit(*fmt)) //检查字符是一个十进制数字字符(0-9)
field_width = skip_atoi(&fmt); //将数字字符转换为相应数字,如将'1'->1
/*...*/
switch(*fmt++)
{
//%nc
case 'c':
{
char *s = (char *) va_arg(args,char*);
//n:1 :输入一个字符
if (field_width == -1)
field_width = 1;
//n:2-9 :输入多个字符,组成固定长度字符串(包含空格)
do
{
*s++ = *str++;
} while (--field_width > 0 && *str);
num++;
}
continue;
//%s
case 's':
{
char *s = (char *) va_arg(args, char *);
if(field_width == -1)
field_width = INT_MAX; //字符串长度为最大值,否则按限定长度读取
//忽略字符串前的所有白字符
while (isspace(*str))
str++;
//存储知道下一个空白符之前
while (*str && !isspace(*str) && field_width--)
{
*s++ = *str++;
}
//加入nul
*s = '\0';
num++;
}
continue;
//%n 返回到目前为止读取的字符数(长度按字节算)
case 'n':
{
int *i = (int *)va_arg(args,int*);
*i = str - buf;
}
continue;
//%o
case 'o':
base = 8;
break;
//%x
case 'x':
case 'X':
base = 16;
break;
//%d
case 'i': //old
base = 0;
case 'd': //new
is_sign = 1;
case 'u': //%u
break;
//%%
case '%':
/* looking for '%' in str */
if (*str++ != '%')
return num;
continue;
default:
return num;
}
//跳过缓冲区中的空白符
while (isspace(*str))
str++;
/*...*/
}
return num;
}
总结
-
格式字符串中的所有空白均无效
-
任何非转换的内容都必须完全匹配,格式中的空白匹配输入中任何数量的空白
-
对于width的举例
scanf("%4d", &a); printf("%d", a);
将输入得到一串0-9的字符直接合并转换为数字输入为"12345" / 输出为1234
scanf("%4c", str); printf("%s", str);
%nc可进行带空白符的固定长度的字符串输出str必须为一段已开拓的空间的首地址:数组/分配的动态内存的首地址
输入为"12 345" / 输出为"12 3"
scanf("%4s", str); printf("%s", str);
%ns与%nc类似,但遇到空白符即终止输入为"12 345" / 输出为"12"