Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Tekhnologia_programmirovania.pdf
Скачиваний:
182
Добавлен:
08.04.2015
Размер:
1.76 Mб
Скачать

184 14

Рис.66. Прямоугольник на экране, созданный программой

14.4. Массивы структур

Структуры, как и любые другие переменные, можно объединять в массивы. Работу с массивами структур разберем на примере.

Напишем программу, которая читает входной поток и определяет число вхождений каждого ключевого слова языка Си в читаемый текст. Для хранения ключевых слов и счетчиков можно было бы создать два параллельных массива:

char *keyword[NKEYS]; int keycount[NKEYS];

Однако для более удобной работы лучше создать массив структур.

Программа 37. Подсчет ключевых слов

Для работы с ключевыми словами и их счетчиками определим следующий массив структур:

// Файл KeyWords.cpp

 

 

struct key{

 

 

// Структура key включает два поля:

char* word;

 

// word – указатель на ключевое слово,

int count;

 

 

// count - число его вхождений в текст

} keytab[] = {

 

// keytab - массив из структур key

"auto",

0, "break",

0,

// Массив структур инициализируется

"case",

0, "char",

0,

// списком ключевых слов и нулями для

"const",

0, "continue",0,

// чисел вхождений.

"default",

0, "do",

0,

// Отметим, что ключевые слова

"double",

0, "else",

0,

// упорядочены в алфавитном порядке.

"enum",

0, "extern",

0,

 

"float",

0, "for",

0,

 

"goto",

0, "if",

0,

 

"int",

0, "long",

0,

 

"register",

0, "return",

0,

 

"short",

0, "signed",

0,

 

"sizeof",

0, "static",

0,

 

"struct",

0, "switch",

0,

 

"typedef",

0, "union",

0,

 

"unsigned",0, "void",

0,

 

"volatile",

0, "while",

0

 

};

 

 

 

Объявление структуры key используется как тип элементов массива keytab. Размер массива определяется по списку инициализации,

Структуры, перечисления, объединения 185

который, строго говоря, следовало бы задавать для каждой структуры из массива в виде:

{{"auto", 0}, {"break", 0},…};

но поскольку имеется правильное чередование строк и чисел, внутренние фигурные скобки не обязательны. Ключевые слова перечисляются в алфавитном порядке, чтобы создать упорядоченный массив, с которым проще работать. Важно помнить, что сами ключевые слова, заданные строковыми константами, хранятся где-то в памяти, в структурах же key хранятся только указатели на эти строки.

//Продолжение файла KeyWords.cpp

//NKEYS - количество элементов массива keytab int NKEYS = (sizeof keytab / sizeof(key));

Оператор sizeof может выполняться на этапе компиляции. Выражение sizeof keytab равно размеру массива keytab в байтах, выражение sizeof(key) дает размер одной структуры, отношение этих выражений есть количество элементов массива.

Предусмотрим в программе функцию fgetword для чтения из файла очередного слова и функцию binsearch для поиска ключевого слова в массиве ключевых слов.

//Продолжение файла KeyWords.cpp #include <iostream.h>

#include <fstream.h> #include <string.h> #include <stdlib.h> #include <conio.h> #include <ctype.h>

//fgetword: читает очередное слово int fgetword(ifstream& , char*, int);

//binsearch: бинарный поиск в упорядоченном массиве int binsearch(char*, key*, int);

Перед тем как привести текст функции main, опишем ее работу. Программа предлагает указать имя файла, из которого следует прочитать текст, открывает файл на чтение и читает содержимое файла по одному слову. Словом считается последовательность букв и цифр, начинающаяся с латинской буквы, а также любой отдельный символ. Разделителями слов считаются пробельные символы. Каждое прочитанное слово ищется в массиве keytab с помощью функции binsearch. Если слово будет найдено в массиве, то есть является ключевым, увеличивается соответствующий счетчик. Ключевые слова, найденные в тексте, печатаются вместе со значениямия счетчиков.

186 14

Далее программа просит ввести имя выходного файла, который сначала открывается на чтение. Если выходной файл удается открыть на чтение, значит файл уже существует. В этом случае программа выдает запрос на подтверждение записи в существующий файл нового содержимого. Если пользователь программы дает положительный ответ, выходной файл открывается на запись, после чего в него выводятся найденные ключевые слова и их счетчики.

// Продолжение файла KeyWords.cpp

const int MAXLENWORD = 100;

// Максимальная длина слова

// Подсчет ключевых слов Си

 

void main()

 

 

{

 

 

int n;

 

 

char word[MAXLENWORD], c;

 

const int LENFN = 30;

 

// Длина имен файлов

char inpfname[LENFN],

 

// Имя входного

outfname[LENFN];

 

// и выходного файла

ifstream inpf;

 

// Входной и

ofstream outf;

 

// выходной файловые потоки

ifstream tmpf;

 

// Вспомогательный файловый поток

cout << "\nВведите имя входного файла\n";

cin.getline(inpfname, LENFN);

// Ввод строки с именем файла

inpf.open(inpfname);

 

// Открытие файла на чтение

if(!inpf){

 

// Если не удалось открыть файл

cerr << "Не могу открыть файл " << inpfname;

exit(1);

 

// Завершение программы

}

 

 

while(fgetword(inpf, word, MAXLENWORD) != EOF) // Пока есть слова

if(isalpha(word[0]))

// Если слово начинается с латинской буквы

if((n = binsearch(word, keytab, NKEYS)) >= 0) // и ключевое,

keytab[n].count++;

 

// увеличиваем счетчик

for(n = 0; n < NKEYS; n++)

// Вывод на экран ключевых слов,

if(keytab[n].count > 0)

 

// найденных в тексте

cout << keytab[n].count << ' ' << keytab[n].word << endl; cout << "Введите имя выходного файла\n";

cin.getline(outfname, LENFN);

// Ввод имени выходного файла

tmpf.open(outfname);

// Попытка открыть выходной файл на чтение

if(!tmpf == 0){

// Если удалось, то файл существующет

cout << "Файл " << outfname << " существует. Переписать? Y/N";

if((c = cin.get()) != 'Y' && c != 'y')

// Если не подтверждаем,

exit(0);

 

// завершение программы

}

 

 

tmpf.close();

// Закрываем временный файловый поток

outf.open(outfname);

// Открываем файл на запись

Структуры, перечисления, объединения 187

for(n = 0; n < NKEYS; n++) // Запись в файл if(keytab[n].count > 0)

outf << keytab[n].count << ' ' << keytab[n].word <<endl; getch();

}

Для поиска слова в упорядоченном по возрастанию массиве ключевых слов применен метод бинарного поиска, который состоит в следующем. Искомый элемент сравнивается со срединным элементом массива. Если искомый элемент меньше срединного, дальнейший поиск производится в первой половине массива, если больше, то во второй половине массива. При совпадении поиск заканчивается и возвращается номер найденного элемента массива. После каждого шага поиска диапазон поиска сужается в два раза. Отсюда происходит название метода. Если нижняя граница поиска превысит верхнюю, поиск прекращается как неудачный и в качестве результата возвращается –1.

//Продолжение файла KeyWords.cpp

//binsearch: найти слово в tab[0]...tab[n-1] int binsearch(char* word, key v[], int n)

{

int cond;

 

int low, high, mid;

 

low = 0;

// Нижняя граница поиска

high = n - 1;

// Верхняя граница поиска

while(low <= high){

 

mid = (low + high) / 2;

// Номер срединного элемента массива

// strcmp: стандартная функция сравнения строк if((cond = strcmp(word, v[mid].word)) < 0)

high = mid - 1; else if(cond > 0) low = mid + 1;

else

return mid;

}

return -1;

}

/* fgetword: принимает следующее слово или символ из файла. Возвращает первый символ слова. Слово – последовательность символов, начинающаяся с буквы или одиночный символ,

не являющийся буквой */

int fgetword(ifstream& f, char* word, int lim)

{

int c;

char* w = word;

188

14

 

while(isspace(c = f.get()))

// Пропуск пробельных

;

 

// символов в начале слова

if(c != EOF)

 

*w++ = c;

 

if(! isalpha(c)){

// Если первый символ не буква,

* w = '\0';

// считаем ее отдельным словом

return c;

 

}

 

 

for( ; --lim > 0; w++)

// Цикл по слову

if(!isalnum(*w = f.get())){

// Если поступила не буква и не цифра,

 

f.putback(*w);

// возвращаем символ обратно во ввод

}

break;

// и выходим из цикла

 

 

*w = '\0';

// Признак конца строки

return word[0];

// Возвращаем первый символ

}

 

 

Далее приведены диалоги с программой для двух запусков. В качестве входного файла в обоих случаях был указан файл с исходным текстом программы 34 Star.cpp. При первом запуске в качестве имени выходного файла было указано имя существующего файла. Программа это обнаружила и задала соответствующий вопрос.

Введите имя входного файла Star.cpp

3 const

2double

2for

1if 10 int

1return

Введите имя выходного файла Star.cpp

Файл Star.cpp существует. Переписать? Y/NN

Введите имя входного файла Star.cpp

3 const

2double

2for

1if 10 int

1return

Введите имя выходного файла Result36.txt