【问题标题】:How to use fscanf and fgets from a text file with ints and strings?如何使用带有整数和字符串的文本文件中的 fscanf 和 fgets?
【发布时间】:2018-05-11 08:20:39
【问题描述】:

示例文件:

School Name (with spaces)
FirstNameofStudent StudentID
School Name2 (with spaces)
FirstNameofStudent2 StudentID2
School Name3 (with spaces)
FirstNameofStudent3 StudentID3

在第一行使用 fgets 后,我似乎不知道该怎么做。

如果单独使用,我可以使用fgets 轻松获取学校名称 第二行本身使用fscanf,然后使用atoistudentID转换为int。

但是如何将fgetsfscanf 结合起来呢?

扫描的信息将被放入一个数组中。

【问题讨论】:

  • fgets() 之后调用fscanf() 有什么问题?此外,您可以只 fgets() 所有行,然后在字符串上使用 sscanf() 来表示具有多个字段的行。
  • 等等,你想要一个既读取一行又格式化它的命令?这不是fscanf 吗?除非您只是想要一种将信息放在字符串数组中的方法?
  • "我自己可以使用 fgets 轻松获取学校名称,使用 fscanf 自己获取第二行" --> 发布该代码。

标签: c scanf fgets


【解决方案1】:

在讨论解决方案之前,我想指出一个数组只能有一个类型。您不能将整数存储在字符数组中。

意思是如果您希望所有这些信息都放在一个二维数组中,则需要将 ID 存储为字符串。然后,如果您需要它作为整数,则需要在检索到它后使用atoi

现在要将两者结合起来,您需要一个带有fgets 的while 循环,以便检索第一行。像这样的:

while(fgets(schoolname, 255, fp) != 0){ ... }

这将继续检索行,直到它失败或到达 EOF。但是,您想在第二行使用fscanf,为此,您需要这样的一行:

fscanf(fp, "%s %s\n", name, id);

这意味着,从当前点开始,有两个字符串由空格和换行符分隔。将这两个字符串存储在nameid 中,然后吞噬换行符。

吞噬换行符是关键,如果你不这样做,下次fgets 运行时,它只会在行上找到一个换行符。

至于将元素存储在数组中,您需要一个二维字符串数组。为此,您可以固定或动态进行。对于固定,这很容易,只需要像char students[3][3][80] 这样的一行,然后在其中简单地存储东西,但对于动态,您需要使用内存分配、指针等,以及变量喜欢char ***students

这是我用来解决您的问题的代码,但我建议您也尝试自己做,以掌握它:

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

int main(){
    FILE *fp = fopen("ex.txt", "r");

    if(fp == 0) exit(-1);

    char ***students;

    // Used to check how many students there are
    int studentno = 0;

    // Init students array
    // So far it means 1 ROW, with 3 COLUMNS, each of 80 CHARACTERS
    students = calloc(studentno+1, 3 * 80);

    // Temporary variables for storage
    char schoolname[80];
    char name[20];
    char id[10];

    int i = 0;

    while(fgets(schoolname, 255, fp) != 0){
        studentno++;

        // Retrieve name and id from second line
        fscanf(fp, "%s %s\n", name, id);

        // Cut off newline left from fgets
        schoolname[strlen(schoolname)-2] = 0;

        // Allocate memory for new members of array
        students[i] = malloc(3 * 80);
        students[i][0] = malloc(80);
        students[i][1] = malloc(80);
        students[i][2] = malloc(80);

        // Copy strings received into array
        strcpy(students[i][0], schoolname);        
        strcpy(students[i][1], name);
        strcpy(students[i][2], id);

        // Resize students array for additional students
        students = realloc(students, (size_t) (studentno+1) * 3*80);

        i++;
    }

    // Check students are stored correctly
    for(int i = 0; i < studentno-1; i++){
        printf("%s - %s - %s\n", students[i][0], students[i][1], students[i][2]);
    }
}

【讨论】:

  • 帮助很大!
  • 假设我们有一个未知大小的文件。将文件中的信息分配到结构/链表中会更容易吗?
  • 对于未知大小的文件,这仍然可以工作,因为无论文件有多大,它都可以工作。至于另一个问题,从技术上讲,您可以拥有一个结构数组并消除类型限制。构造链表比简单地拥有一个结构数组和每次添加时realloc'ing 稍微困难一些。
  • 关于:schoolname[strlen(schoolname)-2] = 0; 这个语句“可能”或“可能不会”实际上切断了换行符。首先,C 中的索引从 0 而不是 1 开始,函数:strlen() 将索引返回到字符串末尾的 NUL 字节。所以,充其量,这个语句将切断换行符和换行符之前的字符。建议使用:schoolname[ strspn( schoolname, "\n" ) ] = '\0'; 虽然分配 '0' 可能会由于隐含转换导致字符被 0x00 替换,(如果使用选项,gcc 编译器会报错:-Wconversion
  • 在调用任何堆分配函数时:malloccallocrealloc,始终检查 (!=NULL) 返回值以确保操作成功
猜你喜欢
  • 1970-01-01
  • 2018-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-16
  • 1970-01-01
  • 2012-07-17
  • 2021-11-09
相关资源
最近更新 更多