C语言细节 - 输入输出库函数 -待完成

文章目录

  • 1 `int getc (FILE* __F) && int getchar (void)`
      • 标准库中的函数实现
      • 功能
      • 输入结束标志
      • 返回值
  • 2 `char *gets(char *str) `
      • 功能
      • 输入结束标志
      • 返回值
      • 说明
      • 缺陷
  • 3 `char *fgets(char *str, int n, FILE *stream) `
      • 功能
      • 输入结束标志
      • 返回值
      • 参数 int n
      • 参数 char *str
      • 参数 FILE *stream
  • 4 `int scanf(const char *format, ...)`
      • 函数实现
      • 功能
      • 可变参数列表
      • 返回值
      • 刨析 vsscanf(g_pcInBuf,fmt,args)
      • 总结

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;
}

总结

  1. 格式字符串中的所有空白均无效

  2. 任何非转换的内容都必须完全匹配,格式中的空白匹配输入中任何数量的空白

  3. 对于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"

  4. 匹配/过滤特定字符



你可能感兴趣的:(C语言细节)