Проходя текстовый файл построчно в C - PullRequest
53 голосов
/ 09 февраля 2012

Я работал над небольшим упражнением для своего класса CIS и очень смущен методами, которые C использует для чтения из файла. Все, что мне действительно нужно сделать, это прочитать файл построчно и использовать информацию, собранную из каждой строки, чтобы сделать несколько манипуляций. Я попытался использовать метод getline и другие безуспешно. Мой код в настоящее время выглядит следующим образом:

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

Прямо сейчас я получаю ошибку сегмента при использовании метода sscanf, и я не уверен, почему. Я полный C noob и просто интересуюсь, была ли какая-то большая картина, которую мне не хватало. Спасибо

Ответы [ 4 ]

119 голосов
/ 09 февраля 2012

Так много проблем в стольких строках. Я, наверное, забываю некоторые:

  • argv [0] - это имя программы, а не первый аргумент;
  • если вы хотите прочитать переменную, вы должны выделить ее память
  • никто никогда не зацикливается на feof, каждый зацикливается на функции ввода-вывода до тех пор, пока она не выйдет из строя, feof затем служит для определения причины сбоя,
  • sscanf существует для анализа строки, если вы хотите проанализировать файл, используйте fscanf,
  • "% s" остановится на первом пробеле в качестве формата для семейства? Scanf
  • для чтения строки, стандартная функция fgets,
  • возврат 1 из основного сбоя средства

So

#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;
}
7 голосов
/ 09 февраля 2012

Чтобы прочитать строку из файла, вы должны использовать функцию fgets: она считывает строку из указанного файла до символа новой строки или EOF.

Использование sscanf в вашем коде не будет работать вообще, так как вы используете filename в качестве строки формата для чтения из line в константу строкового литерала %s.

Причиной SEGV является то, что вы записываете в нераспределенную память, указанную line.

5 голосов
/ 09 февраля 2012

Допустим, вы имеете дело с каким-то другим разделителем, таким как \t, вместо \n новой строки.

Более общий подход к разделителям - использование getc(), который захватывает один символ за раз.

Обратите внимание, что getc() возвращает int, так что мы можем проверить равенство с EOF.

Во-вторых, мы определяем массив line[BUFFER_MAX_LENGTH] типа char,для хранения до BUFFER_MAX_LENGTH-1 символов в стеке (мы должны сохранить этот последний символ для \0 символа-терминатора).

Использование массива избавляет от необходимости использовать malloc и free, чтобы создать символьный указатель правильной длины в куче.

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

Если вы должны использовать char *, то вы все равно можете использовать этот код, но вы strdup() the line[]массив, когда он заполнен значением строки ввода.Вы должны free эту дублированную строку, как только вы закончите с ней, или вы получите утечку памяти:

#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 голосов
/ 09 февраля 2012

В дополнение к другим ответам в последней библиотеке C (совместимой с Posix 2008) вы можете использовать getline . См. этот ответ (на связанный вопрос).

...