Чтение данных из файла

#c #file-io

#c #файловый ввод-вывод

Вопрос:

В настоящее время я получаю ошибку проверки во время выполнения # 2 — поврежден стек вокруг переменной city. Считываемый входной файл отформатирован следующим образом:

Бетти, Мейн-стрит, 12, Северный Элмо, Северная Каролина 29801, 2000.20
Джо, Мейпл-бул., 16, Стамптаун, Джорджия, 33125, 4000.40
Фрэнк, 100 Авент Ферри, Роли, Северная Каролина 27606, -3000,30

Как я могу исправить эту ошибку?

 int main(int argc, char *argv[]) {
    int c, i, zip;
    FILE *fp;
    char name[20], address[50], city[19], state[3];
    float balance;

    for (i = 1; i < argc; i  ) {
        fp = fopen(argv[i], "r");
        if (fp == NULL) {
            fprintf(stderr, "cat: can't open %sn", argv[i]);
            continue;
        }

        while ((c = getc(fp)) != EOF) {
            fscanf(fp, "%s%s%s%s%d%f", amp;name, amp;address, amp;city, amp;state, amp;zip, amp;balance);
        }

        printf("%s%s%s%s%d%fn", name, address, city, state, zip, balance);
        fclose(fp);
    }

    return 0;
}
  

Обновить:

Спасибо за всю помощь на данный момент. Что я сделал сейчас, так это то, что я создал a, struct для каждого person элемента структуры, содержащего имя, адрес и баланс. Я просто изменил предыдущие инструкции, чтобы передавать переменные в a struct person p , а затем создал insert() метод, который вставляет p в a struct person list .

 while (fgets(line, sizeof(line), fp) != NULL) {
        p = malloc(sizeof(struct person));
        sscanf(line, "[^,], I[^,], [^,], %2[^ ] %d, %lfn", p->name, p->address, p->city, p->state, amp;p->zip, amp;p->balance);
        printf("got [%s], [%s], [%s], [%s], [%d] and [%9.2f].n", p->name, p->address, p->city, p->state, p->zip, p->balance);
        insert(p);
    }
  

Я пытаюсь вставить person в список в алфавитном порядке и попытался сделать это с помощью strcmp. После запуска моей программы с помощью этого метода она зависает и останавливает выполнение при первой вставке. Ищу помощь в том, где я ошибся в своем insert() методе.

 void insert(struct person *p) {
struct person *cur, *prev;
int result = 0;

for (cur = list, prev = NULL, result = strcmp(p->name, cur->name); cur != NULL amp;amp; result > 0; prev = cur, cur = cur->next, result = strcmp(p->name, cur->name));

if (cur != NULL amp;amp; result == 0) {
    printf("-------------------------n"
        "DUPLICATE RECORD: %sn"
        "-------------------------n", p->name);
    free(p);
    return;
}

p->next = cur;
if (prev == NULL) {
    list = p;
}
else {
    prev->next = p;
}
}
  

Я тестировал этот метод без strcmp() . Я только что сравнил с p->name > cur->name , и он смог вставить и распечатать список просто отлично с помощью моего print() метода. Единственной проблемой было то, что люди не были отсортированы по алфавиту, поэтому я вернулся и попытался реализовать strcmp() .

Комментарии:

1. Я не знаю о стеке, но ваша fscanf спецификация преобразования (после исправлений, предложенных неперссоном), примененная к предоставляемым вами входным данным, приводит к тому, что вам name присваивается "Betty," ; address получает "12" ; city получает "Main" ; state получает "Street," ; zip и balance получают неопределенные значения, а возвращаемое значение равно 4. Рассмотрите возможность синтаксического анализа входных данных с помощью чего-либо другого, чем scanf() .

2. Не по теме: Почтовые индексы — это не целые числа, это строки, которые просто случайно содержат целые числа (и иногда дефис). Обработка почтового индекса как целого числа вызовет проблемы, если вы потеряете начальные нули или забудете, что начальный ноль обычно указывает на восьмеричное число.

3. @mu слишком короткое, спасибо, но для этого упражнения нам сказали рассматривать его как int

Ответ №1:

Это:

 fscanf(fp, "%s%s%s%s%d%f", amp;name, amp;address, amp;city, amp;state, amp;zip, amp;balance);
  

должно быть:

 fscanf(fp, "%s%s%s%s%d%f", name, address, city, state, amp;zip, amp;balance);
  

Комментарии:

1. 1 … или даже лучше if (fscanf(fp, "%s%s%s%s%d%f", name, address, city, state, amp;zip, amp;balance) != 6) /* error */;

2. Хм, это все еще не устранило ошибку проверки во время выполнения # 2 — был поврежден стек вокруг переменной city. Текстовый файл, который я читаю, отформатирован следующим образом: Бетти, 12 Main Street, North Elmo, NC 29801, 2000.20 Джо, 16 Maple Blvd., Стамптаун, Джорджия, 33125, 4000.40 Фрэнк, 100 Avent Ferry, Роли, NC 27606, -30000.30

3. Спасибо за быстрый ответ. Что я должен использовать для чтения тогда?

4. @raphnguyen Честно говоря, чтобы сделать это правильно (например, обработать встроенные в данные запятые), вам нужно написать анализатор, что не совсем тривиально.

5. @unapersson, похоже, выходит за рамки начального класса программирования на Си. Нет другого способа сделать это с помощью fscanf () или других команд?

Ответ №2:

Я предполагаю, что ваша проблема со стеком возникает из-за попытки поместить в state больше символов, чем он может вместить.

Вы определили state как способный содержать 3 символа: 2 обычных символа и 1 нулевой ограничитель. Ваша fscanf инструкция, представленная с помощью примера ввода, пытается записать 8 символов (7 обычных и 1 нулевой ограничитель) в этом пространстве для 3 символов.

Это вызывает то, что называется неопределенным поведением. Один из способов проявления неопределенного поведения — это повреждение переменных.


Редактировать

Чтобы ограничить scanf фиксированное количество символов, используйте максимальную ширину поля в спецификации преобразования:

 char buf[42];
scanf("As", buf); /* reads at most 41 non whitespace characters to `buf`
                    ** and appends a null terminator,
                    ** for a maximum total of 42 characters */
  

Комментарии:

1. Спасибо за объяснение ошибки стека. Я немного запутался в том, каким способом это сделать сейчас, поскольку текстовый файл — CSV, и, похоже, простого метода не существует. Не могли бы вы предложить fgets () и также токенизацию?

2. Я предлагаю написать свой собственный анализатор (это не так сложно!). Если у вас корректный ввод без встроенных запятых или необязательных кавычек (в вашем примере их нет, как это было GA, 33125 во 2-й строке), вы можете попробовать использовать "%[" спецификатор преобразования в scanf : if (scanf("[^,], I[^,], [^,], %2[^ ]%d,%f", ...) != 6) /* error */;

3. @pmg Спасибо, но я не совсем уверен, как работают анализаторы. Это неправильный синтаксис? fscanf(fp, «%s[^,]%s[^,]%s[^,]%s[^]%d%f», имя, адрес, город, штат, почтовый индекс и баланс); Я не уверен, что понимаю ваше утверждение scanf if != 6.

4. Нет, ваш синтаксис неверен. Используйте только одно из %s или %[ . %s считывает символы, не содержащие пробелов, %[ считывает группу символов, указанную между [ и соответствующим ] . Ваш формат ( %s[^,] ) считывает символы, не содержащие пробелов, за которыми следуют константы '[' , '^' , ',' и ']' . Просто замените ... соответствующими переменными и надейтесь, что if это сработает 🙂

5. @pmg Спасибо! Я пересмотрел свой код следующим образом и добавил инструкцию print, чтобы протестировать вывод имени: while ((c = getc(fp)) != EOF) { fscanf(fp, «[^,], I[^,], [^,], %2[^ ], % d, %f», имя, адрес, город, штат, amp;zip, amp; баланс); printf(«%s n», имя); } Результаты таковы: etty 29801 mptown 0.40 Frank 27606 Нажмите любую клавишу, чтобы продолжить . . . Как вы собираетесь вводить возврат в этих комментариях? Мои комментарии читать намного сложнее, чем ваши.