Код:
Молодой человек! Сколько еще раз мы с вами будем "пулять по Луне"?
Вы же в условии не сказали, что результат взаимодействия последнего
вещества пробирки и очередного опытного вещества СНОВА должен
проверяться по формуле взаимодействия!
Эта ситуация учтена в тексте программы, обрамленная комментариями
//((((( и //)))))
Кроме того добавлен контроль на пустоту введенного имени файла
(см. функцию IsEmpty)
И если программирование будет хотя бы отчасти вашей будущей профессией,
запомните: в программировании четко поставленная задача есть 70 процентов
решения задачи...
#include <stdio.h>
#include <malloc.h>
#include <string.h>
struct formula
{
char *n1; // Первое вещество из пары взаимодействующих веществ
char *n2; // Второе вещество из пары взаимодействующих веществ
char *n3; // Результат их взаимодействия
};
#define MAX_FORMULA 200 // Максимальное число формул
#define MAX_PRIOR 100 // Максимальное число опытных веществ
#define MAX_LEN 512
// Список формул
struct formula prior_formula[MAX_FORMULA];
// Число формул
long cnt_formula;
// Число опытных веществ
long cnt_prior;
// Исходные вещества для опыта
char *prior[MAX_PRIOR];
// Вещества в результате опыта
char *result[MAX_PRIOR];
void stack_func
(
struct formula *para, // Список формул взаимодействий веществ
long len_para, // Размер списка формул
char **src, // Список веществ для опыта
long len_src, // Размер списка веществ для опыта
char **out, // Адрес результирующего списка веществ
// после окончания опыта
long *len_out // Размер списка веществ в результате опыта
)
{
long i, j, d1, d2;
char *n1, *n2;
// Поместить в массив результата первое вещество
out[0] = src[0];
d1 = d2 = 0;
// Просмотр всех остальных веществ
for(i = 1; i < len_src; ++i)
{
n1 = out[d1]; // Последнее вещество из пробирки
n2 = src[++d2]; // Очередное вещество для опыта
// Поиск формулы взаимодействия этих веществ
for(j = 0; j < len_para; ++j)
{
if(strcmp(n1, para[j].n1) == 0 && strcmp(n2, para[j].n2) == 0 ||
strcmp(n2, para[j].n1) == 0 && strcmp(n1, para[j].n2) == 0)
break;
}
// Если формулу взаимодействия нашли, заменить
// последнее вещество из пробирки на результат взаимодействия
if(j != len_para)
{
out[d1] = para[j].n3;
//(((((
// Кроме того, результат взаимодействия может спровоцировать новое
// взаимодействие, поэтому повторяем поиск формулы взаимодействия
// для последних двух веществ
while(d1)
{
n1 = out[d1 - 1]; // Предпоследнее вещество в пробирке
n2 = out[d1]; // Последнее вещество в пробирке
for(j = 0; j < len_para; ++j)
{
if(strcmp(n1, para[j].n1) == 0 && strcmp(n2, para[j].n2) == 0 ||
strcmp(n2, para[j].n1) == 0 && strcmp(n1, para[j].n2) == 0)
break;
}
if(j == len_para)
break;
out[--d1] = para[j].n3;
}
//)))))
continue;
}
// Формулу не нашли, последним веществом в пробирке будет
// очередное выбранное вещество
out[++d1] = n2;
}
*len_out = d1 + 1;
}
// Проверить на пустоту введенные имена файлов
int IsEmpty(char *s)
{
char c, *s1, tmp[MAX_LEN];
for(s1 = s; (c = *s) == ' ' || c == '\n' || c == '\t'; ++s)
;
if(c == 0)
{
printf("Empty Name File\n");
return(1);
}
// Переписать имя файла без начальных пробелов
// Это можно было сделать проще (через memmove),
// но я не хочу еще одной дискуссии по этому поводу
strcpy(tmp, s);
strcpy(s1, tmp);
return(0);
}
int main(int argc, char **argv)
{
long len, i, len1, len2, len3;
FILE *f;
char feld[MAX_LEN + 1];
char c, *s, *s1, *s2, *s3;
char name_formula[MAX_LEN], name_prior[MAX_LEN], name_out[MAX_LEN];
// Программа стартуется без параметров,
// а имена файлов вводятся с консоли
//
// 1) имя файла с формулами взаимодействия
// 2) имя файла с опытными веществами
// 3) имя файла результата
// Ввод данных о формулах взаимодействия веществ из файла,
// структура которого предполагается следующей:
//
// Каждая строка файла представляет собой одну формулу взаимодействия
// и состоит из трех слов, разделенных пробелами, например:
//
// свинец соляная_кислота супер_вещество
//
// Обратите внимание на то, что в названии веществ не должно быть
// пробела, при необходимост последнего следует использовать символ
// подчеркивания
//
// В приведенном выше примере предполагается, что если в пробирке
// последнее вещество есть свинец, то при добавлении в пробирку
// соляная_кислота с этим свинцом превращается в супер_вещество
//
// Конец файла означает конец формул взаимодействия
// Запрос имен трех файлов с консоли
// Не предполагается никаких проверок (переполнение и так далее)
printf("Formula File? ");
gets(name_formula);
if(IsEmpty(name_formula))
return(0);
printf("Prior File? ");
gets(name_prior);
if(IsEmpty(name_prior))
return(0);
printf("Result File? ");
gets(name_out);
if(IsEmpty(name_out))
return(0);
// А есть ли файл с формулами?
if((f = fopen(name_formula, "r")) == NULL)
{
printf("Not File (%s) Found\n", name_formula);
return(0);
}
// Номер строки файла для выдачи ошибок
i = 0;
// Счетчик формул
cnt_formula = 0;
// Счетчик опытных веществ
cnt_prior = 0;
// Читаем символьный файл по записям до его конца
while(fgets(feld, MAX_LEN, f))
{
++i;
// Сбрасываем пустые символы перед первым веществом
for(s = feld; *s == ' ' || *s == '\t' || *s == '\n'; ++s)
;
// Считаем, что если формула начинается с символа ';'
// то это комментарийная строка
// Комментарием будет также полностью пустая строка
if(*s == ';' || *s == 0)
continue;
// Не слишком ли много формул задано?
if(cnt_formula == MAX_FORMULA)
{
printf("Error Big Formula\n");
goto error1;
}
// Отмечаем начало первого слова во введенной записи
s1 = s;
// Ищем конец первого слова
while((c = *++s) != ' ' && c != '\t' && c != '\n' && c)
;
// Записываем в конец первого слова символ конца строки
*s = 0;
// А есть ли еще слова после первого слова?
if(c == 0)
{
error:
printf("Error Formula Line %d\n", i);
error1:
fclose(f);
error2:
// Освобождаем память
for(i = 0; i < cnt_formula; ++i)
free(prior_formula[i].n1);
return(0);
}
// Сбрасываем пустые символы перед вторым веществом
while((c = *++s) == ' ' || c == '\t' || c == '\n')
;
// А есть ли второе вещество?
if(c == 0)
goto error;
// Отмечаем начало второго слова во введенной записи
s2 = s;
// Ищем конец второго слова
while((c = *++s) != ' ' && c != '\t' && c != '\n' && c)
;
// Записываем в конец второго слова символ конца строки
*s = 0;
// А есть ли еще слова после второго слова?
if(c == 0)
goto error;
// Сбрасываем пустые символы перед третьим веществом
while((c = *++s) == ' ' || c == '\t' || c == '\n')
;
// Отмечаем начало третьего слова во введенной записи
s3 = s;
// Ищем конец третьего слова
while((c = *++s) != ' ' && c != '\t' && c != '\n' && c)
;
// Записываем в конец третьего слова символ конца строки
*s = 0;
// После третьего слова до конца записи могут идти только
// пустые символы
if(c)
{
while((c = *++s) == ' ' || c == '\t' || c == '\n')
;
if(c)
goto error;
}
// Теперь можно выделить память под три слова и запомнить их там
// Считаем, что память есть всегда, поэтому не применяем контроль
len1 = strlen(s1) + 1;
len2 = strlen(s2) + 1;
len3 = strlen(s3) + 1;
s = (char *)malloc(len1 + len2 + len3);
prior_formula[cnt_formula].n1 = s;
prior_formula[cnt_formula].n2 = s + len1;
prior_formula[cnt_formula].n3 = s + len1 + len2;
strcpy(prior_formula[cnt_formula].n1, s1);
strcpy(prior_formula[cnt_formula].n2, s2);
strcpy(prior_formula[cnt_formula].n3, s3);
// Увеличиваем счетчик формул
++cnt_formula;
}
// А есть ли хотя бы одна формула?
if(cnt_formula == 0)
{
printf("Error No Formula\n");
goto error1;
}
// С формулами покончено, закрываем их файл
fclose(f);
// Ввод данных о веществах, участвующих в опыте
// Считается, что они тоже заданы словами, разделенными пробелами,
// но для упрощения программы будем их вводить при помощи fscanf,
// а не заниматься разбором, как для формул
// А есть ли файл с опытными веществами?
if((f = fopen(name_prior, "r")) == NULL)
{
printf("Not File (%s) Found\n", name_prior);
goto error2;
}
while(fscanf(f, "%s", feld) == 1)
{
if(cnt_prior == MAX_PRIOR)
{
printf("Error Big Prior\n");
// Освобождаем память
for(i = 0; i < cnt_prior; ++i)
free(prior[i]);
goto error1;
}
prior[cnt_prior] = (char *)malloc(strlen(feld) + 1);
strcpy(prior[cnt_prior], feld);
++cnt_prior;
}
// А есть ли опытные вещества?
if(cnt_prior == 0)
{
printf("Error No Prior\n");
goto error1;
}
// А не слишком ли мало опытных веществ?
if(cnt_prior < 2)
{
printf("Error 2 Prior\n");
goto error1;
}
// С опытными веществами покончено, закрываем их файл
fclose(f);
stack_func(prior_formula, cnt_formula, prior, cnt_prior, result, &len);
// Открыть файл печати результата
f = fopen(name_out, "w");
// Печать результата опыта
for(i = 0; i < len; ++i)
{
fprintf(f, "%s ", result[i]);
if(i + 1 % 5 == 0)
fprintf(f, "\n");
}
fprintf(f, "\n");
fclose(f);
// Освободить память
for(i = 0; i < cnt_formula; ++i)
free(prior_formula[i].n1);
for(i = 0; i < cnt_prior; ++i)
free(prior[i]);
return(0);
}