Системное программирование и операционные системы (часть 1)

Лабораторная работа № 8

"Объединение файлов описания сценария диалога"

  

Введение

 Задача объединения информации, находящейся в многочисленных файлах, в один файл достаточно часто возникает при разработке и подготовке к функционированию системных программ и программ пользователей. Например, редактор связей осуществляет объединение большого количества объектных модулей в один загрузочный модуль. При решении подобных задач, кроме "механического" объединения файлов практически всегда требуется изменить их логическое содержание. При этом изменению подвергается не вся информация в таких файлах, а только так называемы "ссылки".

1. Постановка задачи

В процессе выполнения предыдущей лабораторной работы было сознано некоторое количество файлов, содержащих информацию для организации "шага диалога" типа меню.

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

2. Для организации работы диалоговой надстройки необходимо модифицировать программу проведения шага диалога.

 

2. Методические указания.

2.1 Структура файлов

Пусть файлы шага диалога построены по следующей схеме:

 

файл uo.txt

файл u2.txt

 файл u7.txt

 Что почитаем?
#
 ООП
 Сист. прог.
 Прог. обеспеч.
 Закончим
#
1 u1.txt
1 u2.txt
1 u3.txt
0 0

 Изучим?
#
 ассемблер
 компиляторы
 макрогенераторы
 ни чего
#
1 u9.txt
1 u8.txt
1 u7.txt
0 -1

 Читаем о?
#
алгоритмах
реализациях
ни чего
#
0 16
0 15
0 -1

подробнее смотрите здесь.

Управляющие строки с кодом "0" указывают на "код возврата". "Код возврата" равный -1 определяет "всплытие" на предыдущий шаг. Символ "#" - разделитель строк "вопроса" и "ответа". 

2.2 Описание алгоритма «сборки»

"Сборку" этих файлов будем осуществлять "в два прохода". 

В начале в файл tmp_link.txt последовательно поместим информацию из всех файлов u1.txt, u2.txt и т.д.

При этом номера строк НАЧАЛА каждого из этих файлов в файле  tmp_link.txt и, соответственно, имя файла будем помещать в служебную таблицу sym_TAB, структура которой может быть определена так:

struct sym_tab
{ int n_str;                // номер строки в выходном файле
char n_file[max_line];      // имя файла – шага диалога
};

т.е. в таблицу будем помещать «номер_стоки_в_выходном_файле» и, соответственно, «имя_исходного_файла».

Для выяснения всех имен вспомогательных исходных файлов и построения файла tmp_link.txt, воспользуемся следующим алгоритмом:

1 шаг. Открыть выходной файл tmp_link.txt в режиме «запись».

Например:

   out=fopen(file_out, "w");

   if (out == NULL )
               {printf( "Cannot open output file <%s> \n", file_out);
                getch(); return -1;};

2 шаг. Установить переменную (например num_str_out=0), содержащую номер текущей строки в выводном файле равной 0.

3 шаг. Установить указатель СТЕКА и указатель sym_TAB в начало (см. функции для работы с таблицей и стеком).

4  шаг. Поместить в СТЕК имя первого файла – вершины диалога (для примера из лаб. №2 – это файл F0.txt или U0.txt);

        Например - stek_push(file_tek);

5 шаг. Повторять (шаги 5а, 5б, 5c и 5d ) пока в СТЕКЕ есть имена файлов ( например while ( stek_pop(file_tek) != 0 ) )

      5а. Взять имя файла из СТЕКА (в переменную file_tek уже поместили имя «текущего» файла)

      5б. Открыть file_tek для чтения

                  h=fopen(file_tek, "r");
                 if (h == NULL )
                     { printf( "Cannot open input file <%s> \n", file_tek);
                       
fclose (out); getchar();  return -1;};

      5с. Поместить номер строки и имя текущего файла (см. функции работы с таблицей) в таблицу sym_tab

                // поместить в sym_tab
                t.n_str=num_str_out;
                strcpy(t.n_file,file_tek);
                to_sym_tab(t);

      . Повторять (шаги 5с-0, 5с-1, 5с-2) пока есть записи в файле file_tek ( например: while ( mfgets(buf, max_line+2, h) != NULL ))

           5с-0. Прочитать очередную запись в buf.

               5с-1. Записать записать в выходной файл и увеличить номер строки

                  например:

                      fprintf(out, "%s\n", buf);
                      num_str_out++;

           5с-2. Если «код строки – управляющий код» (первый символ) == '1', то "взять" новое имя файла и положить это имя в СТЕК

                  например:

                     if ( buf[0] == '1' )
                       {
                          sscanf( buf+2, "%s", file_new);
                          stek_push(file_new);
                        };

 

      5d шаг. Закрыть файл h (например -  fclose (h);)

Вторую часть преобразования информации из файла tmp_link.txt в результирующий файл menu_link.txt ("второй проход") можно выполнить так:

1 шаг. Открываем входной и выходной файлы

       например

              h = fopen(file_out, "r");
             if (out == NULL ){printf( "Cannot open input temp file <%s> \n", file_out);
                                     getch(); return -1;};
             out=fopen(file_menu, "w");
             if (out == NULL ){printf( "Cannot open output file <%s> \n", file_menu);
                                      fclose(h); getch(); return -1;};


 

2 шаг. Повторять пока в входном файле есть записи например:   while ( mfgets(buf, max_line+2, h) != NULL )  )

           . Прочитать очередную запись в buf.

               2б. Если «код строки – управляющий код» (первый символ) == '1', то  ( if ( buf[0] == '1' ) ):

                          2б-1. извлечь из прочитанной строки (buf) имя файла, на который ссылается это управляющая запись.

                                           sscanf( buf+2, "%s", t.n_file);

                          2б-2. искать в таблице sym_tab запись с этим именем и определить номер строки выходного файла

               if (seek_sym_tab( t ) == 1)
 
                  { printf("Файл <%s> не найден в таблице символических имен....\n", t.n_file);
                     fclose(h); fclose(out); return -1;
 
                  };

                          2б-3. изменить имя файла в прочитанной строке (buf) на найденный в sym_tab номер записи выходного файла

                sprintf(buf+2, " %d", t.n_str);

              2с. Записать строку (buf) в выходной (результирующий) файл.

           fprintf(out, "%s\n", buf);

 

2.3 Вспомогательные подпрограммы и объекты

константы

#define max_line 80

#define max_sym_tab 20

Переменные и программы для работы с таблицей:

// программы работы с символической таблицей имен
sym_tab TAB[max_sym_tab];
int ptr_tab=0;
// поместить в таблицу
int to_sym_tab(sym_tab t)
 {
   TAB[ptr_tab].n_str=t.n_str;
   strcpy(TAB[ptr_tab].n_file, t.n_file);
   if ( (ptr_tab++) > max_sym_tab)
     {
        printf("Переполнение таблицы имен ....\n");
        exit(666);
        return 0;
      };
 return 1;
 };
// распечатать таблицу
void print_sym_tab()
 { int i;
   for (i=0; i < ptr_tab; i++)
     printf("#=%d n_str=%d n_file=<%s>\n",i, TAB[i].n_str, TAB[i].n_file);
     return;
 };

// найти в таблице и заполнить входную структуру  - по имени находит № строки
// возращает 1 при успехе или 0 при невозможности найти...
int seek_sym_tab(sym_tab &t)
 { int i;
   for (i=0; i < ptr_tab; i++)
        { if (strcmp(TAB[i].n_file,t.n_file) == 0 )
             {   t.n_str=TAB[i].n_str;
                 return 0;
             };
        };
 return 1;
 };
 

"Программы работы со стеком".

В стек будем помещать имена файлов, на которые ссылается текущий файл. Простейшее решение может выглядеть так:

// программы организации СТЕКА
// память для СТЕКА
char s_file[10][max_line];
// указатель СТЕКА
int s_ptr=-1;

// программы работы с стеком
// поместить в СТЕК
int stek_push( char * file) // поместить в стек
{ s_ptr++;
  if(s_ptr > 9) {printf("переполнение стека file=%s", file);
                 exit(16); };
  strcpy(s_file[s_ptr], file);
  return 1;
};


//
взять из СТЕКА
int stek_pop( char * file) //
взять из стека
{
   if(s_ptr < 0 ) return 0;
   strcpy(file,s_file[s_ptr]);
   s_ptr--;
   return 1;
};
 

Программа «чтения файла»

// чтение данных из файла - читается Запись, но возвращается С строка в buf
//
длиной не более l_str_max  из файла h
//
в конце файла
возращается NULL
char * mfgets( char* buf, int l_str, FILE* h)
{
  char c;
  int l=0, buf_l=0;
   c=fgetc(h);
   if (c == EOF ) return NULL;
   while (  (c != '\n') && (c != '\r') && (c != EOF) )
      {
       if ( (l++) <  (l_str-1) ) buf[buf_l++]=c;
       c=fgetc(h);
      };
   buf[buf_l]='\0';
   return buf;
};


 

2.4 Модификация программы проведения шага диалога

Для простоты реализации программы проведения шага диалога введем все строки (записи) файла menu_link.txt в таблицу, расположенную в оперативной памяти:

//=======================================================
// работа с файлом в ОП
char  TFILE[1000][l_str_max]; //таблица
int   nTFILE;                // кол-во элементов
// указатель следующей строки в TFILE
int tTFILE;

// открыть файл и сформировать  таблицу в памяти....(упрощенный вариант)
int t_open(char * nfile)
{
   
char buf[l_
str_max];
   
int n=0;
   
FILE *h;
   
h=
fopen(nfile, "r");             
   
if (h == NULL ){
printf( "Cannot open input file <%s> \n", nfile); getch(); return -1;};
   
while (
mfgets(buf, l_str_max, h) != NULL )
      {
        strcpy(TFILE[n],buf);
        n++;
      };
    nTFILE=n;
    fclose(h);
    return 0;
};

//  установить указатель на заданную запись файла  (Тфайла)....
int t_seek(int n)
  {
    if ( n <= 0 && n > nTFILE)
      { printf( "
Ошибка в указателестроки %d \n", n); getch(); return 0;};
    tTFILE=n; return 1;
};


// чтение данных из Тфайла - читается Запись, но возращается С строка в
buf
// длиной не более l_str_max  из файла h
// в конце файла возращается NULL
char * t_
mfgets( char* buf)
{
 
if (
tTFILE >= nTFILE ) return NULL;
 
strcpy(buf, TFILE[tTFILE]);
 
tTFILE++;
     return buf;
};

//========================================================

// шаг диалога
int step_d(char * mfile)

{
//  строки вопроса
//  и строки ответа
//  до строки с символом #
//  строки "ответа"  - 0 или 1 затем пробел
//  затем код_возврата или имя_файла_шага_меню


  int num_str, rc=-1, num, k;
//@@@@  char nfile[80];
int nfile;  // - здесь в этой переменной будет храниться
                //   номер строки в файле-таблице

  char buf[80];
 //@@@@@  FILE *h;
//@@@@@ 
strcpy(nfile, mfile);
    
t_open(mfile);
     
nfile=0;
beg:
//@@@  h=fopen(nfile, "r");
//@@@ 
if (h == NULL )
//@@@      {
printf( "Cannot open input file <%s> \n", nfile); return -1; };
 // начало шага диалога.....
   t_seek(nfile);
     clrscr();
     num_str=1;
//  задаем вопрос и варианты ответа
next:
   if (t_mfgets(buf) == NULL )
      {   printf( "Ошибка в области вопроса строка N %d в файле <%s> \n", num_str, mnfile);
          return -1;
      };
   num_str++;
   if (buf[0] == '#' ) goto ask;
   printf( "%s\n", buf);
   goto next;
ask:
   scanf("%d",&num);
   if ( num <= 0 || num >= 10) goto err;
// поиск "нужной" строки "ответа"
   k=0;
next1:
   if (t_mfgets(buf) == NULL ) goto err;
   k++;
   if ( k != num ) goto next1; // не нашли
 

// нашли анализ типа строки ответа
   if (buf[0] == '0')  // ВСЕ - код возврата....
       { sscanf( buf+2, "%d", &rc); return rc; };
   if (buf[0] == '1')  // новый узел диалога....
       { sscanf( buf+2, "%d", nfile); goto beg; };
err:
   gotoxy(1,20);
   printf(" Вы ошиблись (%d)...., повторите\n", num);
   getchar();
   getchar();
   goto beg;
};

 

 

Отчёт должен содержать:

1.    Описание структуры входных, выходного и «промежуточного» файлов

2.    Описание алгоритма «слияния» файлов

3.    Укрупнённую блок – схему программы «объединение файлов»

4.    Укрупнённую блок - схему, реализующую шаг диалога на основе «объединенного файла»

5.    Распечатку исходных модулей, разработанной программы (сам диалог демонстрируется преподавателю)

 

Контрольные вопросы

 

·         - Стек, очередь, поиск в таблице.

·         - Обход дерева графа сценария диалога. Основные алгоритмы.

·         - вопросы по пунктам отчета и программе.

 

 

Примеры решений можно посмотреть здесь и здесь.