В качестве примера обработки текстовых файлов напишем программу, которая реализует наиболее часто испбльзуемую функцию препроцессора — подключение файлов с помощью оператора #include.
Ограничим синтаксис оператора:
? оператор начинается с первой позиции строки;
Другой, значительно более эффективный способ копирования файлов мы рассмотрим позже.
• подключаемый файл всегда задается в формате «имя_файла»;
• имя файла отделяется от оператора #i n elude только одним пробелом.
Таким образом, непосредственно имя файла начинается в операторе всегда с 11-й позиции и продолжается до символа-кавычки. Подключаемый файл тоже может содержать операторы #include. Ограничение вложенности, очевидно, задается глобальной константой F0PEN_MAX, определяющей максимальное количество одновременно открытых файлов и определенной в заголовке <stdio. h>.
Итак, в результате получаем:
#include "имя_файла"
При подключении этот оператор записывается в результирующий файл как комментарий:
// #include имя_файла
После этой строки в результирующий файл записывается содержимое указанного файла. Если подключаемый файл не был открыт, то в результирующий файл записывается строка-комментарий:
// #include имя_файла --(Error! File not open!
Подключаемые файлы могут, вообще говоря, располагаться в различных каталогах, но мы для проверки работы программы соберем их все в нашем каталоге TextFlles. Файл, который надо обрабатывать первым, задается в командной строке. Если имя задано неверно, то программа завершает работу. Результирующий файл с именем result.files размещается в том же каталоге TextFiles. Текст программы представлен в листинге 14.5.
Листинг 14.5. Обработка оператора
#include
string NameFiles(const char *namefile) { string path = "c:/textfiles/"; return path+namefile;
}
bool isInclude(const string line)
{ if (line.substr(0,8) == "#includeM) return true;
else return false;
}
void includeFile(const char *namefile, ofstream &result) { static int countfiles = 0; ++countfiles;
if (countfiles > F0PEN_MAX) // файлов больше положенного?
{ сегг << "Too mani files is opened!" << endl; abort();
}
string line; // читаемая строка
const string tenminus = " ";
const string include = "#include "; string comment;
string name = NameFiles(namefile); // попное имя файла
ifstream in; // очередной входной файл
продолжение &
Листинг 14.5 {продолжение)
in. open (name. c__str ()); // открыли
if (in. is__open()) // если открылся
{ getline(in, line); ' while(!in.eof())
{ if (islnclude(line)) // если #include
{ char file[FILENAME_MAX];
int i = 0; // извлекаем имя файла из #include
while(line[i+10]! = "") { file[i]=line[i+10]; i++; }
file[i]=0; // завершающий ноль
comment = ,7/"+tenminus+include+file+tenminus;
result << comment << endl;
includeFile(file, result); // пошли внутрь
result <<"//-end \n";
}
else result << line << endl; '
getline(in, line);
}
in.closeO ;
}
else // формируем ошибочный комментарий
{ const string Error = "Error! -- File not open!";
comment = ,,//"+tenminus+include+namef ile+" -- "+Error+'\n';
result << comment;
}
}
int main(int argc. char *argv[])
{ ofstream result ("c:/textfiles/result.files");
if(!result.is_open()) // ошибка при открытии
{ cerr << "Error result file!" << endl; return 1;
}
includeFile(argv[l], result); result.close(); return 0;
}
Главная программа просто открывает результирующий файл и вызывает рекурсивную функцию обработки, передавая ей в качестве параметра имя файла из командной строки и поток-результат. Ошибки практически не обрабатываются, чтобы не отвлекаться от основной задачи — обработки файлов.
Основную работу выполняет рекурсивная функция includeFilе(), аргументом которой является имя обрабатываемого файла и поток-результат. Функция в начале считает количество открытых файлов — можно запрограммировать здесь более серьезную обработку ошибки (например, генерировать исключение). Если все в порядке, то формируется полное имя файла — работает вспомогательная функция NameFiles(). Далее файл открывается, и в цикле читаются строки файла. Если прочитана обычная строка, то она просто выводится в результирующий файл. Если же строка содержит оператор #i nclude (что определяет простая функция-предикат isIncludeO), то формируется и выводится строка-комментарий о включаемом файле и выполняется рекурсивный вызов. По окончании файла выводится дополнительная строка минусов со словом «end». Таким образом, включенный файл оказывается обрамленным двумя строками-комментариями. Если же при открытии файла возникли проблемы, то формируется строка-комментарий с ошибкой и функция завершает обработку текущего файла.
Создадим в каталоге TextFiles три небольших текстовых файла, a.txt, b.txt и d.txt:
• файл a.txt!:
ааааааааааааааа #include "b.txt" ааааааааааааааа
• файл b.txt:
bbbbbbbbbbbbbbbbbb #include "d.txt" bbbbbbbbbbbbbbbbbb #include "dd.txt" bbbbbbbbbbbbbbbbbb
• файл d.txt:
dddddddddddddddddd dddddddddddddddddd
Файл a.txt подключает файл b.txt, который, в свою очередь, подключает файл d.txt и ошибочный файл dd.txt. Результат работы нашей программы получается следующий:
ааааааааааааааа
// #include b.txt
bbbbbbbbbbbbbbbbbb
// #include d.txt dddddddddddddddddd dddddddddddddddddd
//-end -
bbbbbbbbbbbbbbbbbb
// #include dd.txt
// #include dd.txt -- Error! File not open!
//-end
bbbbbbbbbbbbbbbbbb
//-end-
ааааааааааааааа
Как видим, вложенные файлы обрабатываются совершенно правильно.
Собственно, если бы не нужно было формировать комментарий вокруг вложенного файла, функция includeFi 1е() была бы значительно короче (листинг 14.6).
Листинг 14.6. Функция inctudeFile()
void includeFile(const char *namefile, ofstream &result) { static int countfiles = 0;
++countfiles; // проверяем countfiles > F0PEN_MAX
if (countfiles > F0PEN_MAX)
{ cerr << "Too mani files is opened!" << endl; abort(); }
Листинг 14.6 (продолжение) string line;
string name = NameFiles(namefile); ifstream in; in.open(name.c_str()); if(in.is_open()) { getline(in, line); while(!in.eof())
{ if (islnclude(line)) includeFile(file, result);
else result << line << endl;
getline(in, line);
}
in.close();
}
else result << "Error! -- File not open!" << endl;
}
Этот вариант функции вполне можно использовать в «настоящем» препроцессоре. |