Проблема с sscanf чтением нескольких строк из входного файла - PullRequest
1 голос
/ 09 марта 2020

Так что я медленно продолжаю учиться C. И теперь у меня есть задача - прочитать данные из файла и отсортировать их.

Данные файла:

House naming 1 30 300
House naming 2 45 450
.......
House naming 10 5 120

Итак, первое значение: House naming , может быть любым именем, например Empire state building

Второе значение: адрес дома (я выбрал только integer значения)

Третье значение: возраст здания

Четвертое значение: киловатт-час / год

Программа должна взять данные из файла -> Распечатать -> Сортировать (как? См. Ниже) -> Распечатать снова, отсортировано.

Сортировка:

  • кВт / ч <200 - устойчивое здание, </li>
  • кВт / ч <300 && возраст <40 - нуждается в ремонте, </li>
  • кВт / ч> 300 && возраст> 40 - установлен для сноса.

Вот код:

#include <stdio.h>
#include <stdlib.h>
#include "input.h"

int main(void) {
    int kwh;
    int age;
    char building[SIZE];
    int addr;
    char buff[SIZE];
    FILE *fi;

    // opening the files and checking if it succeeded
    fi = fopen(F_INPUT, "r");
    if (fi == NULL) {
        printf("Error opening input file \"%s\"", F_INPUT);
        exit(EXIT_INPUT_FAIL);
    }
    while (fgets(buff, sizeof(buff), fi) != NULL) {
        sscanf(buff, "%s %d %d %d", building, &addr,&age,&kwh);
        if (kwh < 200) {
            puts(buff);
            printf("Sustainable\n");
        } else
        if (kwh < 300 && age < 40) {
            puts(buff);
            printf("Needs renovation\n");
        } else
        if (kwh > 300 && age > 40) {
            puts(buff);
            printf("IN DEMOLITION LIST\n");
        }
    }
    /* close the files when they're not needed anymore */
    fclose(fi);
    return 0;
}

Я сделал несколько шагов, чтобы сделать его немного проще, читает данные -> выходные данные уже отмечены 1 ) Устойчивое, 2) Требуется ремонт, 3) Установить на снос.

Проблема где-то в while l oop, и я думаю, что это в функции sscanf. В моей логи c, если я не ошибаюсь, она должна прочитать строку из файла, используя логи c (посмотрите sscanf и входной файл): char value, integer, integer, integer. Программа читает файл, выводит данные, но помечает все здания как sustainable.

Что вы предлагаете прочитать более внимательно или какую логику c лучше выбрать для чтения нескольких строк.

Выход:

House naming 1 30 300
Sustainable
House naming 2 45 450
Sustainable
........
House naming 10 5 120
Sustainable

Ответы [ 2 ]

2 голосов
/ 10 марта 2020

Чтение строки из файла в строку через fgets() - хороший первый шаг, как сделал OP.

Can "House Наименование "включать цифры? как "Stdio 54"?
Да, он может включать цифры и не может. Если я прав, задание ничего не говорит о именах.

Следующая часть хитрая, поскольку не существует уникального разделителя между именем дома и следующими 3 целыми числами.

Один из подходов состоит в том, чтобы найти 3 конечных целых числа, а затем оставить оставшийся начальный текст в виде названия дома .

  while (fgets(buff, sizeof(buff), fi) != NULL) {
    int address, age, power;
    char *p = buff + strlen(buff);  // start as buff end
    p = reverse_scan_int(buff, p, &power);
    p = reverse_scan_int(buff, p, &age);
    p = reverse_scan_int(buff, p, &address);
    if (p) {
      *p = '\0';
      trim(buff);  // remove leading trailing white-space
      printf("house_name:'%s', address:%d age:%d power:%d\n", buff, address,
          age, power);
    } else {
      printf("Failed to parse '%s'\n", buff);
    }
  }

Теперь все, что нам нужно, это reverse_scan_int(). Пример идеи непроверенного кода:

#include <ctype.h>
#include <stdbool.h>
char *reverse_scan_int(const char *begin, char *one_past, int *i) {
  if (one_past == NULL) {
    return NULL;
  }
  // is...() functions work best with unsigned char
  unsigned char *p = (unsigned char *) one_past;
  // Skip trailing whitespace;
  while (p > begin && isspace(p[-1])) {
    p--;
  }
  // Skip digits;
  bool digit_found = false;
  while (p > begin && isdigit(p[-1])) {
    p--;
    digit_found = true;
  }
  if (!digit_found)
    return NULL;
  if (p > begin && (p[-1] == '-' || p[-1] == '+')) {
    p--;
  }
  *i = atoi(p); // More roubust code would use `strtol()`, leave for OP.
  return (char *) p;
}

Существует множество способов обрезать строку , включая эту .

2 голосов
/ 09 марта 2020

Вашу проблему сложно решить с помощью sscanf(), потому что нет явного разделителя между названием дома и полями 3 цифр c. %s неуместно: он разбирает одно слово. В вашей программе sscanf() фактически не может преобразовать числа и возвращает 1 для всех строк, что приводит к неопределенному поведению при сравнении числовых значений c, которые фактически неинициализированы.

Вот модифицированная версия, использующая %[ спецификация преобразования:

#include <stdio.h>
#include <stdlib.h>

#define F_INPUT  "input.txt"
#define EXIT_INPUT_FAIL  1

int main(void) {
    char buff[256];
    char building[100];
    int addr, age, kwh;
    FILE *fi;

    // opening the files and checking if it succeeded
    fi = fopen(F_INPUT, "r");
    if (fi == NULL) {
        printf("Error opening input file \"%s\"", F_INPUT);
        exit(EXIT_INPUT_FAIL);
    }
    while (fgets(buff, sizeof(buff), fi) != NULL) {
        /* parse the building name upto and excluding any digit,
           then accept 3 integral numbers for the address, age and power */
        if (sscanf(buff, "%99[^0-9]%d%d%d", building, &addr, &age, &kwh) != 4) {
            printf("parsing error: %s", buff);
            continue;
        }
        if (kwh < 200) {
            puts(buff);
            printf("Sustainable\n");
        } else
        if (kwh < 300 && age < 40) {
            puts(buff);
            printf("Needs renovation\n");
        } else
        if (kwh > 300 && age > 40) {
            puts(buff);
            printf("IN DEMOLITION LIST\n");
        }
        // allocate structure with building details and append it to the list or array of buildings
    }
    /* close the files when they're not needed anymore */
    fclose(fi);
    // sort the list or array and print it
    // free the list or array
    return 0;
}
...