在您担心阅读包含名称的多行之前,请先正确阅读其中的一行。当使用面向字符的输入时,你有两种基本的方法来填充你的数组。 (1) 使用数组索引,并在读取每个有效字符时,在当前索引处添加到数组中;或 (2) 使用指向数组的指针,并在将每个有效字符添加到数组时递增指针。
在读取/存储每个字符时,您至少需要验证两个条件,(1) 您没有尝试写入超出数组末尾的内容(为 nul-terminating 字符留出空间,以及 (2) 您终止对 EOF 的阅读。
剩下的只是(1)何时停止向数组中添加字符和(2)应该放置 nul-terminating 字符的冷逻辑。对于任何这样的学习练习,最简单的方法是拿出一支铅笔和一张纸,写下一个示例行,然后逐步遍历每个字符,在你进行的过程中编码你的条件。我不是在开玩笑,在纸上写下类似以下的内容,然后按照逻辑进行操作:
|K|o|b|e| |B|r|y|a|n|t| |8|1| |6|2| |6|0|\n|
当您阅读每个字符时,确定需要什么逻辑来确定是否存储该字符,是否继续阅读,或者是否读取/丢弃该行中的所有剩余字符(从您遇到第一个无效字符开始特点)。您还必须确定在读取多行时如何处理'\n'。
一个使用数组索引的例子是:
enum { MAXL = 64 }; /* constant for max name length */
...
char name[MAXL] = ""; /* char array to hold name */
int c = 0, idx = 0; /* var for char & array index */
while (idx + 2 < MAXL && /* for each char in line */
((c = fgetc (fp)) != '\n' && c != EOF))
{ /* if valid char */
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|| c == '.' || c == ' ') {
name[idx++] = c; /* write char to name, advance */
}
else { /* if not valid char - read to '\n' or EOF */
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
break;
}
}
if (idx) { /* if name contains chars - check last char */
if (name[idx-1] == '.' || name[idx-1] == ' ')
name[--idx] = 0; /* if not letter, overwrite prior */
else
name[idx] = 0; /* nul-terminate at current idx */
printf (" name: '%s'\n", name);
}
使用指针和结束指针的相同逻辑体将是:
char name[MAXL] = ""; /* char array to hold name */
char *p, *ep; /* current pointer & end pointer */
int c = 0; /* var for current character */
p = ep = name; /* initialize both to name */
while (p + 2 < name + MAXL && /* for each char in line */
((c = fgetc (fp)) != '\n' && c != EOF))
{ /* if valid char */
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|| c == '.' || c == ' ') {
ep = p; /* set end ptr to current positon */
*p++ = c; /* write char to current, advance */
}
else { /* if not valid char - read to '\n' or EOF */
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
break;
}
}
if (p > name) { /* if name contains chars */
if (*ep == '.' || *ep == ' ')
*ep = 0; /* nul-terminate prior */
else
*p = 0; /* nul-terminate current */
printf (" name: %s\n", name);
}
要读取多个名称,您只需将读取每一行的代码包装在一个连续循环中并检查最后是否有c == EOF。例如
for (;;) { /* loop continually over each line in file */
...
if (c == EOF) /* if EOF, break */
break;
}
将所有部分放在一起(使用指针而不是索引)的简短示例可能类似于:
#include <stdio.h>
enum { MAXL = 64 }; /* constant for max name length */
int main (int argc, char **argv) {
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
for (;;) { /* loop continually over each line in file */
char name[MAXL] = ""; /* char array to hold name */
char *p, *ep; /* current pointer & end pointer */
int c = 0; /* var for current character */
p = ep = name; /* initialize both to name */
while (p + 2 < name + MAXL && /* for each char in line */
((c = fgetc (fp)) != '\n' && c != EOF))
{ /* if valid char */
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|| c == '.' || c == ' ') {
ep = p; /* set end ptr to current positon */
*p++ = c; /* write char to current, advance */
}
else { /* if not valid char - read to '\n' or EOF */
while ((c = fgetc (fp)) != '\n' && c != EOF) {}
break;
}
}
if (p > name) { /* if name contains chars */
if (*ep == '.' || *ep == ' ')
*ep = 0; /* nul-terminate prior */
else
*p = 0; /* nul-terminate current */
printf (" name: %s\n", name);
}
if (c == EOF) /* if EOF, break */
break;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
示例输入
$ cat dat/namesnums.txt
Kobe Bryant 81 62 60
John Doe 21 34
Alfred C. Jones 882
使用/输出示例
$ ./bin/fgetcname <dat/namesnums.txt
name: Kobe Bryant
name: John Doe
name: Alfred C. Jones
(注意:代码还考虑了名称后面没有数字的行)
查看示例。确实没有捷径。使用面向字符的输入时,您必须考虑到每个预期的字符。 (为了使您的代码更加健壮,您还应该考虑每个未预料到的字符,但这留到另一天)。如果您有任何问题,请告诉我。