【问题标题】:Read txt file line by line in C在C中逐行读取txt文件
【发布时间】:2020-09-28 15:07:54
【问题描述】:

我想逐行读取一个 txt 文件,并且每一行存储在一个不同的变量中: 这是我要阅读的txt文件

Jenny
Woodbridge Ave 
Amber
Exeter street
Michael
Main Street
David
Plainfield ave

我确实喜欢


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

typedef struct info
{
   char name[20];
   char add[50];
}INFO;

int main(void){
    const char *fileName = "test.txt";
    FILE *file = fopen(fileName, "r");
    INFO* list = (INFO*)malloc(20*sizeof(INFO));


    readFromFile(file,list);
    fclose(file);
    free(list);
   return 0;
}
void readFromFile(FILE *file,INFO* list){
     int i = 0;
     while(!feof(file)){
      fscanf(file,"%s %s\n ",(list+i)->name,(list+i)->adds);
      i++;
     }
}

但我得到了

Name:           Jenny
Addr:   Woodbridge
------------------------------
Name:           Ave
Addr:   Amber
------------------------------
Name:           Exeter
Addr:   street
------------------------------
Name:           Michael
Addr:   Main
------------------------------
Name:           Street
Addr:   David
------------------------------
Name:           Plainfield
Addr:   ave

我只是编辑了一点 所以我需要使用 fgets 来逐行读取而不是 fscanf() 对吗?

【问题讨论】:

  • 你可以告诉计算机做你想让它做的事情。现在你告诉它做一些不同的事情,它正在做你告诉它做的事情。
  • 请发帖minimal reproducible example。特别是我们不知道您如何称呼read。另外你不应该使用名称read,因为已经有一个同名的标准函数,将来可能会导致其他问题。
  • 哦,%s %d %s 与参数不匹配。你可能想要%s %s
  • 首先,您对while(!feof(file)) 的使用是wrong,您必须在使用“读取”之前检查读取是否成功。其次,您的文件似乎没有通过%d 读取的内容。第三,undefined behavior 被调用,因为有 3 个输入指令,而只给出了 2 个指针。
  • 如果你想读行,你不应该使用scanf。你应该使用fgets。如果你使用scanf,你不能使用"%s"

标签: c file io


【解决方案1】:

%s 说明符读取输入流,直到找到一个空白字符,在您的情况下,每个地址有 2 个字,一旦您尝试读取它,它就会变得不平衡,地址的第二个字被读取下一次循环进入name,也存在潜在的缓冲区溢出问题。

你应该使用%49[^\n],这个说明符会读取所有内容,直到找到换行符,包括空格。 49 旨在限制读取行的大小以避免提到的缓冲区溢出,如果您有空间容纳 50 字符,最后一个字符将用于空终止符。

feof 也不是在这种例程中发出文件结束信号的最佳方式,Why is “while ( !feof (file) )” always wrong? 中的更多信息。

我在以下工作示例的 cmets 中解决了一些其他问题:

Online demo

int main()
{
    //casting malloc may make you program fail, it hides the lack of #include <stdlib.h>
    INFO* list = malloc(20 * sizeof *list); //using the the dereferenced variable 
                                            //is safer regarding future code refactoring
    const char *fileName = "test.txt";
    FILE *f = fopen(fileName, "r"); //dont forget to verify the return of fopen
    int i = read(f, list);

    for (int j = 0; j < i; j++) //test print
    {
        printf("Name: %s    Address: %s\n", list[j].name, list[j].add);
    }
}
int read(FILE *file, INFO *list)
{
    int i = 0;
    //use scanf as stop condition, [] notation is easier to read
    //limiting the size in scanf specifiers avoids buffer overflow
    while (fscanf(file, "%19s %49[^\n]", list[i].name, list[i].add) == 2)
    {
        i++;
    }
    //return i so that you know how many structs were read
    return i;
}

输出:

Name: Jenny    Address: Woodbridge Ave 
Name: Amber    Address: Exeter street
Name: Michael    Address: Main Street
Name: David    Address: Plainfield ave

【讨论】:

    【解决方案2】:

    所以我需要使用 fgets 来逐行读取而不是 fscanf() 对吗?

    fscanf(file,"%s %s\n ",.... 失败,因为%s 没有按照"Woodbridge Ave" 的需要将空格读入字符串,并且无法防止缓冲区溢出。

    有很多方法可以解决这个任务。 fgets()是最清楚的。

    考虑一个辅助函数来读取一行并处理行输入的fgets() 特性。根据需要进行调整。

    // return 1 on success
    // return EOF on end-of-file/input error
    // else return 0
    int read_line(FILE *file, int sz, char *line) {
      if (fgets(line, sz, file) == NULL) {
        return EOF; // EOF or rare input error
      }
      int len = strlen(line);
      if (len > 0 && line[len - 1] == '\n') {
        line[--len] = '\0'; // lop off potential \n
      } else if (len + 1 == sz) { // no \n read, `line` full, so look for rest of input
        int ch;
        int extra = 0;
        while ((ch = fgetc(file)) != '\n' && ch != EOF) {
          extra = 1;
        }
        if (extra) {
          return 0;  // input too long
        }
      }
      return 1;
    }
    

    现在将文件成行读入INFO

    int read_INFO(FILE *file, int n, INFO *list) {
      int i = 0;
      while (i < n 
          && read_line(file, sizeof list->name, list[i].name) == 1
          && read_line(file, sizeof list->add, list[i].add) == 1) {
        i++;
      }
      return i;
    }
    

    用法

      int n = 20;
      INFO *list = malloc(sizeof *list * n);
      if (list) {
        int number_read = read_INFO(file, n, list);
        if (number_read > 0) {
          // The happy path
        }
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-13
      • 2014-04-02
      • 1970-01-01
      • 2012-05-04
      • 2022-01-25
      相关资源
      最近更新 更多