日期:2014-05-16  浏览次数:20729 次

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 si