设为首页 加入收藏

TOP

scanf函数使用和实现详解(三)
2014-03-10 12:59:17 来源: 作者: 【 】 浏览:292
Tags:scanf 函数 使用 实现 详解


    一个空格也不容易看出来。当你的程序出现上面的问题时,自己对照检查一下就可以了。
    实现原理
    #include    <stdio.h>
    #include    <stdarg.h>
    #include    "loc_incl.h"
    int scanf(const char *format, …)
    {
    va_list ap;
    int retval;
    va_start(ap, format);
    retval = _doscan(stdin, format, ap);
    va_end(ap);
    return retval;
    }
    在上述代码中我们可以看到有一个_doscan函数,而这一函数在头文件loc_incl.h中定义,函数声明如下:
    int _doscan(FILE * stream, const char*format, va_list ap)
    int _doscan(register FILE *stream, const char *format, va_list ap)
    {
    int        done = 0;    /* number of items done */
    int        nrchars = 0;    /* number of characters read */
    int        conv = 0;    /* # of conversions */
    int        base;        /* conversion base */
    unsigned long    val;        /* an integer value */
    register char    *str;        /* temporary pointer */
    char        *tmp_string;    /* ditto */
    unsigned    width = 0;    /* width of field */
    int        flags;        /* some flags */
    int        reverse;    /* reverse the checking in […] */
    int        kind;
    register int    ic = EOF;    /* the input character */
    #ifndef    NOFLOAT
    long double    ld_val;
    #endif
    if (!*format) return 0;
    while (1) {
    if (isspace(*format)) {
    while (isspace(*format))
    format++;    /* skip whitespace */
    ic = getc(stream);
    nrchars++;
    while (isspace (ic)) {
    ic = getc(stream);
    nrchars++;
    }
    if (ic != EOF) ungetc(ic,stream);
    nrchars--;
    }
    if (!*format) break;    /* end of format */
    if (*format != '%') {
    ic = getc(stream);
    nrchars++;
    if (ic != *format++) break;    /* error */
    continue;
    }
    format++;
    if (*format == '%') {
    ic = getc(stream);
    nrchars++;
    if (ic == '%') {
    format++;
    continue;
    }
    else break;
    }
    flags = 0;
    if (*format == '*') {
    format++;
    flags |= FL_NOASSIGN;
    }
    if (isdigit (*format)) {
    flags |= FL_WIDTHSPEC;
    for (width = 0; isdigit (*format);)
    width = width * 10 + *format++ - '0';
    }
    switch (*format) {
    case 'h': flags |= FL_SHORT; format++; break;
    case 'l': flags |= FL_LONG; format++; break;
    case 'L': flags |= FL_LONGDOUBLE; format++; break;
    }
    kind = *format;
    if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
    do {
    ic = getc(stream);
    nrchars++;
    } while (isspace(ic));
    if (ic == EOF) break;        /* outer while */
    } else if (kind != 'n') {        /* %c or %[ */
    ic = getc(stream);
    if (ic == EOF) break;        /* outer while */
    nrchars++;
    }
    switch (kind) {
    default:
    /* not recognized, like %q */
    return conv || (ic != EOF) done : EOF;
    break;
    case 'n':
    if (!(flags & FL_NOASSIGN)) {    /* silly, though */
    if (flags & FL_SHORT)
    *va_arg(ap, short *) = (short) nrchars;
    else if (flags & FL_LONG)
    *va_arg(ap, long *) = (long) nrchars;
    else
    *va_arg(ap, int *) = (int) nrchars;
    }
    break;
    case 'p':        /* pointer */
    set_pointer(flags);
    /* fallthrough */
    case 'b':        /* binary */
    case 'd':        /* decimal */
    case 'i':        /* general integer */
    case 'o':        /* octal */
    case 'u':        /* unsigned */
    case 'x':        /* hexadecimal */
    case 'X':        /* ditto */
    if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
    width = NUMLEN;
    if (!width) return done;
    str = o_collect(ic, stream, kind, width, &base);
    if (str < inp_buf
    || (str == inp_buf
    && (*str == '-'
    || *str == '+'))) return done;
    /*
    * Although the length of the number is str-inp_buf+1
    * we don't add the 1 since we counted it already
    */
    nrchars += str - inp_buf;
    if (!(flags & FL_NOASSIGN)) {
    if (kind == 'd' || kind == 'i')
    val = strtol(inp_buf, &tmp_string, base);
    else
    val = strtoul(inp_buf, &tmp_string, base);
    if (flags & FL_LONG)
    *va_arg(ap, unsigned long *) = (unsigned long) val;
    else if (flags & FL_SHORT)
    *va_arg(ap, unsigned short *) = (unsigned short) val;
    else
    *va_arg(ap, unsigned *) = (unsigned) val;
    }
    break;
    case 'c':
    if (!(flags & FL_WIDTHSPEC))
    width = 1;
    if (!(flags & FL_NOASSIGN))
    str = va_arg(ap, char *);
    if (!width) return done;
    while (width && ic != EOF) {
    if (!(flags & FL_NOASSIGN))
    *str++ = (char) ic;
    if (--width) {
    ic = getc(stream);
    nrchars++;
    }
    }
    if (width) {
    if (ic != EOF) ungetc(ic,stream);
    nrchars--;
    }
    break;
    case 's':
    if (!(flags & FL_WIDTHSPEC))
    width = 0xffff;
    if (!(flags & FL_NOASSIGN))
    str = va_arg(ap, char *);
    if (!width) return done;
    while (width && ic != EOF && !isspace(ic)) {
    if (!(flags & FL_NOASSIGN))
    *str++ = (char) ic;
    if (--width) {
    ic = getc(stream);
    nrchars++;
    }
    }
    /* terminate the string */
    if (!(flags & FL_NOASSIGN))
    *str = '\0';
    if (width) {
    if (ic != EOF) ungetc(ic,stream);
    nrchars--;
    }
    break;
    case '[':
    if (!(flags & FL_WIDTHSPEC))
    width = 0xffff;
    if (!width) return done;
    if ( *++format == '^' ) {
    reverse = 1;
    format++;
    } else
    reverse = 0;
    for (str = Xtable; str < &Xtable[NR_CHARS]
    ; str++)
    *str = 0;
    if (*format == ']') Xtable[*format++] = 1;
    while (*format && *format != ']') {
    Xtable[*format++] = 1;
    if (*format == '-') {
    format++;
    if (*format
    && *format != ']'
    && *(format) >= *(format -2)) {
    int c;
    for( c = *(format -2) + 1
    ; c <= *format ; c++)
    Xtable[c] = 1;
    format++;
    }
    else Xtable['-'] = 1;
    }
    }
    if (!*format) return done;
    if (!(Xtable[ic] ^ reverse)) {
    /* MAT 8/9/96 no match must return character */
    ungetc(ic, stream);
    return done;
    }
    if (!(flags & FL_NOASSIGN))
    str = va_arg(ap, char *);
    do {
    if (!(flags & FL_NOASSIGN))
    *str++ = (char) ic;
    if (--width) {
    ic = getc(stream);
    nrchars++;
    }
    } while (width && ic != EOF && (Xtable[ic] ^ reverse));
    if (width) {
    if (ic != EOF) ungetc(ic, stream);
    nrchars--;
    }
    if (!(flags & FL_NOASSIGN)) {    /* terminate string */
    *str = '\0';
    }
    break;
    #ifndef    NOFLOAT
    case 'e':
    case 'E':
    case 'f':
    case 'g':
    case 'G':
    if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
    width = NUMLEN;
    if (!width) return done;
    str = f_collect(ic, stream, width);
    if (str < inp_buf
    || (str == inp_buf
    && (*str == '-'
    || *str == '+'))) return done;
    /*
    * Although the length of the number is str-inp_buf+1
    * we don't add the 1 since we counted it already
    */
    nrchars += str - inp_buf;
    if (!(flags & FL_NOASSIGN)) {
    ld_val = strtod(inp_buf, &tmp_string);
    if (flags & FL_LONGDOUBLE)
    *va_arg(ap, long double *) = (long double) ld_val;
    else
    if (flags & FL_LONG)
    *va_arg(ap, double *) = (double) ld_val;
    else
    *va_arg(ap, float *) = (float) ld_val;
    }
    break;
    #endif
    }        /* end switch */
    conv++;
    if (!(flags & FL_NOASSIGN) && kind != 'n') done++;
    format++;
    }
    return conv || (ic != EOF) done : EOF;
    }
    在上面的源代码中,值得注意的是第26行的getc宏,定义代码如下:
    #define   getc(p)        (--(p)->_count>= 0 (int) (*(p)->_ptr++) : \
    __fillbuf(p))
    getc的调用形式:ch=getc(fp); 功能是从文件指针指向的文件读入一个字符,并把它作为函数值返回给int型变量ch.
    第4行~第17行,定义一些后面需要用到的变量
    第23行~第34行,跳过format格式串中的空格,并且跳过输入流中的空格
    第37行~第42行,输入流stream与format格式串中的空白符(空白符可以是空格(space)、制表符(tab)和新行符(newline))保持一致
    第44行~第52行,在format中的字符为'%'的前提下,stream中的字符也为'%',则继续
    第54行~第57行,format当前字符为'*',表示读指定类型的数据但不保存
    第58行~第62行,指定说明最大域宽。在百分号(%)与格式码之间的整数用于限制从对应域读入的最大字符数于宽度
    第64行~第282行,switch语句,用于格式修饰符,这些修饰符包括: h、l、L、c、p、b、d、i、o、u……,还有基于扫描集的'['修饰符

        

首页 上一页 1 2 3 4 5 下一页 尾页 3/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇OpenGL库编译相关错误 下一篇A+B输入输出练习系列题

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: