Проблема с fscanf из-за файловой текстовой разделительной запятой C - PullRequest
0 голосов
/ 25 апреля 2020

Я должен использовать этот файл информации
Сигареты, Для курения, 1,5500000
Древесина, Для внутренних дымоходов, 2 100,000000

Для заполнения этой структуры:

typedef struct product{
    char name[32];
    char about_product[32];
    int product_id;
    double price;
}sProduct;

Использование этой функции:

void print_prod(){
    sProduct ptr;
    FILE *fp=fopen("product.txt", "r");
    int cnt=1;
    do{
        fscanf(fp, "%s,%s,%d,%g\n", ptr.name, ptr.about_product, &ptr.product_id, &ptr.price);
        cnt++;
    }while(!feof(fp));  
    fclose(fp);
}

Но это не вызывает ошибок, потому что я использую запятую для делителя в текстовом файле.

1 Ответ

2 голосов
/ 25 апреля 2020

Здесь есть две проблемы, обе вращаются вокруг того, как scanf обрабатывает %s директивы:

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

Существует несколько чтобы вы могли go о задаче ввода, которую вы установили, но чтобы продолжать использовать scanf для сканирования вашего конкретного ввода непосредственно в вашу структуру данных, вам нужна директива %[ вместо %s.

Директива %[ принимает «набор сканирования», описывающий, какие именно символы могут появляться в поле, которые могут включать пробелы. Это принимает форму, похожую на класс символов регулярного выражения или глобуса. Соответствующий аргумент должен быть указателем на char, как и для директивы %s. Вы также должны знать, что в отличие от большинства директив, включая %s, директива %[ не пропускает начальные пробелы. Для вас его использование может выглядеть следующим образом:

        fscanf(fp, "%[^,],%[^,],%d,%g\n", ptr.name, ptr.about_product, &ptr.product_id, &ptr.price);

Два дескриптора поля %[^,], каждый из которых сканирует любое количество символов, кроме запятой (,).

Дополнительно , было бы разумно указать ширину полей, чтобы избежать превышения границ ваших массивов в случае появления в ваших данных слишком длинных полей. Поскольку вы предоставляете 32-байтовые массивы, и один байт каждого должен быть зарезервирован для ограничителя строки, это может быть следующим:

        fscanf(fp, "%31[^,],%31[^,],%d,%g\n", ptr.name, ptr.about_product, &ptr.product_id, &ptr.price);

Но также, как и практически со всеми функциями, которые имеют любые режимы сбоя, вы Также следует проверить программно, была ли функция выполнена успешно. Вы должны делать это заранее и последовательно, иначе ваши программы могут потерпеть неудачу тонкими и неожиданными способами. Для этого использования scanf и многих подобных, это означает проверку, что возвращаемое значение равно количеству входных директив в формате (возвращаемое значение указывает, сколько полей было успешно отсканировано и назначено):

        int fields;
        fields = fscanf(fp, "%31[^,],%31[^,],%d,%g\n", ptr.name, ptr.about_product,
                &ptr.product_id, &ptr.price);
        if (fields != 4) {
            // handle input error ...
        }

Приложение:

Продолжая с того, что вы должны проверять ошибки в функциях, которые имеют режимы ошибок, я также замечаю, что другой такой функцией является fopen() , Эта функция может полностью потерпеть неудачу, и в этом случае она возвращает нулевой указатель. Надежная программа определенно проверит этот случай и изящно обработает его, используя шаблон, аналогичный описанному мной для scanf(). Это, однако, не связано с конкретным c неправильным поведением, о котором вы спрашивали. (Кредит @RobertSsupportsMonicaCellio.)

...