linux下使用fscanf实现scanf
首先,我们知道,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: abqyexingkong@gmail.com
> 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
linux下使用fscanf实现scanf的更多相关文章
- Linux下用程序实现统计cpu和内存的利用率
Linux下没有直接可以调用系统函数知道CPU占用和内存占用.那么如何知道CPU和内存信息呢.只有通过proc伪文件系统来实现. proc伪文件就不介绍了,只说其中4个文件.一个是/proc/stat ...
- Linux下计算进程的CPU占用和内存占用的编程方法[转]
from:https://www.cnblogs.com/cxjchen/archive/2013/03/30/2990548.html Linux下没有直接可以调用系统函数知道CPU占用和内存占用. ...
- 归纳整理Linux下C语言常用的库函数----文件操作
在没有IDE的时候,记住一些常用的库函数的函数名.参数.基本用法及注意事项是很有必要的. 参照Linux_C_HS.chm的目录,我大致将常用的函数分为一下几类: 1. 内存及字符串控制及操作 2. ...
- linux下与windows下的换行符
[原文有些许错误,已作了修改] 回车符号和换行符号产生背景 关于“回车”(carriage return)和“换行”(line feed)这两个概念的来历和区别.在计算机还没有出现之前,有一种叫做电传 ...
- Linux下调试程序方法
您可以用各种方法来监控运行着的用户空间程序:可以为其运行调试器并单步调试该程序,添加打印语句,或者添加工具来分析程序.本文描述了几种可以用来调试在 Linux 上运行的程序的方法.我们将回顾四种调试问 ...
- Linux下的C编程实战
Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来, Linu ...
- linux下的c编程
linux下的c编程 Linux 系统上可用的 C 编译器是 GNU C 编译器, 它建立在自由软件基金会的编程许可证的基础上,因此可以自由发布.GNU C 对标准 C 进行一系列扩展,以增强标准 ...
- Linux下缓冲区溢出攻击的原理及对策(转载)
前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实现 ...
- Linux下多线程编程
一.为什么要引入线程? 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式.在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维 ...
随机推荐
- javaScript常用方法整合(项目中用到过的)
防止输入空格.缩进等字符: function trim(str){ return str.replace(/^\s+|\s+$/g,""); } JS去掉style样式标签 fun ...
- python入门笔记第一天
查询acsii命令 ord(‘A’) 导入模块python执行系统命令显示文件.查找文件方法1import osa = os.popen('目标').read()a 解释output = os.pop ...
- Python环境搭建中解决C编译的问题
下载必要文件 Python Microsoft Visual C++ Compiler for Python 2.7 setuptools 安装Python 安装VCForPython27 在命令行下 ...
- ARM920T系统总线时序分析
一.系统总线时序图 二.分析 第一个时钟周期开始,系统地址总线给出需要访问的存储空间地址. 经过Tacs时间后,片选信号也相应给出,并且锁存当前地址线上地址信息. 再经过Tcso时间后,处理器给出当前 ...
- Linux脚本
放置在/usr/local/sbin下面: 收到一个问题:"-bash XXX 没有权限":需要在此目录下执行chmod +x filename 收到一个问题:"-bas ...
- 关于scroll无法绑定的问题
关于jQuery的scroll([[data],fn])事件, 概述是:当用户滚动指定的元素(元素包括:所有可滚动的元素和 window 对象)时,会触发该事件. 如绑定窗口对象: $(window) ...
- How to enable/disable EWF
date: 2015/2/18 Enhanced Write Filter (or EWF) is a component of Windows XP Embedded and Windows Emb ...
- seajs配合spm应用之四弹出框
前面描述了 seajs的弹出遮罩层, 还没讲到弹出框, 这里接着把那几个例子介绍完. 目前已经有的工作是, 点击toggle按钮,可以弹出一个背投一样的暗灰色遮罩层, 主要的作用就是遮住当前页面上所有 ...
- BZOJ 3676 回文串
Description 考虑一个只包含小写拉丁字母的字符串\(s\).我们定义\(s\)的一个子串\(t\)的"出现值"为\(t\)在\(s\)中的出现次数乘以\(t\)的长度.请 ...
- swift中类似宏定义
建一个类 如,在Contans.swift中 import UIKit let kMAIN_SIZE = UIScreen.mainScreen().bounds 在其他地方直接用 比如在 MyTab ...