Я практикую C с проектом периодической таблицы. У меня есть этот файл .txt, который содержит все элементы в следующем порядке:
atomic_number name symbol mass_number.
Моя программа должна была заполнить массив структур данными (для удобства поиска). Проблема в том, что моя программа не доходит до последнего элемента (atomic_number = 118), она просто останавливается на 54 и не печатает дальше. Он также не заполняет массив за пределами 54.
Вот как я пытался решить проблему
файл: populate.c
#include "periodic_table.h"
int populate(void)
{
struct element
{
int atomic_num;
char name[15];
char symbol[5];
float mass_num;
};
// Array to hold the elements
struct element e[118];
// checks that the file exists
FILE *ptr = fopen("periodic_table.txt", "r");
if (ptr == NULL)
{
printf("No file to read from.\nExiting...\n");
return 1;
}
int line; // first line of the txt file
for (line = 1; line <= 118; line++)
{
if (fscanf(ptr, "%d %s %s %f", &e[line].atomic_num, &e[line].name, &e[line].symbol, &e[line].mass_num))
{
//print is just for debugging purpose
printf("%d record: %d %s %s %.2f\n", line, e[line].atomic_num, e[line].name, e[line].symbol, e[line].mass_num);
}
fflush(stdin);
}
fclose(ptr);
return (0);
}
Мой текстовый файл выглядит так
1 Hydrogen H 1.01
2 Helium He 4.00
3 Lithium Li 6.94
.
.
.
116 Livermorium Lv 293.00
117 Tennesine Ts 294.00
118 Oganesson Og 294.00
Не используйте & при сканировании массива. Имя массива само по себе уже преобразовано в правильный адрес для использования... scanf("...%s...", ..., e[line].name, ...)
Код в обороне. Использовать ограничения для строк внутри scanf: scanf("...%14s%4s...", ..., e[line].name, e[line].symbol, ...)
Наряду с другим советом я бы предложил сначала прочитать и распечатать каждую строку в виде строки, чтобы убедиться, что вы знаете, что происходит с вашими данными. Если какая-либо строка не соответствует вашим спецификациям fscanf, она будет вести себя непредсказуемо. Например, я заметил, что некоторые атомные массы в Интернете указаны в квадратных скобках, а не в виде десятичных чисел, из-за этого fscanf может подумать, что это строки.
fflush(stdin) — это неопределенное поведение. Сброс предназначен для выходного потока.
Re: сообщения об ошибках. Когда fopen терпит неудачу, это указывает не на сбой при чтении файла, а на сбой при открытии файла. Если пользователь видит сообщение «нет файла для чтения», но видит, что файл существует, это может вызвать ненужную путаницу. Точные сообщения об ошибках важны, и их легко генерировать. И они принадлежат stderr. printf("ERROR ... всегда неправильно (должно быть fprintf(stderr, "ERROR ...). Попробуйте char *name = "periodic_table.txt"; if ( (ptr = fopen(name, "r")) == NULL ){ perror(name); ....
@WilliamPursell Большое спасибо за то, что указали на мои любительские навыки обработки ошибок ... Я очень ценю это @pmg Да, извините за это, у меня было предупреждение на терминале кодовых блоков, но я решил его проигнорировать. Позже я удалил a, когда мне надоело видеть предупреждения «защитный код ...» понял! Большое спасибо за ваше время @mgillett Большое спасибо @Irelia Это было совершенно ненужно, я просто отчаянно пытался заставить его работать LOL @DavidRanieri Спасибо. Я ценю





В вашей программе несколько проблем:
определение для struct element и массив e является локальным для функции populate, поэтому данные будут недоступны после возврата функции.
индексная переменная line должна работать от 0 до 117, поскольку она используется для индексации массива e[118]. доступ к e[118] имеет неопределенное поведение.
вы должны читать по одной строке за раз и использовать sscanf() для анализа строки и сообщать об ошибках преобразования вместе с содержимым строки для упрощения отладки.
тест if (fscanf(...)) неверен: возвращаемое значение должно быть 4, если все 4 преобразования завершились успешно. любой другой результат следует считать ошибкой.
%s является рискованным спецификатором преобразования, поскольку fscanf() может вызвать переполнение буфера, если в содержимом файла слова длиннее ожидаемого.
вы не должны использовать & для переменных назначения, которые являются массивами.
fflush(stdin); бесполезен и на самом деле имеет неопределенное поведение.
Вот модифицированная версия:
#include "periodic_table.h"
// these definitions should be moved to "periodic_table.h"
struct element {
int atomic_num;
char name[15];
char symbol[5];
float mass_num;
};
// Array to hold the elements
struct element e[118];
int populate(void) {
// checks that the file exists
FILE *ptr = fopen("periodic_table.txt", "r");
if (ptr == NULL) {
fprintf(stderr, "No file to read from.\nExiting...\n");
return 1;
}
int line; // first line of the txt file
for (line = 0; line < 118; line++) {
char buffer[128];
int pos;
if (!fgets(buffer, sizeof buffer, ptr)) {
fprintf(stderr, "cannot read element %d\n", line + 1);
break;
}
if (sscanf(buffer, "%d%14s%4s%f%n", &e[line].atomic_num, e[line].name,
e[line].symbol, &e[line].mass_num, &pos) == 4) {
//print is just for debugging purpose
printf("%d record: %d %s %s %.2f\n",
line + 1, e[line].atomic_num, e[line].name,
e[line].symbol, e[line].mass_num);
if (buffer[pos] != '\n') {
fprintf(stderr, "line %d: extra data: %s\n", line + 1, buffer + pos);
}
} else {
fprintf(stderr, "line %d: invalid data: %s\n", line + 1, buffer);
}
}
fclose(ptr);
return 0;
}
Большое спасибо. Это исправило это. Благодаря вашему c else { fprintf(stderr, "line %d: invalid data: %s\n", line + 1, buffer); } я обнаружил, что допустил ошибку в строке 55. У меня была 55 Caesium 55 Cs 132.91, из-за которой мой код ломался. Я многому научился из этого решения. Огромное спасибо
1) Массивы 0 основаны на C:
for(line = 1; line <= 118; line++)->for(line = 0; line < 118; line++)2) Вы хотитеif (fscanf(...) == 4)3)fflush(stdin);неправильно