实施scanf

时间:2018-07-13 20:29:44

标签: c scanf stdin

由于几乎所有旨在从stdin获取数据的C函数都是不良/有缺陷的:

  • gets对此说的越少越好

  • scanf不检查缓冲区是否溢出,并且'\n'始终保留在stdin中,从而使接下来的scanf s次出现

  • scanf_s几乎相同,但是具有缓冲区溢出检查
  • fgets'\n'附加到string
  • gets_s没有先前的问题,但对其他流没有用

我决定编写自己的函数,该函数至少可用于从stdin读取数字

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void *scano(char mode);

int main()
{

  // int *num = (int *) scano(sData, 'd');
   float *num = (float *)scano('f');
   printf("Half: %f", *(num)/2);



    return 0;
}

void *scano(char mode){

    char sData[20];
    fgets(sData, 20, stdin);
    *(sData + strlen(sData) - 1) = '\0'; //get rid of the '\n' before the '\0'
   switch(mode){
       case 'd':{
           int *dataI = (int *)malloc(sizeof(int));
           *dataI = atoi(sData);
           return dataI;
       }
       case 'f':{
           float *dataF = (float *)malloc(sizeof(float));
           *dataF = atof(sData);
           return dataF;
       }
       case 'D':{
           //double...
       }
   }

}

对于其他数据类型,该功能显然未完成,但是我首先要提出一些问题:

  • 该函数的算法如何改进?
  • 我不需要每个free()中的case吗?我知道分配的内存 需要释放,但是在使用列表时,free()仅 用于删除Nodes,在创建Nodes时未调用任何free() malloc()之后。
  • 它完全安全吗?如果没有,如何使其安全?

1 个答案:

答案 0 :(得分:1)

下面是几个从输入源返回数字值的函数的简单示例。这些示例期望使用几种类型的空白字符(空格,制表符,行尾,返回)来分隔一组数字字段。

这些是演示一种方法,肯定还有改进的空间。

我建议您看看atoi vs atol vs strtol vs strtoul vs sscanf

中有关问题和答案的讨论

fgetc()函数用于一次从输入源中提取一个字符,并测试是否应继续或停止读取输入源。通过使用fgetc()函数,当使用了scano_l()scan_d()这些函数时,我们可以允许其他函数继续从输入源读取。

我们还通过使用本地缓冲区并返回实际值作为malloc()free()来消除对long及其伴随的double和内存管理的需求

例如,下面将使用C ++ main对这些进行简单测试(_tmain()是因为我正在使用Microsoft Visual Studio 2005生成Windows控制台应用程序)。可以通过编译然后尝试几种不同的数据输入方案来进行测试,其中先输入一个整数,例如1234,然后输入一个或多个空格字符(空格,制表符,换行),然后是浮点数数字,例如45.678,后跟至少一个空格字符,再加上一些文本字符。

// scan_no.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <stdlib.h>

extern "C" {
long  scano_l (FILE *pFile);

double  scano_d (FILE *pFile);
};

int _tmain(int argc, _TCHAR* argv[])
{
    // test by reading a few white space delimited numeric value and then a
    // text string just to demonstrate how it works.
    long l = scano_l (stdin);
    double d = scano_d (stdin);
    char testBuffer[256] = {0};

    fgets (testBuffer, 255, stdin);

    printf (" value is %ld\n", l);
    printf (" value is %lf\n", d);
    printf (" value is %s\n", testBuffer);
    return 0;
}

在我的情况下位于另一个源文件csource.c中的函数是:

#include <stdio.h>
#include <stdlib.h>

// define the states for our Finite State Machine. It be a simple one with
// straight transitions from one state to the next.
enum  StateFSM {StateBegin = 1, StateAccept = 2, StateEnd = 3};

static char *fetchValue (FILE *pFile, char *buffer, int nMaxLen)
{
    int    iBuffIndex = 0;
    enum StateFSM   iState = StateBegin;

    do {
        // until we reach an end state of our state machine or we reach end of file
        // on our input source, lets fetch characters from the input source and decide
        // what to do with the character until our end state is reached.
        // end state is either white space trailing the desired characters or end of file.
        int    iInput = fgetc (pFile);

        if (feof(pFile)) break;
        switch (iInput) {
            case ' ':
            case '\t':
            case '\n':
            case '\r':
                // eat up any leading whitespace
                if (iState != StateAccept) break;
                // we have found trailing white space so we are done.
                iState = StateEnd;
                break;
            default:
                if (iBuffIndex < nMaxLen) {
                    // as long as we are not at the max length lets get a character into
                    // the supplied buffer. if we are at max buffer length then we will
                    // just eat any remaining characters until we come to white space.
                    buffer[iBuffIndex++] = (iInput & 0x7f);
                }
                iState = StateAccept;
                break;
        }
    } while (! (iState == StateEnd));

    return buffer;    // be a good citizen and return the pointer provided to us. allows chaining.
}

long  scano_l (FILE *pFile)
{
    char   buffer[32] = {0};
    long   lValue = 0;
    char   *pTemp;

    lValue = strtol (fetchValue(pFile, buffer, 31), &pTemp, 10);  // max characters is 31 to ensure zero terminator.

    return lValue;
}

double  scano_d (FILE *pFile)
{
    char    buffer[32] = {0};
    double  dValue = 0.0;
    char    *pTemp;

    dValue = strtod (fetchValue(pFile, buffer, 31), &pTemp);  // max characters is 31 to ensure zero terminator.

    return dValue;
}

和其他方便的功能将是读取一串字符的功能。以下函数从输入中读取字符,并将其添加到字符缓冲区,直到读取结束字符或读取最大字符数为止。

非空格空格字符(制表符,换行符,返回符)被视为文本指示符的结尾。现在,空格字符被认为是有效的文本字符,该字符已添加到根据输入构造的字符串中。会丢弃所有前导的非空格空格,并将文本字符串视为从第一个字符开始,该字符不是非空格空格字符。

char *  scano_asz(FILE *pFile, char *buffer, int nMaxLen)
{

    int    iBuffIndex = 0;
    enum StateFSM   iState = StateBegin;

    do {
        // until we reach an end state of our state machine or we reach end of file
        // on our input source, lets fetch characters from the input source and decide
        // what to do with the character until our end state is reached.
        // end state is either white space trailing the desired characters or end of file.
        int    iInput = fgetc(pFile);

        if (feof(pFile)) break;
        switch (iInput) {
        case '\t':
        case '\n':
        case '\r':
            // eat up any leading non-space whitespace. spaces embedded in the string are
            // considered part of the string. delimiters include tab, new line, return.
            if (iState != StateAccept) break;
            // we have found trailing non-space white space so we are done.
            iState = StateEnd;
            break;
        default:
            if (iBuffIndex < nMaxLen) {
                // as long as we are not at the max length lets get a character into
                // the supplied buffer. allowable characters include the space character
                // but not other white space characters such as tab, new line, return.
                buffer[iBuffIndex++] = (iInput & 0x7f);
                if (iBuffIndex >= nMaxLen) break;    // once we reach max size then we will break and exit.
            }
            iState = StateAccept;
            break;
        }
    } while (!(iState == StateEnd));

    if (iBuffIndex < nMaxLen) buffer[iBuffIndex] = 0;   // terminate the string if there is roome in the buffer.
    return buffer;
}