简体   繁体   English

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

[英]Reading 3 strings from a text file

I am having trouble reading 3 strings from one line, moving to the next one, and putting them in my struct correctly.我无法从一行读取 3 个字符串,移动到下一个,并将它们正确地放入我的结构中。

My text file looks like this:我的文本文件如下所示:

- John Johnson Math
- Eric Smith Biology
- etc

I need to arrange students by class they have chosen.我需要按学生选择的班级来安排他们。 How should i read first string as name then blank, second as last name, and third by class and do that for every line, then store correctly in my struct?我应该如何读取第一个字符串作为名称然后是空白,第二个作为姓氏,第三个作为类并为每一行执行此操作,然后正确存储在我的结构中? Here is what I have right now:这是我现在所拥有的:

#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';
}
}

Continuing from the comment, why are you approaching this problem with a linked-list ?继续评论,你为什么用链表来解决这个问题? You can use a linked list, but the overhead and code complexity is more than required.您可以使用链表,但开销和代码复杂度超出了要求。 A straight forward solution is a simply dynamic array of type students .直接的方案是类型的简单动态数组students The benefits of an array or significant.数组的好处还是很大的。 Direct access to all student, simple sorting with a single call to qsort , simple additions, etc..直接访问所有学生,通过一次调用qsort简单排序,简单添加等。

Don't get me wrong, if you assignment is to use a linked-list , by all means do, but you should really look at an dynamic array of type student , where you can realloc as needed to add as many students as you may like.不要误会我的意思,如果你的任务是使用链表,通过各种手段做,但你应该看看类型的动态数组student ,在那里你可以realloc需要更多的学生你可能添加喜欢。

For example:例如:

#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;
}

( note: if your data file really doesn't begin each line with '- ' , then just remove that from the sscanf format-string ) 注意:如果您的数据文件确实没有以'- '开始每一行,那么只需将其从sscanf格式字符串中删除)

Example Input示例输入

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

Example Use/Output示例使用/输出

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

students:

 Johnson, John                                                Math
 Smith, Eric                                                  Biology

Memory Error/Check内存错误/检查

In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.在您编写的任何动态分配内存的代码中,您对分配的任何内存块都有 2 个责任:(1) 始终保留一个指向内存块起始地址的指针,因此,(2) 当它不是时可以释放它不再需要。

It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.您必须使用内存错误检查程序来确保您没有在分配的内存块之外/之外写入,尝试读取或基于未初始化的值进行跳转,并最终确认您已释放所有内存已分配。

For Linux valgrind is the normal choice.对于 Linux valgrind是正常的选择。 eg例如

$ 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)

Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts .始终确认所有堆块都已释放——不可能发生泄漏,而且同样重要错误摘要:0 个上下文中的 0 个错误

Look it over an let me know if the array approach fits your needs and if you have any questions.查看它,让我知道数组方法是否适合您的需求以及您是否有任何问题。

Sorting by class memberclass成员排序

In order to sort the array of struct students by the class , the easiest way is to use the qsort function.为了数组排序struct studentsclass ,最简单的方法是使用qsort功能。 It is the standard sorting function provided by the C library (include stdio.h ).它是 C 库(包括stdio.h )提供的标准排序函数。 You can sort by any member of the students struct.您可以按students结构的任何成员进行排序。 You can even sort by class, and by name.您甚至可以按类别和名称排序。

The only problem new programmers have with qsort is writing the compare function to pass to qsort in order to have it sort in the desired order.新程序员对qsort的唯一问题是编写比较函数以传递给qsort ,以便按所需顺序对其进行排序。 The compare function will receive a pointer to two elements in your array of struct student.比较函数将接收一个指向学生结构数组中两个元素的指针。 The parameters passes are passed as void * ( const void * actually).传递的参数作为void * ( 实际上是const void * ) 传递。 Just like any void pointer, you must cast it to the proper type before you can dereference it.就像任何void指针一样,您必须先将其强制转换为正确的类型,然后才能取消引用它。 (in this case students * ) Therefore, you simply need a function that casts the void pointers to students * and passes the values to strcmp . (在这种情况下students * )因此,您只需要一个将void指针转换为strcmp students *并将值传递给strcmp的函数。 Example:例子:

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

The only thing left is calling qsort (after fclose in the code):唯一剩下的就是调用qsort (在代码中的fclose之后):

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

Your output is then sorted by class.然后您的输出按类排序。

If you then want to sort further by last name after sorting by class , instead of returning on the strcmp for class , test if the result is not equal to zero, and return that result.如果您想在按class排序后按last进一步排序,而不是在classstrcmp上返回,请测试结果是否不等于零,并返回该结果。 If the result of the strcmp on class is zero, then you simply return strcmp (((students *)a)->last, ((students *)b)->last);如果classstrcmp的结果为零,那么您只需return strcmp (((students *)a)->last, ((students *)b)->last); That simply sorts by class first, but if the class is the same, sorts further by last .这只是首先按class排序,但如果类相同,则按last进一步排序。 For example:例如:

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);
}

Example Input示例输入

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

Example Use/Output示例使用/输出

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

students:

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

Take the time to learn qsort .花时间学习qsort

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM