Перемещает ли fscanf указатель файла назад?

Это содержимое моего файла unsorted.txt:

3 Роберт Джастин Трамп

Это мой код:

#include <stdio.h>

int main(void) {
    FILE *f = fopen("unsorted.txt", "r");
    char n;
    printf("%d\n", ftell(f));
    fscanf(f, "%s", &n);
    int l = n - '0';
    printf("%d %d\n", l, ftell(f));
    return 0;
}

при выполнении выдает следующий результат:

0
3 -1

почему он вернул -1 во втором случае? Он должен перейти от 0 к 1, верно?

ПРИМЕЧАНИЕ: файл можно открыть, потому что тогда как он напечатает 0 при первом вызове и первый символ из файла, не будучи открытым?


person Novice    schedule 17.09.2017    source источник
comment
Убедитесь, что fopen не дал сбой.   -  person Basile Starynkevitch    schedule 17.09.2017
comment
Вы должны были получить предупреждения компилятора с этим кодом. Если вы ничего не получили, вы, вероятно, захотите выяснить, как включить предупреждения и/или обновить свой компилятор.   -  person n. 1.8e9-where's-my-share m.    schedule 17.09.2017


Ответы (2)


 fscanf(f,"%s",&n);

очень неправильно, так как вы объявили char n; (только один байт). Вы получили неопределенное поведение. Будьте очень испуганы (и в следующий раз стыдитесь).

Я рекомендую:

Проверьте, что fopen не подведет:

FILE *f = fopen("unsorted.txt","r");
if (!f)  { perror("fopen unsorted.txt"); exit(EXIT_FAILURE); };

Объявите буфер разумного размера (80 был размером перфокарты в 1970-х).

char buf[80];

очистите его (вы хотите защитное программирование):

memset(buf, 0, sizeof(buf));

Затем внимательно прочитайте fscanf. Прочтите эту документацию несколько раз. Используйте его с фиксированным размером и проверьте результат:

if (fscanf(f, "%72s", buf) > 0) {

(72 - полезный размер перфокарт в программах PL/1; он меньше 80)

Не забудьте прочитать документацию по другим функциям, включая ftell.

Важный совет:

скомпилировать со всеми предупреждениями и отладочной информацией (gcc -Wall -Wextra -g с помощью GCC), улучшить код, чтобы не было предупреждений, используйте отладчик gdb для пошагового запуска.

ПС. В качестве упражнения найдите возможное содержимое unsorted.txt, благодаря которому ваша исходная программа заработала правильно. Могли бы вы в таком случае предсказать его выход? Если нет, то почему??

person Basile Starynkevitch    schedule 17.09.2017

В вашем коде есть несколько проблем:

  • Вы не проверяете возвращаемое значение fopen(). Вызов ftell() с указателем NULL имеет неопределенное поведение. Вы не можете делать выводы из наблюдаемого поведения.

  • printf("%d\n", ftell(f)); неверно, потому что возвращаемое значение ftell() равно long. Вы должны использовать формат %ld.

  • fscanf(f, "%s", &n); неверно, потому что вы передаете адрес одного char для fscanf(), чтобы сохранить строку с завершающим нулем. fscanf() будет обращаться к памяти, превышающей размер char, который имеет неопределенное поведение. Определите массив char, например char buf[80];, и передайте максимальное количество символов для хранения как: fscanf(f, "%79s", buf); и проверьте возвращаемое значение, или используйте %c для чтения одного байта.

  • int l = n - '0'; не является строго неверным, но чревато ошибками: избегайте называть переменную l, так как она похожа на 1 до степени смешения.

  • printf("%d %d\n", l, ftell(f)); неверен, как и предыдущий вызов printf: используйте спецификатор преобразования %ld для возвращаемого значения ftell().

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

Вот исправленная версия:

#include <stdio.h>

int main(void) {
    FILE *f = fopen("unsorted.txt", "r");
    char c;

    if (f != NULL) {
        printf("%ld\n", ftell(f));
        if (fscanf(f, "%c", &c) == 1) {
            int diff = c - '0';
            printf("%d %ld\n", diff, ftell(f));
        }
    }
    return 0;
}

Выход:

0
3 1
person chqrlie    schedule 17.09.2017