Przechodzenie przez plik tekstowy linia po linii w C

Pracowałem nad małym ćwiczeniem dla mojej klasy CIS i jestem bardzo zdezorientowany metodami C używanymi do odczytu z pliku. Wszystko, co naprawdę muszę zrobić, to odczytać plik linia po linii i wykorzystać informacje zebrane z każdej linii, aby wykonać kilka manipulacji. Próbowałem użyć metody getline i innych bez powodzenia. Mój kod jest obecnie następujący:

int main(char *argc, char* argv[]){
      const char *filename = argv[0];
      FILE *file = fopen(filename, "r");
      char *line = NULL;

      while(!feof(file)){
        sscanf(line, filename, "%s");
        printf("%s\n", line);
      }
    return 1;
}

W tej chwili dostaję błąd seg z metodą sscanf i nie jestem pewien dlaczego. Jestem totalnym C noobem i po prostu zastanawiam się, czy jest coś, co mi umyka. Dzięki

Author: Dan Bradbury, 2012-02-09

4 answers

Tak wiele problemów w tak niewielu linijkach. Pewnie o niektórych zapomniałem:

  • argv [0] jest nazwą programu, a nie pierwszym argumentem;
  • Jeśli chcesz odczytać zmienną, musisz przydzielić jej pamięć
  • nigdy nie pętla na feof, jedna pętla na funkcji IO, dopóki nie zawiedzie, feof służy do określenia przyczyny niepowodzenia,
  • sscanf służy do parsowania linii, jeśli chcesz parsować plik, użyj fscanf,
  • "%s" zatrzyma się na pierwszej spacji jako format ?scanf family
  • Aby odczytać linię, standardową funkcją jest fgets,
  • zwrócenie 1 z głównego oznacza awarię

Więc

#include <stdio.h>

int main(int argc, char* argv[])
{
    char const* const fileName = argv[1]; /* should check that argc > 1 */
    FILE* file = fopen(fileName, "r"); /* should check the result */
    char line[256];

    while (fgets(line, sizeof(line), file)) {
        /* note that fgets don't strip the terminating \n, checking its
           presence would allow to handle lines longer that sizeof(line) */
        printf("%s", line); 
    }
    /* may check feof here to make a difference between eof and io failure -- network
       timeout for instance */

    fclose(file);

    return 0;
}
 108
Author: AProgrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-10 14:21:42

Aby odczytać linię z pliku, należy użyć funkcji fgets: odczytuje łańcuch znaków z podanego pliku do znaku nowej linii lub EOF.

Użycie sscanf w Twoim kodzie w ogóle nie zadziała, ponieważ używasz {[3] } jako ciągu formatującego do odczytu z line do stałego ciągu literalnego %s.

Powodem SEGV jest to, że zapisujesz do nie przydzielonej pamięci wskazywanej przez line.

 7
Author: Abrixas2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-09 06:11:55

Załóżmy, że masz do czynienia z innym ogranicznikiem, np. tabulatorem \t, zamiast znaku nowego wiersza \n.

Bardziej ogólnym podejściem do ograniczników jest użycie getc(), który chwyta jedną postać na raz.

Zauważ, że getc() zwraca int, abyśmy mogli sprawdzić równość z EOF.

Po Drugie, definiujemy tablicę line[BUFFER_MAX_LENGTH] Typu char, aby przechowywać do BUFFER_MAX_LENGTH-1 znaków na stosie (musimy zapisać ten ostatni znak dla \0 terminatora charakter).

Użycie tablicy pozwala uniknąć konieczności używania malloc i free do tworzenia wskaźnika znakowego o odpowiedniej długości na stercie.

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])
{
    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else {
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    }

    if (!file) {
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    }

    /* get a character from the file pointer */
    while(tempChar = fgetc(file))
    {
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) {
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        }

        /* test character value */
        if (tempChar == EOF) {
            line[tempCharIdx] = '\0';
            fprintf(stdout, "%s\n", line);
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            fprintf(stdout, "%s\n", line);
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}

Jeśli musisz użyć char *, możesz nadal używać tego kodu, ale strdup() tablica line[], gdy zostanie wypełniona wartością wejściową linii. Musisz free ten zduplikowany ciąg, gdy skończysz z nim, albo dostaniesz wyciek pamięci:

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])
{
    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;
    char *dynamicLine = NULL;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else {
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    }

    if (!file) {
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    }

    while(tempChar = fgetc(file))
    {
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) {
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        }

        /* test character value */
        if (tempChar == EOF) {
            line[tempCharIdx] = '\0';
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}
 4
Author: Alex Reynolds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 22:00:00

Oprócz innych odpowiedzi, w najnowszej bibliotece C (zgodnej z Posix 2008), możesz użyć getline . Zobacz tę odpowiedź (na powiązane pytanie).

 3
Author: Basile Starynkevitch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:24