【问题标题】:reading name and characters from a file从文件中读取名称和字符
【发布时间】:2016-09-02 03:17:24
【问题描述】:

我在弄清楚如何使用循环从具有数字的文件中读取名称时遇到问题。例如

科比布莱恩特 81 62 60

我知道如何将每个字符串和数字分别扫描到一个变量中,但我不知道如何更简单。

while ((chh = fgetc(input)) != EOF)
{
    printf("%c", chh);
}

这会读取每一行直到 EOF,但我不知道如何让它停在科比布莱恩特。我可以解决此问题的一种方法是在循环中创建一个 c 字符串,使其停止某种方式并将其设置为等于一个变量。

旁注:如果在科比·布莱恩特手下还有另外 2 名球员姓名和数字,我将如何处理?

谢谢!

【问题讨论】:

  • fscanf 有什么问题?
  • 自己解析输入的每一部分并不是我所说的“更简单”。 :)
  • 输入的末尾是否总是包含三个数字?然后您可以读取整行(例如fgets)并将字符串“拆分”为第一个数字之前的两个子字符串。使用例如sscanf 解析三个数字并从第一个子字符串中复制名称。您可以通过从末尾找到第三个空格来拆分字符串并将字符串终止符放在那里。
  • 文件包含实际上包含10个数字。但我想我知道要解释什么。所以也许是一个 do while 循环并设置了一个等于 fgets 的字符串变量,但我被告知不要使用 sscanf。另外,谢谢你帮助我
  • 那么您可能想查看strtok 将第二个子字符串(带有数字)拆分成更小的子字符串,然后使用例如strtol 转换所有数字。

标签: c


【解决方案1】:

在您担心阅读包含名称的多行之前,请先正确阅读其中的一行。当使用面向字符的输入时,你有两种基本的方法来填充你的数组。 (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

注意:代码还考虑了名称后面没有数字的行)

查看示例。确实没有捷径。使用面向字符的输入时,您必须考虑到每个预期的字符。 (为了使您的代码更加健壮,您还应该考虑每个未预料到的字符,但这留到另一天)。如果您有任何问题,请告诉我。

【讨论】:

  • 我忘了谢谢你,这对我帮助很大!谢谢!你真棒!
  • 很高兴我能帮上忙。 C 为您提供了灵活性和控制力,让您可以做任何您可能需要的事情。但是,这需要更多的努力和时间来学习。花在学习 C 上的时间是值得的,无论您最终使用哪种语言进行编码,都将帮助您成为一名更好的程序员。祝您编码顺利。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 2013-06-18
  • 1970-01-01
  • 2021-04-27
相关资源
最近更新 更多