C อ่านไฟล์ทีละบรรทัด


184

ฉันเขียนฟังก์ชันนี้เพื่ออ่านบรรทัดจากไฟล์:

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 เอาท์พุทซึ่งพูดพล่อยๆ ทำไม?


ใช้แทนfgets fgetcคุณกำลังอ่านตัวอักษรโดยตัวละครแทนบรรทัดโดยบรรทัด
ชีฟ

3
โปรดทราบว่าgetline()นี่เป็นส่วนหนึ่งของ POSIX 2008 อาจมีแพลตฟอร์มคล้าย POSIX โดยไม่ต้องใช้โดยเฉพาะอย่างยิ่งหากไม่รองรับ POSIX 2008 ที่เหลือ แต่ภายในโลกของระบบ POSIX getline()จะสามารถพกพาไปได้ทุกวันนี้
Jonathan Leffler

คำตอบ:


305

หากงานของคุณไม่ได้คิดค้นฟังก์ชั่นการอ่านทีละบรรทัด แต่เพียงเพื่ออ่านไฟล์ทีละบรรทัดคุณอาจใช้ข้อมูลโค้ดทั่วไปที่เกี่ยวข้องกับ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);
}

83
นั่นไม่ใช่พกพา
JeremyP

16
แม่นยำยิ่งขึ้นนี่getlineเป็นเฉพาะ GNU libc เช่นกับ Linux อย่างไรก็ตามถ้าเจตนาคือการมีฟังก์ชั่นการอ่านบรรทัด (ตรงข้ามกับการเรียนรู้ C) มีฟังก์ชั่นการอ่านบรรทัดโดเมนสาธารณะจำนวนมากที่มีอยู่บนเว็บ
Gilles 'หยุดความชั่วร้าย'

11
ทำไมฉันต้องทำอย่างนั้น? อ่านคู่มือการใช้งานบัฟเฟอร์จะถูกจัดสรรใหม่ในแต่ละการโทรจากนั้นมันควรจะเป็นอิสระในตอนท้าย
mbaitoff

29
การif(line)ตรวจสอบนั้นไม่จำเป็น การโทรfree(NULL)เป็นสิ่งที่ไม่มีความจำเป็น
aroth

50
สำหรับผู้ที่กล่าวว่า getline นี้ใช้เฉพาะกับ GNU libc "ทั้ง getline () และ getdelim () เป็นส่วนขยายของ GNU เดิม แต่เดิมเป็นส่วนขยายมาตรฐานใน POSIX.1-2008"
willkill07

37
FILE* filePointer;
int bufferLength = 255;
char buffer[bufferLength];

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

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

fclose(filePointer);

สำหรับฉันผลลัพธ์นี้จะเขียนทับแต่ละบรรทัดด้วยบรรทัดถัดไป ดูคำถามนี้ตามคำตอบข้างต้น
Cezar Cobuz

5
ทำไมต้องเลือกนักแสดง(FILE*) fp? ยังไม่fpได้เป็นFILE *และยังfopen()ส่งคืนFILE *หรือไม่
นักบัญชี

1
หากคุณตกลงกับบรรทัดที่จำกัดความยาวไว้นี่เป็นคำตอบที่ดีที่สุด มิฉะนั้นการใช้getlineเป็นทางเลือกที่ดี ฉันเห็นด้วยกับการFILE *โยนนั้นไม่จำเป็น
Theicfire

ฉันลบ cast ที่ไม่จำเป็นเพิ่มตัวแปรสำหรับความยาวบัฟเฟอร์และเปลี่ยนfpเป็นfilePointerความชัดเจนมากขึ้น
Rob

21

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

@Iron: ฉันได้เพิ่มบางสิ่งในคำตอบของฉัน แต่ฉันไม่แน่ใจว่าความยากของคุณคืออะไร
Gilles 'หยุดความชั่วร้าย'

@Iron: คำตอบคือคุณไม่ปล่อยให้เป็นอิสระ เอกสารของคุณ (ในเอกสารประกอบ API) ข้อเท็จจริงที่ว่าบัฟเฟอร์ที่ส่งคืนเป็น malloc'd และ ansd จำเป็นต้องได้รับการปลดปล่อยจากผู้เรียก จากนั้นคนที่ใช้ฟังก์ชัน readLine ของคุณจะ (หวังว่า!) เขียนโค้ดคล้ายกับตัวอย่างที่ Gilles ได้เพิ่มไว้ในคำตอบของเขา
JeremyP

15
//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

1
มีปัญหาบางอย่างกับรหัสนี้: fopen_sทำให้รหัสไม่สามารถพกพาได้ printfจะมองหา specifiers รูปแบบและไม่ได้พิมพ์เครื่องหมายเปอร์เซ็นต์และต่อไปนี้ตัวอักษร (s) ที่พวกเขาเป็น ไบต์ Null จะทำให้อักขระทั้งหมดในบรรทัดที่เหลือหายไป (อย่าบอกฉันว่า null null ไม่สามารถเกิดขึ้นได้!)
hagello

และโดยวิธีการที่คุณไม่ได้แก้ปัญหา OP อธิบายว่าค่าตอบแทนของฟังก์ชันของเขาหายไป ฉันไม่เห็นคุณแก้ไขปัญหานี้
hagello

@Hartley ฉันรู้ว่านี่เป็นความคิดเห็นที่เก่ากว่า แต่ฉันกำลังเพิ่มสิ่งนี้เพื่อให้บางคนไม่ได้อ่านความคิดเห็นของเขาและพยายามฟรี (บรรทัด) ในลูป หน่วยความจำสำหรับบรรทัดจะถูกจัดสรรเพียงครั้งเดียวก่อนที่ลูปจะเริ่มต้นดังนั้นจึงควรมีอิสระเพียงครั้งเดียวหลังจากที่ลูปสิ้นสุด หากคุณลองเพิ่มบรรทัดภายในวงคุณจะได้ผลลัพธ์ที่ไม่คาดคิด ขึ้นอยู่กับว่าตัวชี้นั้นว่าง () อย่างไร หากเป็นเพียงการจัดสรรคืนหน่วยความจำและปล่อยให้ตัวชี้ชี้ไปที่ตำแหน่งเดิมรหัสอาจทำงานได้ ถ้ามันกำหนดค่าอื่นให้กับตัวชี้คุณจะเขียนทับส่วนต่าง ๆ ของหน่วยความจำ
alaniane

2
printf (สาย) ผิด! อย่าทำอย่างนี้. สิ่งนี้จะเปิดรหัสของคุณไปยังช่องโหว่รูปแบบสตริงซึ่งคุณสามารถอ่าน / เขียนโดยตรงไปยังหน่วยความจำได้อย่างอิสระผ่านสิ่งที่กำลังพิมพ์ ถ้าฉันจะใส่% n /% p ในไฟล์และชี้ตัวชี้กลับไปยังที่อยู่ในหน่วยความจำ (ในสตริงจากไฟล์) ที่ฉันควบคุมฉันสามารถรันโค้ดนั้นได้
oxagast

10

readLine() ส่งคืนตัวชี้ไปยังตัวแปรโลคัลซึ่งทำให้เกิดลักษณะการทำงานที่ไม่ได้กำหนด

หากต้องการรับรอบคุณสามารถ:

  1. สร้างตัวแปรในฟังก์ชั่นผู้โทรและส่งผ่านที่อยู่ของมัน readLine()
  2. จัดสรรหน่วยความจำสำหรับlineใช้malloc()- ในกรณีนี้lineจะคงอยู่
  3. ใช้ตัวแปรโกลบอลแม้ว่าโดยทั่วไปจะเป็นแนวปฏิบัติที่ไม่ดี


4

มีบางอย่างผิดปกติกับตัวอย่าง:

  • คุณลืมที่จะเพิ่ม \ n ไปยัง printfs ของคุณ นอกจากนี้ข้อความผิดพลาดควรไปที่ stderr เช่นfprintf(stderr, ....
  • (ไม่ Biggy แต่) พิจารณาใช้มากกว่า fgetc() เป็นแมโครเป็นฟังก์ชั่นที่เหมาะสมgetc()getc()fgetc()
  • getc()ส่งกลับintดังนั้นควรจะประกาศเป็นch intสิ่งนี้มีความสำคัญเนื่องจากการเปรียบเทียบกับEOFจะได้รับการจัดการอย่างถูกต้อง บางชุดตัวอักษร 8 บิตใช้0xFFเป็นตัวละครที่ถูกต้อง (ISO-Latin-1 จะเป็นตัวอย่าง) และEOFซึ่งเป็น -1 จะได้รับหากได้รับมอบหมายให้0xFFchar
  • มีบัฟเฟอร์ล้นที่อาจเกิดขึ้นที่บรรทัด

    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วาง


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

แล้วอันนี้หล่ะ?


2

นี่เป็นเวลาหลายชั่วโมงของฉัน ... อ่านไฟล์ทั้งหมดทีละบรรทัด

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

1
ทำไมคุณกำลังใช้fgetcแทนfgets?
Theicfire

1
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] -> มันจะถือเป็นมูลค่าหลังจากกลับมาจากฟังก์ชั่น) -> ไม่ดีฟังก์ชั่นไม่ได้เป็น reentrant และสภาพการแข่งขันสามารถเกิดขึ้นได้ -> ถ้าคุณเรียกมันสองครั้งจากสองเธรดมันจะเขียนทับผลลัพธ์ของมัน
  • malloc()ไอเอ็นจี char char [] และปลดปล่อยมันในฟังก์ชั่นการโทร -> mallocs แพงเกินไปและมอบหมายความรับผิดชอบในการเพิ่มบัฟเฟอร์ให้กับฟังก์ชั่นอื่น (ทางออกที่ดีที่สุดคือการโทรmallocและfreeบัฟเฟอร์ใด ๆ ในฟังก์ชั่นเดียวกัน)

BTW หล่อ 'อย่างชัดเจนจากchar*การconst char*ซ้ำซ้อน

btw2 ไม่จำเป็นต้องmalloc()มี lineBuffer เพียงแค่กำหนดมันchar lineBuffer[128]ดังนั้นคุณไม่จำเป็นต้องเพิ่มมัน

btw3 ไม่ได้ใช้ 'ไดนามิกขนาดกองอาร์เรย์' (กำหนดอาร์เรย์เป็นchar arrayName[some_nonconstant_variable]) ถ้าคุณไม่ทราบว่าสิ่งที่คุณกำลังทำมันทำงานได้เฉพาะใน C99


1
โปรดทราบว่ามีการประกาศตัวแปร 'line' ในฟังก์ชั่นการโทรและผ่านแล้ว - คุณอาจจะต้องลบการประกาศ local line ของฟังก์ชั่นแล้ว นอกจากนี้คุณต้องแจ้งให้ฟังก์ชั่นทราบว่าบัฟเฟอร์นั้นผ่านไปนานแค่ไหนและคิดว่ากลยุทธ์สำหรับการจัดการบรรทัดที่ยาวเกินไปสำหรับบัฟเฟอร์ที่คุณผ่านเข้ามา
JeremyP

1

คุณควรใช้ฟังก์ชัน ANSI เพื่ออ่านบรรทัดเช่น fgets หลังจากโทรคุณจะต้องว่าง () ในบริบทการโทรเช่น:

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

1

ใช้วิธีการอ่านและรับเนื้อหาจากไฟล์ (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);
}

หวังว่าความช่วยเหลือนี้ การเข้ารหัสที่มีความสุข!


0

คุณทำผิดพลาดในการคืนค่าตัวชี้ไปยังตัวแปรอัตโนมัติ บรรทัดตัวแปรถูกจัดสรรในสแต็กและใช้งานได้ตราบเท่าที่ฟังก์ชันยังมีชีวิตอยู่ คุณไม่ได้รับอนุญาตให้ส่งคืนตัวชี้เนื่องจากมันจะส่งคืนหน่วยความจำทันที

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

เพื่อหลีกเลี่ยงปัญหานี้คุณอาจส่งคืนตัวชี้ไปยังหน่วยความจำที่อยู่บนฮีปเช่น lineBuffer และควรเป็นความรับผิดชอบของผู้ใช้ในการโทรฟรี () เมื่อเขาทำเสร็จแล้ว หรือคุณสามารถขอให้ผู้ใช้ส่งคุณเป็นอาร์กิวเมนต์ที่อยู่หน่วยความจำที่จะเขียนเนื้อหารายการที่


มีความแตกต่างระหว่างพฤติกรรมที่ผิดกฎหมายและไม่ได้กำหนด ^^
Phong

0

ฉันต้องการรหัสจากพื้นดิน 0 ดังนั้นฉันจึงทำสิ่งนี้เพื่ออ่านเนื้อหาของคำศัพท์ต่อบรรทัด

ถ่าน temp_str [20]; // คุณสามารถเปลี่ยนขนาดบัฟเฟอร์ตามความต้องการของคุณและความยาวบรรทัดเดียวในไฟล์

หมายเหตุฉันเริ่มต้นบัฟเฟอร์ด้วยอักขระ 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;
}

โปรแกรมของคุณจะทำงานหากวงเล็บอยู่ในตำแหน่งที่ถูกต้อง;) เช่นint main() {
dylnmc

อนึ่งคุณไม่จำเป็นต้องระบุทั้งหมด 20 '\ 0' คุณสามารถเขียนได้: codechar temp_str [20] = {'\ 0'}; code c จะเติมแต่ละช่องโดยอัตโนมัติด้วยตัวปิดเทอร์มินัล null เนื่องจากวิธีการประกาศอาร์เรย์ทำงานว่าถ้าอาร์เรย์ถูกเริ่มต้นด้วยองค์ประกอบที่น้อยกว่าที่อาร์เรย์มีองค์ประกอบสุดท้ายจะถูกกรอกองค์ประกอบที่เหลือ
alaniane

ฉันเชื่อว่าchar temp_str[20] = {0}ยังเติมเต็มชุดอักขระทั้งหมดด้วยตัวยกเลิก null
พฤหัสบดี Yein Tun

0

การใช้งานของฉันตั้งแต่เริ่มต้น:

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

เหตุใดคุณจึงใช้ฮีป (malloc) แทนสแต็ก ดูเหมือนว่าจะมีวิธีการแก้ปัญหาที่ง่ายกว่าfgetsซึ่งสามารถใช้งานได้
Theicfire

0

ให้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;
}

ทำไมทำเช่นนี้เมื่อfgetsมีอยู่?
Theicfire

วิดเจ็ตสามารถกำหนดค่าตัวคั่นบรรทัดหรือกำหนดสิ่งที่ต้องทำเกี่ยวกับบรรทัดปัจจุบันได้เอง
南山竹

getdelimอนุญาตสำหรับตัวคั่นที่กำหนดเอง นอกจากนี้ผมไม่แจ้งให้ทราบล่วงหน้าไม่ได้มีข้อ จำกัด ความยาวสาย - getlineในกรณีนี้คุณสามารถใช้สแต็คที่มี (ทั้งคู่อธิบายไว้ที่นี่: man7.org/linux/man-pages/man3/getline.3.html )
theicfire

คุณพูดถึงลินุกซ์คำถามคือเกี่ยวกับวิธีอ่านบรรทัดใน C ใช่ไหม
南山竹

สิ่งนี้ใช้ได้กับการใช้งาน c มาตรฐานใด ๆ ( getdelimและgetlineได้มาตรฐานใน POSIX.1-2008 มีคนกล่าวถึงในหน้านี้) fgetsเป็นมาตรฐาน c และไม่เจาะจง linux
theicfire
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.