C Lees Bestandslijn per regel

Ik heb deze functie geschreven om een ​​regel uit een bestand te lezen:

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

De functie leest het bestand correct, en met behulp van printf zie ik dat de Constline-string ook correct is gelezen.

Echter, als ik de functie b.v. zoals dit:

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

Printf geeft gibbers uit. Waarom?


Antwoord 1, Autoriteit 100%

Als uw taak de line-by-line-leesfunctie niet uitvinden, maar om de bestandslijn-by-line te lezen, kunt u een typisch codefragment gebruiken met de getline()Functie (zie de handmatige pagina hier ):

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

Antwoord 2, Autoriteit 15%

FILE* filePointer;
int bufferLength = 255;
char buffer[bufferLength];
filePointer = fopen("file.txt", "r");
while(fgets(buffer, bufferLength, filePointer)) {
    printf("%s\n", buffer);
}
fclose(filePointer);

Antwoord 3, Autoriteit 6%

In uw readLineFunctie retourneert u een aanwijzer naar de lineARRAY (strikt genomen, een aanwijzer op zijn eerste teken, maar het verschil is hier irrelevant). Omdat het een automatische variabele is (d.w.z. het is “op de stapel”), wordt het geheugen teruggewonnen wanneer de functie terugkeert. U ziet Gibbers omdat printfzijn eigen spullen op de stapel heeft geplaatst.

U moet een dynamisch toegewezen buffer van de functie retourneren. Je hebt er al een, het is lineBuffer; Het enige dat u hoeft te doen, is het in de gewenste lengte afknippen.

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

toegevoegd (reactie op follow-up vraag in opmerking): readLineRetourneert een aanwijzer naar de personages die de lijn vormen. Deze aanwijzer is wat u moet werken met de inhoud van de lijn. Het is ook wat u moet doorgeven naar freewanneer u klaar bent met het gebruik van het geheugen dat door deze tekens is gemaakt. Hier is hoe u de readLine-functie kunt gebruiken:

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

Antwoord 4, Autoriteit 5%

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

Antwoord 5, Autoriteit 3%

readLine()Retourneert aanwijzer naar lokale variabele, die ongedefinieerd gedrag veroorzaakt.

Om je heen te laten kunnen:

  1. Maak variabele in bellerfunctie en geef het adres door op readLine()
  2. Wijs het geheugen toe voor linemet malloc()– In dit geval wordt linePARLESENTE
  3. Gebruik globale variabele, hoewel het over het algemeen een slechte praktijk is

Antwoord 6, autoriteit 3%

Gebruik fgets()om een regel te lezen van een bestandshandle.


Antwoord 7, autoriteit 3%

Een complete, fgets()oplossing:

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

Uitvoer:

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

Vergeet niet dat als je wilt lezen uit Standard Input (in plaats van een bestand zoals in dit geval), je alleen maar stdinhoeft door te geven als de derde parameter van fgets()methode, zoals deze:

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

Bijlage

Trailing newline-teken verwijderen uit fgets()-invoer

hoe te detecteren dat een bestand is geopend of niet in c


Antwoord 8

Er zijn een paar dingen mis met het voorbeeld:

  • je bent vergeten \n toe te voegen aan je printfs. Ook moeten foutmeldingen naar stderr gaan, d.w.z. fprintf(stderr, ....
  • (geen biggy maar) overweeg om fgetc()te gebruiken in plaats van getc(). getc()is een macro, fgetc()is een juiste functie
  • getc()retourneert een int, dus chmoet worden gedeclareerd als een int. Dit is belangrijk omdat de vergelijking met EOFcorrect wordt afgehandeld. Sommige 8-bits tekensets gebruiken 0xFFals een geldig teken (ISO-LATIN-1 zou een voorbeeld zijn) en EOFdat -1 is, zal 0xFFindien toegewezen aan een char.
  • Er is een mogelijke bufferoverloop aan de lijn

    lineBuffer[count] = '\0';
    

    Als de regel precies 128 tekens lang is, is count128 op het punt dat wordt uitgevoerd.

  • Zoals anderen al hebben opgemerkt, is lineeen lokaal gedeclareerde array. Je kunt er geen verwijzing naar terugsturen.

  • strncpy(count + 1)kopieert maximaal count + 1tekens maar wordt beëindigd als het '\0'Omdat je lineBuffer[count]instelt op '\0', weet je dat het nooit op count + 1zal komen. Als dit echter het geval was, zou er geen afsluitende '\0'op staan, dus u moet het doen. Je ziet vaak zoiets als het volgende:

    char buffer [BUFFER_SIZE];
    strncpy(buffer, sourceString, BUFFER_SIZE - 1);
    buffer[BUFFER_SIZE - 1] = '\0';
    
  • als u malloc()een regel retourneert (in plaats van uw lokale chararray), moet uw retourtype char*– laat de constvallen.


Antwoord 9

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

merk op dat de variabele ‘line’ wordt gedeclareerd in de aanroepfunctie en vervolgens wordt doorgegeven, dus uw functie readLinevult de vooraf gedefinieerde buffer en retourneert deze gewoon. Dit is de manier waarop de meeste C-bibliotheken werken.

Er zijn andere manieren die ik ken:

  • de char line[]definiëren als statisch
    (static char line[MAX_LINE_LENGTH]
    -> het zal zijn waarde behouden NA het terugkeren van de functie). -> slecht,
    De functie is niet reimrant, en
    Race-toestand kan voorkomen – & GT; als jij
    noem het twee keer van twee threads, het
    zal het zijn resultaten overschrijven
  • malloc()ING DE CHAR LINE [], EN
    het bevrijden van het bellen van functies – & GT;
    Te veel duur mallocS en,
    De verantwoordelijkheid delegeren om de buffer te bevrijden naar een andere functie (de meest elegante oplossing is om te bellen mallocEN freeOP ENIGE BUFFERS IN DEZELFDE FUNCTIE)

BTW, ‘Expliciet’ gieten van char*naar const char*is redundant.

BTW2, het is niet nodig malloc()De linebuffer, definieer het gewoon char lineBuffer[128], dus u hoeft het niet te bevrijden

BTW3 GEBRUIK NIET ‘DYNAMISCHE SIDE STACK ARRAYS’ (Bepalen van de array als char arrayName[some_nonconstant_variable]), als u niet bepaald weet wat u aan het doen bent, werkt het alleen in C99.


Antwoord 10

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

Hoe zit het met deze?


Antwoord 11

Implementatiemethode om te lezen en ontvang de inhoud uit een bestand (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);
}

Hoop dat dit helpt. Veel plezier met coderen!


Antwoord 12

Hier zijn mijn paar uur… Het hele bestand regel voor regel lezen.

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

Antwoord 13

U moet de ANSI-functies gebruiken om een regel te lezen, bijv. fgets. Na het bellen heb je free() nodig in de belcontext, bijvoorbeeld:

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

Antwoord 14

Mijn werktuig helemaal opnieuw:

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

Antwoord 15

Bied een draagbare en generieke getdelim-functie, test geslaagd via 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;
}

Antwoord 16

Je maakt de fout om een pointer terug te sturen naar een automatische variabele.
De variabele lijn wordt toegewezen in de stapel en leeft slechts zo lang als de functie leeft.
Je mag er geen pointer naar terugsturen, want zodra hij terugkomt wordt het geheugen ergens anders gegeven.

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

Om dit te voorkomen, stuurt u ofwel een aanwijzer naar het geheugen die zich op de heap bevindt, bijv. lijnBuffer
en het zou de verantwoordelijkheid van de gebruiker moeten zijn om free() aan te roepen als hij er klaar mee is.
Als alternatief kunt u de gebruiker vragen om u als argument een geheugenadres door te geven waarop de regelinhoud kan worden geschreven.


Antwoord 17

Ik wil een code van grond 0, dus ik deed dit om de inhoud van het woord van het woordenboek regel voor regel te lezen.

char temp_str[20]; // u kunt de buffergrootte volgens uw vereisten wijzigen en de lengte van een enkele regel in een bestand.

OpmerkingIk heb de buffer met Null-tekens elke keer dat ik een regel lees geïnitialiseerd. Deze functie kan worden geautomatiseerd, maar omdat ik een proof of Concept nodig heb en een programma Byte By Byte wil ontwerpen

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

Other episodes