首页 文章

C逐行读取文件

提问于
浏览
131

我写了这个函数来从文件中读取一行:

const char *readLine(FILE *file) {

    if (file == NULL) {
        printf("Error: file pointer is null.");
        exit(1);
    }

    int maximumLineLength = 128;
    char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error allocating memory for line buffer.");
        exit(1);
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
            maximumLineLength += 128;
            lineBuffer = realloc(lineBuffer, maximumLineLength);
            if (lineBuffer == NULL) {
                printf("Error reallocating space for line buffer.");
                exit(1);
            }
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }

    lineBuffer[count] = '\0';
    char line[count + 1];
    strncpy(line, lineBuffer, (count + 1));
    free(lineBuffer);
    const char *constLine = line;
    return constLine;
}

该函数正确读取文件,并使用printf我看到constLine字符串也正确读取 .

但是,如果我使用这个功能,例如像这样:

while (!feof(myFile)) {
    const char *line = readLine(myFile);
    printf("%s\n", line);
}

printf输出乱码 . 为什么?

15 回答

  • 0

    如果你的任务不是发明逐行读取功能,而只是逐行读取文件,你可以使用涉及 getline() 函数的典型代码片段(参见手册页here):

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        FILE * fp;
        char * line = NULL;
        size_t len = 0;
        ssize_t read;
    
        fp = fopen("/etc/motd", "r");
        if (fp == NULL)
            exit(EXIT_FAILURE);
    
        while ((read = getline(&line, &len, fp)) != -1) {
            printf("Retrieved line of length %zu:\n", read);
            printf("%s", line);
        }
    
        fclose(fp);
        if (line)
            free(line);
        exit(EXIT_SUCCESS);
    }
    
  • 0

    readLine 函数中,返回一个指向 line 数组的指针(严格来说,指向第一个字符的指针,但这里的差异无关紧要) . 由于's an automatic variable (i.e., it' s“在堆栈上”),当函数返回时回收内存 . 你看到了胡言乱语,因为 printf 把自己的东西放在了堆栈上 .

    您需要从函数返回动态分配的缓冲区 . 你已经拥有一个,它是 lineBuffer ;你所要做的就是将它截断到所需的长度 .

    lineBuffer[count] = '\0';
        realloc(lineBuffer, count + 1);
        return lineBuffer;
    }
    

    ADDED (对评论中的后续问题的回复): readLine 返回指向组成该行的字符的指针 . 此指针是您处理行内容所需的 . 当你've finished using the memory taken by these characters. Here'如何使用 readLine 函数时,你也必须传递给 free

    char *line = readLine(file);
    printf("LOG: read a line: %s\n", line);
    if (strchr(line, 'a')) { puts("The line contains an a"); }
    /* etc. */
    free(line);
    /* After this point, the memory allocated for the line has been reclaimed.
       You can't use the value of `line` again (though you can assign a new value
       to the `line` variable if you want). */
    
  • 4
    FILE* fp;
    char buffer[255];
    
    fp = fopen("file.txt", "r");
    
    while(fgets(buffer, 255, (FILE*) fp)) {
        printf("%s\n", buffer);
    }
    
    fclose(fp);
    
  • 2
    //open and get the file handle
    FILE* fh;
    fopen_s(&fh, filename, "r");
    
    //check if file exists
    if (fh == NULL){
        printf("file does not exists %s", filename);
        return 0;
    }
    
    
    //read line by line
    const size_t line_size = 300;
    char* line = malloc(line_size);
    while (fgets(line, line_size, fh) != NULL)  {
        printf(line);
    }
    free(line);    // dont forget to free heap memory
    
  • 236

    readLine() 返回指向局部变量的指针,这会导致未定义的行为 .

    您可以:

    • 在调用函数中创建变量并将其地址传递给 readLine()

    • 使用 malloc()line 分配内存 - 在这种情况下 line 将是持久的

    • 使用全局变量,虽然这通常是一种不好的做法

  • 1

    使用fgets()从文件句柄中读取一行 .

  • 5

    这个例子有些不对劲:

    • 你忘了在你的printfs上添加\ n . 错误消息也应该发送到stderr,即 fprintf(stderr, ....

    • (不是大傻子但是)考虑使用 fgetc() 而不是 getc() . getc() 是一个宏, fgetc() 是一个正常的函数

    • getc() 返回 int ,因此 ch 应声明为 int . 这很重要,因为与 EOF 的比较将得到正确处理 . 一些8位字符集使用 0xFF 作为有效字符(ISO-LATIN-1将是一个示例), EOF (-1),如果分配给 char ,则为 0xFF .

    • 该行可能存在缓冲区溢出

    lineBuffer[count] = '\0';
    

    如果该行长度正好为128个字符,则 count 在执行点处为128 .

    • 正如其他人所指出的那样, line 是一个本地声明的数组 . 您无法返回指向它的指针 .

    • strncpy(count + 1) 将最多复制 count + 1 个字符,但如果它命中 '\0' 则会终止因为你将 lineBuffer[count] 设置为 '\0' ,你知道它永远不会到达 count + 1 . 但是,如果确实如此,它将不会终止 '\0' ,所以你需要这样做 . 您经常会看到如下内容:

    char buffer [BUFFER_SIZE];
    strncpy(buffer, sourceString, BUFFER_SIZE - 1);
    buffer[BUFFER_SIZE - 1] = '\0';
    
    • 如果你要 malloc() 返回一行(代替你的本地 char 数组),你的返回类型应为 char* - 删除 const .
  • 8
    void readLine(FILE* file, char* line, int limit)
    {
        int i;
        int read;
    
        read = fread(line, sizeof(char), limit, file);
        line[read] = '\0';
    
        for(i = 0; i <= read;i++)
        {
            if('\0' == line[i] || '\n' == line[i] || '\r' == line[i])
            {
                line[i] = '\0';
                break;
            }
        }
    
        if(i != read)
        {
            fseek(file, i - read + 1, SEEK_CUR);
        }
    }
    

    这个如何?

  • 0

    这是我几个小时......逐行读取整个文件 .

    char * readline(FILE *fp, char *buffer)
    {
        int ch;
        int i = 0;
        size_t buff_len = 0;
    
        buffer = malloc(buff_len + 1);
        if (!buffer) return NULL;  // Out of memory
    
        while ((ch = fgetc(fp)) != '\n' && ch != EOF)
        {
            buff_len++;
            void *tmp = realloc(buffer, buff_len + 1);
            if (tmp == NULL)
            {
                free(buffer);
                return NULL; // Out of memory
            }
            buffer = tmp;
    
            buffer[i] = (char) ch;
            i++;
        }
        buffer[i] = '\0';
    
        // Detect end
        if (ch == EOF && (i == 0 || ferror(fp)))
        {
            free(buffer);
            return NULL;
        }
        return buffer;
    }
    
    void lineByline(FILE * file){
    char *s;
    while ((s = readline(file, 0)) != NULL)
    {
        puts(s);
        free(s);
        printf("\n");
    }
    }
    
    int main()
    {
        char *fileName = "input-1.txt";
        FILE* file = fopen(fileName, "r");
        lineByline(file);
        return 0;
    }
    
  • 18
    const char *readLine(FILE *file, char* line) {
    
        if (file == NULL) {
            printf("Error: file pointer is null.");
            exit(1);
        }
    
        int maximumLineLength = 128;
        char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);
    
        if (lineBuffer == NULL) {
            printf("Error allocating memory for line buffer.");
            exit(1);
        }
    
        char ch = getc(file);
        int count = 0;
    
        while ((ch != '\n') && (ch != EOF)) {
            if (count == maximumLineLength) {
                maximumLineLength += 128;
                lineBuffer = realloc(lineBuffer, maximumLineLength);
                if (lineBuffer == NULL) {
                    printf("Error reallocating space for line buffer.");
                    exit(1);
                }
            }
            lineBuffer[count] = ch;
            count++;
    
            ch = getc(file);
        }
    
        lineBuffer[count] = '\0';
        char line[count + 1];
        strncpy(line, lineBuffer, (count + 1));
        free(lineBuffer);
        return line;
    
    }
    
    
    char linebuffer[256];
    while (!feof(myFile)) {
        const char *line = readLine(myFile, linebuffer);
        printf("%s\n", line);
    }
    

    请注意'line'变量在调用函数中声明然后传递,因此 readLine 函数填充预定义缓冲区并返回它 . 这是大多数C库的工作方式 .

    还有其他方法,我知道:

    • char line[] 定义为静态( static char line[MAX_LINE_LENGTH] - >它会保持's value AFTER returning from the function). -> bad, the function is not reentrant, and race condition can occur -> if you call it twice from two threads, it will overwrite it'的结果

    • malloc() 使用char行[],并在调用函数中释放它 - >太多昂贵的 malloc ,并且将责任委托给另一个函数释放缓冲区(最优雅的解决方案是在任何缓冲区中调用 mallocfree )功能相同)

    btw,'explicit'从 char*const char* 的铸造是多余的 .

    btw2,没有必要 malloc() lineBuffer,只需定义它 char lineBuffer[128] ,所以你不需要释放它

    btw3不使用'dynamic sized stack arrays'(将数组定义为 char arrayName[some_nonconstant_variable] ),如果你不确切知道你在做什么,它只适用于C99 .

  • 1

    您应该使用ANSI函数来读取一行,例如 . 与fgets . 在调用之后,你需要在调用上下文中使用free(),例如:

    ...
    const char *entirecontent=readLine(myFile);
    puts(entirecontent);
    free(entirecontent);
    ...
    
    const char *readLine(FILE *file)
    {
      char *lineBuffer=calloc(1,1), line[128];
    
      if ( !file || !lineBuffer )
      {
        fprintf(stderr,"an ErrorNo 1: ...");
        exit(1);
      }
    
      for(; fgets(line,sizeof line,file) ; strcat(lineBuffer,line) )
      {
        if( strchr(line,'\n') ) *strchr(line,'\n')=0;
        lineBuffer=realloc(lineBuffer,strlen(lineBuffer)+strlen(line)+1);
        if( !lineBuffer )
        {
          fprintf(stderr,"an ErrorNo 2: ...");
          exit(2);
        }
      }
      return lineBuffer;
    }
    
  • 16

    实现读取和从文件中获取内容的方法(input1.txt)

    #include <stdio.h>
    #include <stdlib.h>
    
    void testGetFile() {
        // open file
        FILE *fp = fopen("input1.txt", "r");
        size_t len = 255;
        // need malloc memory for line, if not, segmentation fault error will occurred.
        char *line = malloc(sizeof(char) * len);
        // check if file exist (and you can open it) or not
        if (fp == NULL) {
            printf("can open file input1.txt!");
            return;
        }
        while(fgets(line, len, fp) != NULL) {
            printf("%s\n", line);
        }
        free(line);
    }
    

    希望这有帮助 . 快乐的编码!

  • 2

    你犯了一个返回指向自动变量的指针的错误 . 变量行在堆栈中分配,只有在函数存在时才存在 . 您不能返回指向它的指针,因为只要它返回,内存就会在其他地方给出 .

    const char* func x(){
        char line[100];
        return (const char*) line; //illegal
    }
    

    为了避免这种情况,你要么返回一个驻留在堆上的内存的指针,例如 . lineBuffer,用户有责任在完成后调用free() . 或者,您可以要求用户将您作为参数传递给您在其上写入行内容的内存地址 .

  • 14

    我想要一个来自地面0的代码所以我这样做是为了逐行读取字典的内容 .

    char temp_str [20]; //您可以根据您的要求更改缓冲区大小和文件中单行的长度 .

    Note 我每次读行时都用Null字符初始化缓冲区 . 这个函数可以自动化但是因为我需要一个概念证明并且想要设计一个Byte By Byte程序

    #include<stdio.h>
    
    int main()
    {
    int i;
    char temp_ch;
    FILE *fp=fopen("data.txt","r");
    while(temp_ch!=EOF)
    {
     i=0;
      char temp_str[20]={'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};
    while(temp_ch!='\n')
    {
      temp_ch=fgetc(fp);
      temp_str[i]=temp_ch;
      i++;
    }
    if(temp_ch=='\n')
    {
    temp_ch=fgetc(fp);
    temp_str[i]=temp_ch;
    }
    printf("%s",temp_str);
    }
    return 0;
    }
    
  • 1

    我从头开始实现:

    FILE *pFile = fopen(your_file_path, "r");
    int nbytes = 1024;
    char *line = (char *) malloc(nbytes);
    char *buf = (char *) malloc(nbytes);
    
    size_t bytes_read;
    int linesize = 0;
    while (fgets(buf, nbytes, pFile) != NULL) {
        bytes_read = strlen(buf);
        // if line length larger than size of line buffer
        if (linesize + bytes_read > nbytes) {
            char *tmp = line;
            nbytes += nbytes / 2;
            line = (char *) malloc(nbytes);
            memcpy(line, tmp, linesize);
            free(tmp);
        }
        memcpy(line + linesize, buf, bytes_read);
        linesize += bytes_read;
    
        if (feof(pFile) || buf[bytes_read-1] == '\n') {
            handle_line(line);
            linesize = 0;
            memset(line, '\0', nbytes);
        }
    }
    
    free(buf);
    free(line);
    

相关问题