C - неожиданные случайные символы читаются из конца файла - PullRequest
0 голосов
/ 12 января 2020

Я пытаюсь прочитать список слов, разделенных запятыми, из файла csv, и у меня возникают проблемы при работе со случайными символами, которые появляются в конце файла при чтении с помощью C. Кажется, что символы в конце файла полностью меняются, когда я добавляю / удаляю слова из списка.

Вот что содержится в файле: johnny,david,alan,rodney,bob,ronald,andrew,hola,goodbye. Это точно скопировано, нет случайного пробела или возврата каретки в конце.

Вот что читает программа:

Это код, который читается в тексте:

    char* name;
    FILE *fp;
    char *fcontent;
    int wordCount = 0;
    char delim = ',';
    long fsize;
    bool end = false;
    char guessedLetters[26];
    int guessNum = 0;
    int lives = 0;

    for (int i = 0; i < 26; i++) {
        guessedLetters[i] = '\0';
    }

    fp = fopen(WORDS_FILENAME, "r");

    if (fp == NULL) {
        printf("Words File Exception: Exiting.");
        return 1;
    }

    fseek(fp, 0L, SEEK_END);
    fsize = ftell(fp);
    fseek(fp, 0L, SEEK_SET);

    fcontent = (char*)calloc(fsize, sizeof(char));

    if (fcontent == NULL) {
        printf("No words in file: Exiting.");
        return 1;
    }

    fread(fcontent, sizeof(char), fsize, fp);
    char *fcontent2 = malloc(strlen(fcontent + 1));
    strcpy(fcontent2, fcontent);
    fclose(fp);

Слова разбиваются на массив слов, а неконтролируемые символы добавляются в конце последних слов, вызывая довольно много проблем в дальнейшем в программе.

Этот код разбивает строку на массив wordArr:

char wordArr[wordCount][15];

    char *ptr2 = strtok(fcontent2, &delim);
    int count = 0;

    while (ptr2 != NULL) {
        strcpy(wordArr[count], ptr2);
        count++;
        ptr2 = strtok(NULL, &delim);
    }

Возможно, если невозможно полностью пропустить символы из будучи прочитанным, они могут быть опущены в процессе разделения?

Спасибо, Джек.

Ответы [ 2 ]

3 голосов
/ 12 января 2020

Сначала вы открываете файл в режиме text :

fp = fopen(WORDS_FILENAME, "r");

В соответствии со C стандартом 7.21.9.4 Функция ftell , абзац 2 :

Функция ftell получает текущее значение индикатора положения файла для потока, на который указывает поток. Для двоичного потока значением является количество символов в начале файла. Для текстового потока его указатель положения файла содержит неопределенную информацию, используемую функцией fseek для возврата указателя положения файла для потока в его положение во время вызова ftell; разница между двумя такими возвращаемыми значениями не обязательно является значимым показателем количества написанных или прочитанных символов.

Вы не можете использовать ftell() в текстовом потоке, чтобы сказать, сколько байты могут быть прочитаны.

Таким образом, вам придется открыть файл в режиме двоичный , чтобы использовать ftell() (но см. примечание ниже):

fp = fopen(WORDS_FILENAME, "rb");

Теперь у вас есть размер файла:

fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0L, SEEK_SET);

fcontent = (char*)calloc(fsize, sizeof(char));

Но , который не оставляет места для любого '\0' терминатора, так что должно быть

// no need to cast a void * in C, and sizeof(char)
// is **always** one by definition
fcontent = calloc(fsize + 1 , 1);

Теперь вы будет иметь завершенную строку для содержимого файла.

Примечание о fseek() в двоичном потоке

Использование fseek() для достижения конца двоичного потока буквально неопределенное поведение в соответствии со стандартом C.

За 7.21.9.2 Функция fseek , пункт 3 :

Для двоичный поток, новая позиция, измеренная в символах от начала файла, получается путем добавления смещения к позиции, указанной в се. Указанная позиция является началом файла, если значение откуда SEEK_SET, текущим значением индикатора позиции файла, если SEEK_CUR, или концом файла, если SEEK_END. Бинарный поток не обязательно должен поддерживать вызовы fseek со значением откуда SEEK_END.

Сноска 268 четные состояния:

Установка индикатора положения файла в конец файла, как с fseek (file, 0, SEEK_END ), имеет неопределенное поведение для двоичного потока (из-за возможных завершающих нулевых символов) или для любого потока с кодированием, зависящим от состояния, которое не обязательно заканчивается в начальном состоянии сдвига.

Единственная причина, по которой вы можете использовать fseek(fp, 0L, SEEK_END);, заключается в том, что большинство операционных систем расширяют язык C и фактически определяют его для работы.

2 голосов
/ 12 января 2020

В прочитанных данных отсутствует нулевой символ завершения.

Вам необходимо проверить количество прочитанных символов, а затем "вручную" установить нулевой символ завершения:

int cnt = fread(fcontent, sizeof(char), fsize, fp);
fcontent[cnt] = '\0';

Of Конечно, перед использованием в качестве индекса массива рекомендуется проверить, что cnt не является отрицательным (ошибка чтения).

...