c - NOOB C编程问题:我在将代码中从文件读取的值插入整个代码中时遇到麻烦

标签 c file readfile

因此,为简化我的问题,我创建了这个更具模块化的程序,该程序首先在我的'main()'以及我的其他两种方法的切换案例中要求用户输入。我在下面有这段代码,在我的readDigits()方法中,我想知道如何从value到'firstDigit和secondDigit中获取特定的值。说value == 5时,我希望'5'进入firstDigit

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

int firstDigit, secondDigit, input;

void getNumbers() {
   printf("Enter these digits: \n");
   scanf("%d", &firstDigit);
}

void getMoreNumbers() {
   printf("Enter some more digits: \n");
   scanf("%d", &secondDigit);
}

int readDigits(int value) {
   FILE *fp
   fp = fopen("digits.txt", "r");
   if(fp == NULL) {
     printf("Failed to open file");
     return -1;
   }
   while(fscanf(fp, "%d", &value)==1){
      printf("%d ", value);


**#I was thinking of doing these 'if' checks whenever value has a number that 
#I would want 'firstDigit' ,'secondDigit' , or 'input' to be. But then I 
#figured that this would be too tedious and not efficient If I were to read 
#a file with a lot of data in it.**

      if(value== 1){
         firstDigit = value;
      }
  }   
  fclose(fp);
}  

int main() {
   while(input < 3){
   printf("Please select which method you'd like to access:\n1) getNumbers()\n2getMoreNumbers()");
  // I also want input's value to be read from digits.txt
  scanf("%d", &input);  
  switch(input){
     case 1:
        getNumbers();
        break;
     case 2:
        getMoreNumbers();
        break;
     default:
        printf("Program complete");
     }  
  }
  return 0;
}


我知道这几乎不是最困难的事情,但是,我是C语言的新手,确实感到很压力,这是一个简单的问题。

我的digits.txt文件具有以下值:


  1个
  10
  299
  1个
  200
  15
  3
  150
  13
  2
  150

最佳答案

感谢您发布输入文件。您的问题未得到广泛认可的原因是,很难确定您要达到的目标。很明显,您需要帮助,但是从哪里开始到底是个谜。

为什么?对于初学者,您的getNumbers() [1]和getMoreNumbers()函数执行完全相同的操作,只是使用不同的全局变量。没有理由在任何函数中使用全局变量,因为可以很容易地将它们作为参数传递给需要它们的任何函数。更一般而言,除非绝对必要,否则您要避免使用全局变量(存在对全局变量的合法使用,但是在您初学C时可能不会遇到的全局变量-避免使用它们)

接下来,如果您从该答案中没有获得其他任何帮助,请学习始终通过至少检查所使用函数的返回来验证用户输入。否则,您会盲目地使用输入变量来邀请未定义行为,而无需任何验证它实际上是否包含有效数据。

退后一步,尝试了解您想要做的事情,看来您的目标是双重的,(1)根据菜单选择从stdin读取整数,或者(2)从文件"digits.txt"读取(如果有替代方法)进行了菜单选择。但是,永远不会调用readDigits()函数。此外,如果要从"digits.txt"以外的文件读取该怎么办?为什么不简单地提示用户输入文件名,然后打开该文件(处理任何错误),从文件中读取所有整数,然后在完成后关闭文件?

那么,我们如何实现这两个目标,要么从stdin读取一个整数,要么从用户给定的文件名中读取所有整数?

首先,您必须完全验证用户提供的所有输入,然后对该输入进行操作以处理可能出现的任何错误。 scanf(及其功能系列)对于新的C程序员来说充满了陷阱。例如,在用户发生的任何scanf ("%d", ...)函数调用中,用户意外地键入了一个字母字符而不是一个“数字”吗?尝试一下。

使用scanf时,如果输入与使用转换说明符不匹配,则会发生匹配失败。发生匹配故障时,将停止从输入缓冲区读取,不再提取其他字符,而将故障发生时存在的所有字符都保留在输入缓冲区中(在您的情况下为stdin)未读,只是等待您咬一下再次尝试下一次阅读。此外,仅数字转换说明符和"%s"占用前导空格。

意思是用于输入的任何其他转换说明符(或与此相关的面向行输入函数)都无法读取任何内容(当遇到用户按下Enter并留在'\n'中生成的stdin时停止)将读取'\n'作为用户输入。都不是你想要的。

您如何确保从用户那里获得的每个输入都将是用户输入的?您可以在每次输入后清空输入缓冲区来进行清理,以确保先前输入尝试后stdin中没有多余的字符或其他字符。 (这也是为什么鼓励新的C程序员避免使用scanf进行用户输入,而是使用面向行的输入函数,例如fgets()(具有足够的缓冲区大小)或POSIX getline来读取并分配为这两个面向行的函数都读取'\n'并将其包括在填充的缓冲区中,从而消除了将其保留在输入缓冲区中而不被读取的可能性。

如果选择继续使用scanf,则每次有可能在stdin中保留未读字符时,至少使用一个简单函数手动清空输入缓冲区(stdin)。一个简单的功能就可以做到。只需使用getchar()读取每个字符,直到遇到'\n'EOF,例如

void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}


注意:如果函数不带参数,则正确的声明包括(void)作为参数列表,以明确表明不希望有参数。

现在,通过一种可以正确处理除去stdin中保留的字符的方法,您可以查看输入函数了。

如果您使用getNumbers() [1]和getMoreNumbers()的目的是简单地从stdin读取整数,则只需使用一个具有稍微更具描述性的名称的函数来完成工作,例如:

int readintstdin (int *value) {

    int rtn = 0;

    printf ("Enter digits: ");

    rtn = scanf ("%d", value);     /* read 1 integer */

    if (rtn == EOF)
        fputs ("(user canceled input.)\n", stderr);
    else if (rtn == 0)
        fputs (" readintstdin() error: invalid input.\n", stderr);

    empty_stdin();  /* remove all remaining chars from stdin */

    return rtn;
}


注意:使用scanf时,您有责任处理以下三种情况:


用户通过按Ctrl + d(或Windows上的Ctrl + z)手动生成EOF来取消输入;
匹配或输入失败;和
好的输入案例,然后您必须验证收到的输入是否在预期范围内,等等。


考虑到这一点,您可以设计一个从stdin读取的函数,类似于:

int readintstdin (int *value) {

    int rtn = 0;

    printf ("Enter digits: ");

    rtn = scanf ("%d", value);     /* read 1 integer */

    if (rtn == EOF)
        fputs ("(user canceled input.)\n", stderr);
    else if (rtn == 0)
        fputs (" readintstdin() error: invalid input.\n", stderr);

    empty_stdin();  /* remove all remaining chars from stdin */

    return rtn;
}


注意两件事,值的**地址作为参数传递,允许输入存储在该内存地址并在调用函数(此处为main())中可用,并且scanf的返回值作为函数返回,指示在main()中回读成功/失败。您必须始终编写具有适当返回类型的函数,该返回类型将在调用函数中指示该函数的成功或失败。否则,没有检查开始的scanf返回也没什么不同。

您的下一个目标似乎是从"digits.txt"读取所有值。由您自己决定,通常的方法是在调用方中打开FILE* stream以确认文件已打开以供读取,然后再将该文件流作为参数传递给函数。否则,如果打开失败,则无需调用函数开头,例如:

int readfromfile (FILE *fp) {

    int value = 0, count = 0;

    while (fscanf (fp, "%d", &value) == 1) {    /* while valid int read */
        printf ("%d ", value);
        count++;
    }

    putchar ('\n'); /* tidy up with newline */

    return count;   /* return value indicating number of values read */
}  


在此处指示从文件读取的成功/失败,将返回从文件读取的值的数量。如果返回值为0,则不会从文件中读取任何值。

接下来,您的菜单。您需要将这些相同的课程应用于菜单。这意味着您需要处理scanf的所有三种情况,并删除菜单每次显示之间stdin中保留的所有字符。采取这些保护措施,您可以执行以下操作,例如

#define MAXC 1024
...
int main (void) {

    int input = 1,          /* initialize all variables */
        value = 0;
    char fname[MAXC] = "";  /* buffer to hold filename to read */
    FILE *fp = NULL;

    while (0 < input && input < 3) {
        int rtn = 0;    /* store scanf return */
        printf ("\nPlease select which method you'd like to access:\n"
                " 1) readintstdin()\n"
                " 2) readfromfile()\n"
                " (any other numeric input exits)\n\n"
                " choice: ");
        rtn = scanf ("%d", &input);
        if (rtn == EOF) {   /* user generated manual EOF */
            fputs ("(user canceled input)\n", stderr);
            break;
        }
        else if (rtn == 0) {    /* no integer input */
            fputs (" error: invalid integer input.\n", stderr);
            empty_stdin();  /* empty all chars from stdin */
            continue;       /* re-display menu, try again */
        }
        else    /* good integer input */
            empty_stdin();  /* empty all chars from stdin */
        ...


剩下的就是处理switch上的input。调用readintstdin的第一种情况很简单,只需调用readintstdin并传递value的地址作为其参数,并在打印value之前检查函数返回是否为非零,例如

        switch (input) {    /* switch on integer input */
            case 1:
                if (readintstdin (&value))  /* read from stdin */
                    printf ("value: %d\n", value);
                break;


第二种情况需要更多考虑。您将从用户那里输入文件名(因此,在fname开头使用变量main()的目的)。在这里,您只需提示并读取文件名(可能包含空格),验证读取的内容,然后将文件名传递给fopen验证文件已打开以供读取。确认文件已打开以供读取后,您只需将打开的文件流传递给函数(检查函数的返回是否成功),然后在完成后关闭文件。可以类似于执行以下操作:

            case 2:
                printf ("\n enter filename: "); /* get filename to read */
                if (scanf ("%1023[^\n]", fname) != 1) {
                    fputs ("(user canceled input)\n", stderr);
                    empty_stdin();
                    break;
                }
                /* open/validate file open for reading */
                if ((fp = fopen (fname, "r")) == NULL) {
                    perror ("fopen-fname");
                    empty_stdin();
                    break;
                }
                if (!readfromfile (fp)) /* read/output integers */
                    fprintf (stderr, "error: no values read from '%s'\n",
                            fname);
                fclose (fp);        /* close file */
                break;


注意:您的default:案例应告知用户已收到不在接受的菜单值之内的值,然后您可以指示程序已完成。 (这只是一个小问题,您可以按照自己喜欢的任何方式进行处理)

综上所述,您可以执行以下操作:

#include <stdio.h>

#define MAXC 1024

void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

int readintstdin (int *value) {

    int rtn = 0;

    printf ("Enter digits: ");

    rtn = scanf ("%d", value);     /* read 1 integer */

    if (rtn == EOF)
        fputs ("(user canceled input.)\n", stderr);
    else if (rtn == 0)
        fputs (" readintstdin() error: invalid input.\n", stderr);

    empty_stdin();  /* remove all remaining chars from stdin */

    return rtn;
}

int readfromfile (FILE *fp) {

    int value = 0, count = 0;

    while (fscanf (fp, "%d", &value) == 1) {    /* while valid int read */
        printf ("%d ", value);
        count++;
    }

    putchar ('\n'); /* tidy up with newline */

    return count;   /* return value indicating number of values read */
}  

int main (void) {

    int input = 1,      /* initialize all variables */
        value = 0;
    char fname[MAXC] = "";
    FILE *fp = NULL;

    while (0 < input && input < 3) {
        int rtn = 0;    /* store scanf return */
        printf ("\nPlease select which method you'd like to access:\n"
                " 1) readintstdin()\n"
                " 2) readfromfile()\n"
                " (any other numeric input exits)\n\n"
                " choice: ");
        rtn = scanf ("%d", &input);
        if (rtn == EOF) {   /* user generated manual EOF */
            fputs ("(user canceled input)\n", stderr);
            break;
        }
        else if (rtn == 0) {    /* no integer input */
            fputs (" error: invalid integer input.\n", stderr);
            empty_stdin();  /* empty all chars from stdin */
            continue;       /* re-display menu, try again */
        }
        else    /* good integer input */
            empty_stdin();  /* empty all chars from stdin */

        switch (input) {    /* switch on integer input */
            case 1:
                if (readintstdin (&value))  /* read from stdin */
                    printf ("value: %d\n", value);
                break;
            case 2:
                printf ("\n enter filename: "); /* get filename to read */
                if (scanf ("%1023[^\n]", fname) != 1) {
                    fputs ("(user canceled input)\n", stderr);
                    empty_stdin();
                    break;
                }
                /* open/validate file open for reading */
                if ((fp = fopen (fname, "r")) == NULL) {
                    perror ("fopen-fname");
                    empty_stdin();
                    break;
                }
                if (!readfromfile (fp)) /* read/output integers */
                    fprintf (stderr, "error: no values read from '%s'\n",
                            fname);
                fclose (fp);        /* close file */
                break;
            default:    /* handle invalid input */
                fputs ("Selection not menu entry.\n", stderr);
                break;
        }  
    }
    printf ("Program complete\n");

    return 0;
}


注意:除非使用的是stdlib.h提供的函数或常量,否则没有理由包含标题。并没有什么坏处,只是显示了对所需头文件的误解。

使用/输出示例

现在开始练习程序,并在每个提示中输入不正确/无效的输入,有意地尝试破坏输入例程。如果出现故障,请修复它。我并没有尝试所有的极端情况,但是了解了输入功能的工作原理之后,就可以防止大多数极端情况发生。查看第一个整数输入"four-hundred twenty-one",程序会中断吗?否。查看无效的文件名"somefile.ext",程序会中断吗?不。对您的每个输入都执行此操作。

$ ./bin/menureadfilestdin

Please select which method you'd like to access:
 1) readintstdin()
 2) readfromfile()
 (any other numeric input exits)

 choice: 1
Enter digits: four-hundred twenty-one
 readintstdin() error: invalid input.

Please select which method you'd like to access:
 1) readintstdin()
 2) readfromfile()
 (any other numeric input exits)

 choice: 1
Enter digits: 421
value: 421

Please select which method you'd like to access:
 1) readintstdin()
 2) readfromfile()
 (any other numeric input exits)

 choice: 2

 enter filename: somefile.ext
fopen-fname: No such file or directory

Please select which method you'd like to access:
 1) readintstdin()
 2) readfromfile()
 (any other numeric input exits)

 choice: 2

 enter filename: dat/digits.txt
1 10 299 1 200 15 3 150 13 2 150

Please select which method you'd like to access:
 1) readintstdin()
 2) readfromfile()
 (any other numeric input exits)

 choice: 1
Enter digits: 422
value: 422

Please select which method you'd like to access:
 1) readintstdin()
 2) readfromfile()
 (any other numeric input exits)

 choice: (user canceled input)
Program complete


希望这可以解决您的大多数问题,并帮助您避免“真正感到压力很简单的压力”。是的,但是C需要远远超出“我会尝试这一点,如果它不起作用,我会更改某些内容,重新编译并重试...”之外的深刻理解。您可以控制整个机器并负责用于处理输入缓冲区的每个字节,并确保程序处理的每个字节有足够的存储空间。 C上没有训练轮。这就是C产生极快速度的地方。但是,“权力越大,责任就越大”。

脚注:

1.尽管不是错误,但C通常避免使用camelCaseMixedCase变量名,而支持所有小写字母,同时保留大写字母名称以供宏和常量使用。这是风格问题-因此完全取决于您,但是如果不遵循它,可能会在某些圈子中导致错误的第一印象。

关于c - NOOB C编程问题:我在将代码中从文件读取的值插入整个代码中时遇到麻烦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53039650/

相关文章:

python - Windows错误 : [Error 32] The process cannot access the file because it is being used by another process: 'new.dat'

file - Perl:检查文件(-f 与 !-d)

node.js - readFile 是否缓存内容?

c - 扫描到数组

c - 按其中一个字符串值的字母顺序对结构数组进行排序的函数

c - '->' doesnt doesn' t 适用于结构内部的结构

java - 如何将 String 数组存储为枚举类型

c - 阿尔萨 API : how to use mmap in c?

java - 关于将 GUI 程序制作为 exe 文件.... (Java)

计算文件中的行数 - C