因此,为简化我的问题,我创建了这个更具模块化的程序,该程序首先在我的'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通常避免使用
camelCase
或MixedCase
变量名,而支持所有小写字母,同时保留大写字母名称以供宏和常量使用。这是风格问题-因此完全取决于您,但是如果不遵循它,可能会在某些圈子中导致错误的第一印象。
关于c - NOOB C编程问题:我在将代码中从文件读取的值插入整个代码中时遇到麻烦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53039650/