c - 从文本文件中读取 3 个字符串

标签 c

我在从一行读取 3 个字符串、移动到下一行并将它们正确放入我的结构中时遇到问题。

我的文本文件如下所示:

- John Johnson Math
- Eric Smith Biology
- etc

我需要按学生选择的类(class)来安排他们。我应该如何将第一个字符串读取为名称,然后读取空白,第二个字符串读取为姓氏,第三个字符串读取为类别,并对每一行执行此操作,然后正确存储在我的结构中?这是我现在拥有的:

#include <stdio.h>
#include <stdlib.h>
#define MAX 30

typedef struct students {
    char nameFirst[MAX];
    char NameLast[MAX];
    char choClass[MAX];
    struct students *next;
} Students;

int main() {
    FILE *in;
    Students *first = NULL, *New = NULL, *last = NULL, *old = NULL, *current = NULL;
    in = fopen("studenti.txt", "r");
    if (in == NULL)
    {
        printf("Cannot open file!\n");
        return 1;
    }
while (i = fgetc(in) != (int)(EOF))
{
    New = calloc(1, sizeof(Students));
    if (first == NULL)
        first = New;
    else
        last->next = New;

    j = 0;
    while (i != (int)(' '))
    {
        New->nameFirst[j++] = (char)i;
        i = fgetc(in);
    }
    New->nameFirst[j] = '\0';
}
}

最佳答案

继续评论,你为什么用链接列表来解决这个问题?您可以使用链表,但开销和代码复杂性超出了要求。一个直接的解决方案是 students 类型的简单动态数组。 。数组的好处还是显着的。直接访问所有学生,只需调用 qsort 即可简单排序,简单的添加等等。

不要误会我的意思,如果您的分配是使用链接列表,那么一定要这样做,但您确实应该查看 student 类型的动态数组。 ,您可以在哪里realloc根据需要添加任意数量的学生。

例如:

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

enum { MAXC = 30, MAXS = 60, MAXLN = 128 };

typedef struct students {
    char first[MAXC];
    char last[MAXC];
    char class[MAXC];
} students;

int main (int argc, char **argv) {

    students *array = NULL;
    size_t i, idx = 0, maxs = MAXS;
    char buf[MAXLN] = "";
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* allocate/validate maxs students in array */
    if (!(array = malloc (maxs * sizeof *array))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    while (fgets (buf, MAXLN, fp)) {    /* read each line into buf */
        /* separate in to struct members */
        if (sscanf (buf, "- %s %s %s", array[idx].first,
                    array[idx].last, array[idx].class) != 3)
            continue;

        if (++idx == maxs) {    /* check against current allocations */
            void *tmp = realloc (array, (maxs + MAXS) * sizeof *array);
            if (!tmp) {         /* valdate realloc array succeeded */
                fprintf (stderr, "error: realloc memory exhausted.\n");
                break;          /* or break and use existing data   */
            }
            array = tmp;        /* assign reallocated block to array */
            maxs += MAXS;       /* update current allocations size   */
        }
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    printf ("\nstudents:\n\n");     /* output formatted data   */
    for (i = 0; i < idx; i++) {
        char tmp[2 * MAXC + 2] = "";
        strcpy (tmp, array[i].last);
        strcat (tmp, ", ");
        strcat (tmp, array[i].first);
        printf (" %-60s %s\n", tmp, array[i].class);
    }
    putchar ('\n');

    free (array);   /* free all allocated memory */

    return 0;
}

(注意:如果您的数据文件确实不是以 '- ' 开头,那么只需将其从 sscanf 格式字符串中删除即可)

输入示例

$ cat dat/studentclass.txt
- John Johnson Math
- Eric Smith Biology
- etc.

示例使用/输出

$ ./bin/structstudents <dat/studentclass.txt

students:

 Johnson, John                                                Math
 Smith, Eric                                                  Biology

内存错误/检查

在您编写的动态分配内存的任何代码中,对于分配的任何内存块,您都有两个责任:(1)始终保留指向内存块起始地址的指针,因此,(2)可以在以下情况下释放它:不再需要它了。

您必须使用内存错误检查程序来确保您没有在分配的内存块之外进行写入,尝试读取或基于未初始化的值进行跳转,最后确认您已释放所有内存您分配的内存。

对于 Linux valgrind是正常的选择。例如

$ valgrind ./bin/structstudents <dat/studentclass.txt
==14062== Memcheck, a memory error detector
==14062== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14062== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==14062== Command: ./bin/structstudents
==14062==

students:

 Johnson, John                                                Math
 Smith, Eric                                                  Biology

==14062==
==14062== HEAP SUMMARY:
==14062==     in use at exit: 0 bytes in 0 blocks
==14062==   total heap usage: 1 allocs, 1 frees, 5,400 bytes allocated
==14062==
==14062== All heap blocks were freed -- no leaks are possible
==14062==
==14062== For counts of detected and suppressed errors, rerun with: -v
==14062== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

始终确认所有堆 block 均已释放 - 不可能发生泄漏并且同样重要错误摘要:0 个上下文中出现 0 个错误

仔细检查一下,让我知道数组方法是否适合您的需求以及您是否有任何疑问。

排序依据class成员(member)

为了对struct students的数组进行排序通过class ,最简单的方法是使用qsort功能。它是 C 库提供的标准排序函数(包括 stdio.h )。您可以按students中的任何成员进行排序结构。您甚至可以按类别和名称排序。

新程序员遇到的唯一问题是 qsort正在编写 compare 函数以传递给 qsort以便让它按所需的顺序排序。 compare 函数将接收指向 struct Student 数组中两个元素的指针。参数传递为 void * (实际上是const void *)。就像任何void一样指针,您必须先将其转换为正确的类型,然后才能取消引用它。 (在本例中 students * )因此,您只需要一个转换 void 的函数。指向students *的指针并将值传递给 strcmp 。示例:

int compare (const void *a, const void *b)
{ 
    return strcmp (((students *)a)->class, ((students *)b)->class);
}

唯一剩下的就是调用 qsort (在代码中的fclose之后):

qsort (array, idx, sizeof *array, compare);

然后您的输出将按类别排序。

如果您想按 last 进一步排序按 class 排序后的名称,而不是返回 strcmp对于 class ,测试结果是否不等于 0,并返回该结果。如果strcmp的结果上class为零,那么你只需 return strcmp (((students *)a)->last, ((students *)b)->last);这只是按class排序首先,但如果类相同,则按 last 进一步排序。例如:

int compare (const void *a, const void *b)
{ 
    int r;
    if ((r = strcmp (((students *)a)->class, ((students *)b)->class)))
        return r;

    return strcmp (((students *)a)->last, ((students *)b)->last);
}

输入示例

$ cat dat/studentclass.txt
- Wade Williams Biology
- John Johnson Math
- Eric Smith Biology
- etc.

示例使用/输出

$ ./bin/structstudents <dat/studentclass.txt

students:

 Smith, Eric                                                  Biology
 Williams, Wade                                               Biology
 Johnson, John                                                Math

花时间学习qsort .

关于c - 从文本文件中读取 3 个字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36902460/

相关文章:

mysql - 将 MySQL 结果转换为 int (C++)

c - 如何在C中增加具有多线程的结构的值

c - 外部结构数组错误: array type has incomplete element type

c - 仅 GET 命令正文,缺少 header

c++ - C 和 C++ 中静态变量存储在哪里?

c++ - 使用 SDL 捕获屏幕?

c++ - 如何查看我对一个文件使用了多少次 fopen?

c - 编译器是否允许消除无限循环?

c - 如何从 OpenSSL BIO 对象获取对等地址

c - 在 ubuntu 12.04 LTS 上安装 qt4-make