首先,我们知道,linux下的scanf标准库函数是一个可变参的函数,那么,我们自己要实现一个scanf也必须是一个可变参的.
其实,在liunx的库中就提供了这样的的宏,来方便我们自己来实现变参函数.这个宏在stdarg.h头文件中.
这几个宏如下:
void va_start( va_list arg_ptr, prev_param ); //va_start宏初始化变量arg_ptr,这个宏的第二个参数是第 一个可变参数的前一个参数,是一个固定的参数.
type va_arg( va_list arg_ptr, type ); //a_arg返回可变的参数, va_arg的第二个 参数是你要返回的参数的类型,
void va_end( va_list arg_ptr ); //va_end宏结束可变参数的获取,
va在这里是variable-argument(可变参数)的意思.
这几个宏的具体使用方法,百度上也有很多,不过为了方便这也给出了一个介绍其使用的博客:点击打开链接
好了,下面就是我写的用fscanf来实现scanf的程序,如有不对之处,敬请指出...
/************************************************************************* > File Name: scanf.c > Author: yexingkong > Mail: [email protected] > Created Time: Wed 04 Sep 2013 16:23:17 CST > Description:用fscanf() 模拟scanf,此函数的不足之处在于对于flaot类型数的精确度不够,还未想到更好的解决方法. ************************************************************************/ #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <ctype.h> #include <stdint.h> //检测所输入的字符串str是否全为空格,若是,则重新输入,这也是scanf是I/O阻塞型的原因. static uint8_t scan_skip(char *str, uint8_t i) { FILE *fp; fp = fdopen(0,"r"); if (fp == NULL) { perror("error\n"); exit(EXIT_FAILURE); } loop: //isspace检测所输入的字符是否为空格,返回空格数. while(isspace(str[i])) { i++; } //如果一直到字符串结束都是空格,则重新输入 if (str[i] == 0) { i = 0; fscanf(fp,"%[^'\n']",str); goto loop; } //返回第一个非空格字符的下标 return i; } //如果以%d/%u格式读取数,则对其字符串str中的数字字符转成base进制的数. //参数base 是进行进制转换的基数,*nb用来保存所转换后的结果.(eg: base= 8则为八 //进制数) static uint8_t scan_int(char *str,uint8_t i, uint8_t base,int8_t *nb) { int8_t n = 0; uint8_t j,sign = 0; //检测此十进制数的正,负性 switch(str[i]) { case '-': sign = 1; case '+': i++; break; } while(1) { if (isdigit(str[i])) //判断是否为数字'0' ~ '9' { j = str[i] - '0'; }else if (isalpha(str[i])) //判断是否为字母 { j = toupper(str[i]) - 'A' + 10; //用来计算16进制数 } else break; if (j >= base) break; n = base * n + j; //将字符转换成base进制的数 i++; } *nb = (sign == 0 ? n: -n); //正负数的判读 //返回当前已读到的最后一个字符的下标 return i; } //以长整型格式输入,则对其字符串str中的数字字符转成base进制的数, //i为当前所要读取的字符的下标,*nb用来保存所转换后的结果. static uint8_t scan_long(char *str,uint8_t i,uint8_t base,int16_t *nb) { int16_t n = 0; uint8_t j,sign = 0; //对所读取的正负性判断 switch (str[i]) { case '-': sign = 1; case '+': i++; break; } while (1) { if (isdigit(str[i])) //判断是否为数字 { j = str[i] - '0'; }else if (isalpha(str[i])) //判断是否为字母 { j = toupper(str[i]) - 'A' + 10; //用来计算16进制的数 }else break; if (j >= base) break; n = n * base + j; i++; } *nb = (sign == 0? n: -n); //对正负数的判读 //返回当前已读到的最后一个字符的下标 return i; } static uint8_t scan_float(char *str,uint8_t i,uint8_t base,float *nb) { float n = 0.0, j, m = 0; uint8_t sign = 0; int flag = 0 , k =0; switch (str[i]) { case '-': sign = 1; case '+': i++; break; } while (1) { //计算整数部分 if (isdigit(str[i]) && flag == 0) { j = str[i] - '0'; n = base * n + j; } else if (str[i] == '.') { flag = 1; //作为标记符 i++; continue; }else if (isdigit(str[i]) && flag == 1) //计算小数部分 { j = str[i] - '0'; m = base * m + j; k++; //计算小数点位数 if (k > 5) break; }else break; i++; } //将小数点六位后的数字忽略掉 while(1) { if (isdigit(str[i])) { i++; continue; }else break; } switch (k) { case 1: n = n + m * 1e-1; break; case 2: n = n + m * 1e-2; break; case 3: n = n + m * 1e-3; break; case 4: n = n + m * 1e-4; break; case 5: n = n + m * 1e-5; break; default: n = n + m * 1e-6; break; } *nb = (sign == 0? n : -n); return i; } int8_t myscanf(const char *format , ...) { //定义一个指向形参列表的指针pArg va_list pArg; char str[64]; uint8_t i = 0; int8_t nb = 0; //记录参数个数 FILE *fp; fp = fdopen(0,"r"); if (NULL == fp) { perror("error\n"); exit(EXIT_FAILURE); } fscanf(fp,"%[^'\n']",str); printf("iput=%s\n",str); va_start(pArg,format);//让pArg指向函数参数列表中的最后一个明确的参数,这里就是format参数. for(;*format != 0; format++) { if (isspace(*format)) continue; //找到所输入的字符串中第一个非空格字符的下标,或I/O阻塞,从新输入 i = scan_skip(str,i); if (*format == '%') { switch(*(++format)) { case 'c': /* char */ *va_arg(pArg,char *) = str[i++]; //给pArg所指向的地址空间单元赋值 break; case 'd': /* decimal int */ case 'u': /* unsigned int */ i = scan_int(str,i,10,va_arg(pArg,int8_t *)); //va_arg()用来返回pArg所指向的地址单元中的值 break; case 'f': i = scan_float(str,i,10,va_arg(pArg,float *)); break; case 'o': i = scan_int(str,i,8,va_arg(pArg,int8_t *)); break; case 'x': i = scan_int(str,i,16,va_arg(pArg,int8_t *)); break; case 's': { int8_t j = 0; char *d = va_arg(pArg,char *); while ((d[j++] = str[i++]) != 0) ; } break; /* long */ case 'l': switch (*(++format)) { case 'd': /*decimal long */ case 'u': i = scan_long(str,i,10,va_arg(pArg,int16_t *)); break; case 'o': i = scan_long(str,i,8,va_arg(pArg,int16_t *)); break; case 'x': i = scan_long(str,i,16,va_arg(pArg,int16_t *)); break; } break; default: if (str[i] != *format) return -1; break; } nb++; }else if (str[i] != *format) return -1; } //使pArg不再指向堆栈,结束对形参的取值 va_end(pArg); //返回参数个数 return nb; } int main(int argc,char *argv[]){ int d = 0; char c ='\0'; float f = 0.0; char str[10]; int se; printf("输入char float char[]型的数\n"); myscanf("%c %f %s",&c,&f,str); printf("%c\t%f\t%s\n",c,f,str); return 0; }
转载请注明出处:http://write.blog.csdn.net/postedit/12451273