簡體   English   中英

C 逐行讀取文件

[英]C read file line by line

我寫了這個 function 從文件中讀取一行:

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

function 正確讀取文件,使用 printf 我看到 constLine 字符串也被正確讀取。

但是,如果我使用 function 例如這樣:

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

printf 輸出亂碼。 為什么?

如果您的任務不是發明逐行讀取函數,而只是逐行讀取文件,您可以使用包含getline()函數的典型代碼片段(請參閱此處的手冊頁):

#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);
}
FILE* filePointer;
int bufferLength = 255;
char buffer[bufferLength];

filePointer = fopen("file.txt", "r");

while(fgets(buffer, bufferLength, filePointer)) {
    printf("%s\n", buffer);
}

fclose(filePointer);

在你的readLine函數中,你返回一個指向line數組的指針(嚴格來說,一個指向它的第一個字符的指針,但這里的區別無關緊要)。 由於它是一個自動變量(即,它“在堆棧上”),當函數返回時內存會被回收。 你會看到胡言亂語,因為printf已經把它自己的東西放在了堆棧上。

您需要從函數返回一個動態分配的緩沖區。 你已經有了一個,它是lineBuffer 您所要做的就是將其截斷為所需的長度。

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

添加(對評論中的后續問題的回應): readLine返回一個指向組成該行的字符的指針。 這個指針是你處理行內容所需要的。 當您使用完這些字符占用的內存時,這也是您必須傳遞給free 下面是如何使用readLine函數:

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). */
//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

readLine()返回指向局部變量的指針,這會導致未定義的行為。

要四處走動,您可以:

  1. 在調用函數中創建變量並將其地址傳遞給readLine()
  2. 使用malloc()line分配內存 - 在這種情況下, line是持久的
  3. 使用全局變量,盡管這通常是一種不好的做法

使用fgets()從文件句柄中讀取一行。

一個完整的fgets()解決方案:

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

#define MAX_LEN 256

int main(void)
{
    FILE* fp;
    fp = fopen("file.txt", "r");
    if (fp == NULL) {
      perror("Failed: ");
      return 1;
    }

    char buffer[MAX_LEN];
    // -1 to allow room for NULL terminator for really long string
    while (fgets(buffer, MAX_LEN - 1, fp))
    {
        // Remove trailing newline
        buffer[strcspn(buffer, "\n")] = 0;
        printf("%s\n", buffer);
    }

    fclose(fp);
    return 0;
}

輸出:

First line of file
Second line of file
Third (and also last) line of file

請記住,如果您想從標准輸入(而不是本例中的文件)讀取,那么您要做的就是將stdin作為fgets()方法的第三個參數傳遞,如下所示:

while(fgets(buffer, MAX_LEN - 1, stdin))

附錄

從 fgets() 輸入中刪除尾隨換行符

c - 如何檢測文件是否在c中打開

這個例子有一些錯誤:

  • 您忘記將 \\n 添加到您的 printfs 中。 錯誤消息也應該轉到 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

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

這個如何?

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] -> 它將在從函數返回后保持它的值)。 -> 不好,該函數不可重入,並且可能發生競爭條件 -> 如果你從兩個線程調用它兩次,它會覆蓋它的結果
  • malloc()調用 char line[],並在調用函數時釋放它 -> 太多昂貴的malloc s,並且將釋放緩沖區的責任委托給另一個函數(最優雅的解決方案是在任何緩沖區上調用mallocfree在相同的功能)

順便說一句,從char*const char* “顯式”轉換是多余的。

btw2,不需要malloc() lineBuffer,只需定義它char lineBuffer[128] ,所以你不需要釋放它

btw3 不使用“動態大小堆棧數組”(將數組定義為char arrayName[some_nonconstant_variable] ),如果您不完全知道自己在做什么,則它僅適用於 C99。

實現方法從文件中讀取和獲取內容(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);
}

希望這有幫助。 快樂編碼!

這是我的幾個小時......逐行讀取整個文件。

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

您應該使用 ANSI 函數來讀取一行,例如。 獲取。 調用后,您需要在調用上下文中使用 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;
}

我的工具從頭開始:

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

提供可移植的通用getdelim函數,測試通過 msvc、clang、gcc。

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

ssize_t
portabl_getdelim(char ** restrict linep,
                 size_t * restrict linecapp,
                 int delimiter,
                 FILE * restrict stream) {
    if (0 == *linep) {
        *linecapp = 8;
        *linep = malloc(*linecapp);
        if (0 == *linep) {
            return EOF;
        }
    }

    ssize_t linelen = 0;
    int c = 0;
    char *p = *linep;

    while (EOF != (c = fgetc(stream))) {
        if (linelen == (ssize_t) *linecapp - 1) {
            *linecapp <<= 1;
            char *p1 = realloc(*linep, *linecapp);
            if (0 == *p1) {
                return EOF;
            }
            p = p1 + linelen;
        }
        *p++ = c;
        linelen++;

        if (delimiter == c) {
            *p = 0;
            return linelen;
        }
    }
    return EOF == c ? EOF : linelen;
}


int
main(int argc, char **argv) {
    const char *filename = "/a/b/c.c";
    FILE *file = fopen(filename, "r");
    if (!file) {
        perror(filename);
        return 1;
    }

    char *line = 0;
    size_t linecap = 0;
    ssize_t linelen;

    while (0 < (linelen = portabl_getdelim(&line, &linecap, '\n', file))) {
        fwrite(line, linelen, 1, stdout);
    }
    if (line) {
        free(line);
    }
    fclose(file);   

    return 0;
}

您犯了返回指向自動變量的指針的錯誤。 變量 line 在堆棧中分配,並且只在函數存在時才存在。 不允許返回指向它的指針,因為一旦它返回,內存就會分配給其他地方。

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

為了避免這種情況,您要么返回一個指向駐留在堆上的內存的指針,例如。 lineBuffer 並且在完成之后調用 free() 應該是用戶的責任。 或者,您可以要求用戶將要寫入行內容的內存地址作為參數傳遞給您。

我想要一個來自地面 0 的代碼,所以我這樣做是為了逐行閱讀字典單詞的內容。

字符 temp_str[20]; //您可以根據您的要求更改緩沖區大小以及文件中單行的長度。

注意我每次讀取行時都用空字符初始化緩沖區。這個函數可以是自動的但是因為我需要一個概念證明並且想要一個字節一個字節地設計一個程序

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM