Написать программу форматирования текста, читаемого из файла unformt.txt и состоящего из строк ограниченной длины. Слова в строке разделены произвольным количеством пробелов. Программа должна читать входной файл по строкам, форматировать каждую строку и выводить результат в выходной файл formatd.txt. Форматирование заключается в выравнивании границ текста слева и справа путем равномерного распределения пробелов между соседними словами, а также в отступе с левой стороны страницы на margin позиций, то есть результирующий текст должен находиться в позициях margin + 1.. margin + maxl_line. Кроме этого, программа должна подсчитать общее количество слов в тексте.
На примере этой задачи мы показываем технологию разработки многофайловых проектов.
Алгоритм решения задачи не представляет особой сложности:
- Открыть входной файл.
- Читать файл построчно в текстовый буфер line, попутно удаляя возможные пробелы в начале строки (до первого слова).
- Для каждой строки line выполнить следующие действия:
- Вычислить величину интервала (количество пробелов), которую необходимо обеспечить между соседними словами для равномерного распределения слов в пределах строки.
Вывести каждое слово из строки line в выходной файл, вставляя между словами необходимое количество пробелов и одновременно увеличивая счетчик слов на единицу.
- После обработки последней строки входного файла вывести на экран значение счетчика слов и закрыть выходной файл.
Разбиение на подзадачи.
В результате детализации описанного алгоритма определяем спецификации нужных нам функций:
- void DefInter (const char* pline, int & base_int, int & add_int, int & inter) определяет для строки, на которую указывает pline, количество межсловных промежутков inter, требуемую величину основного интервала base_int для каждого промежутка (количество пробелов) и величину дополнительного интервала add_int, определяемую как остаток от деления общего количества пробелов в строке на количество межсловных промежутков; последняя величина должна быть равномерно распределена путем добавления одного пробела в каждый из первых add_int промежутков;
- void GetLine (FILE* finp, char* pline) читает очередную строку из входного файла в массив символов с адресом pline, ликвидируя при этом пробелы в начале строки;
- void PutInterval (FILE* fout, const int k) выводит очередной интервал, состоящий из пробелов;
- int PutWord (FILE* fout, const char* pline, const int startpos) выводит очередное слово в выходной файл, начиная с позиции startpos текущей строки pline; возвращает номер позиции в строке pline, следующей за последним переданным символом, или 0 — если достигнут конец строки;
- int SearchNextWord (const char* pline, const int curpos) возвращает номер позиции, с которой начинается следующее слово в строке pline, или 0, если достигнут конец строки (поиск начинается с позиции curpos).
Разбиение на модули.
Наша программа будет располагаться в двух исходных файлах: task7_7.cpp -с функцией main, edit. ерр — с реализацией перечисленных выше функций, а также заголовочный файл edit.h с интерфейсом этих функций.
Ниже приводится содержимое этих файлов.
////////////////////////////////////////////////////////////////
// Файл Task7_7.cpp
#include < stdio.h>
#include < string.h> #include < stdlib.h>
#include " edit.h"
// Глобальные переменные
const int maxl_line = 63;
const int margin = 5;
int main() {
FILE* finp;
FILE* fout;
char line[maxl_line + 1];
int b_i, a_i, start, next, inter;
int nword = 0;
printf(" Работает программа Task7_7.\n");
if(! (finp = fopen(" unformt.txt", " r"))) {
printf(" Файл unformt.txt не найден.\n"); exit(0);
}
printf(" Читается файл unformt.txt.\n");
if(! (fout = fopen(" formatd.txt". " w"))) {
printf(" Файл formatd.txt не создан.\n") exit(0);
}
printf(" Выполняется запись в файл formated.txt.\n");
whi1e(GetLine(finp. line)) { DefInter(line, b_i, a_i, inter);
PutInterval(fout, margin);
next = PutWord(fout, line, 0, nword);
for (int i = 0; i < inter; i++) {
start = SearchNextWord(line, next);
PutInterval(fout, b_i);
if (a_i) { a_i--; PutInterval(fout, 1); }
next = PutWord(fout, line, start, nword);
if (! next) break;
}
fprintf(fout, " \n"); }
printf(" \nКоличество слов - %d\n", nword); fclose(fout);
printf(" Работа завершена.\n");
return 0;
}
/////////////////////////////////////////////////////
// Файл Edit.h
// Прототипы функций
void DefInter(const char* pline, int& base_int, int& add_int, int& inter);
int GerLine (FILE*, char*);
void PutInterval(FILE*, const int);
int PutWord(FILE*, const char*, const int, int&);
int SearchNextWord(const char*, const int);
// Глобальные переменные
extern const int maxl_line;
////////////////////////////////////////////////////
// Файл Edit.cpp
#include < stdio.h>
#include < string.h>
#inclede " edit.h"
int GetLine(FILE* finp, char* pline) {
int i = 0;
char c;
while ((c = fgetc(finp)) == ' ') i++;
if(c == EOF) return 0;
fseek(finp, -1, SEEK_CUR);
fgets(pline, maxl_line – i + 1, finp);
plane[strlen(pline) – 1] =0;
return 1;
}
int SearchNextWord(const char* pline, const int curpos) { int i = curpos;
while(pline[i]! = ' ') { if (pline[i] == '\n') return 0; i++;
}
while (pline[i] == ' ' & & pline[i + 1] == ' ') i++;
return i + 1;
}
void DefInter(const char* pline, int& base_int, int& add_int, int& inter) {
int к = 0, end;
end = strlen(pline) – 1;
while ((pline[end] == ' ') || (pline[end] == '\n') || (paline[end] == '\r')) end;
inter = 0;
for (unsigned int i = 0; i < end; i++) {
if (pline[i] == ' ') {
k++;
if (pline[i + 1]! = ' ') inter++;
}
}
int blank_amount = k + maxl_llne – end;
if (! k) {
base int = 0;
add_int = 0; }
else {
base_int = blank_amount / inter;
add_int = blank_amount % inter;
}
return;
}
int PutWord (FILE* fout, const char* pline, const int startpos, int& n) {
int i = startpos;
char c;
n++;
while ((c = pline[i++])! = ' ') { fprintf(fout, " %c", c);
if ((c == '\n') || (c == '\0')) {i = 0; break; }
}
return i - 1;
}
void PutInterval(FILE* fout, const int k) {
for (int i = 0; i < k; i++) fprintf(fout, " ");
return;
}
///////////////////////////////////////////////////////////
|
Обратите внимание, что имена функций мы здесь записали (для разнообразия) в стиле Microsoft, с использованием прописных букв для выделения осмысленных частей имени. Константу maxline следует задавать большей, чем максимальная длина строки исходного файла. В качестве самостоятельного упражнения измените программу так, чтобы можно было форматировать текст и в более узкую колонку, чем в исходном файле.
Приведем результаты тестирования этой программы.
Содержимое входного файла unformt.txt1;
23. Не терпеть и малого своего недостатка - вот
признак совершенства. От изъянов духовных и телесных
редко кто свободен, но часто их лелеют, когда от них легко бы исцелиться. Вчуже досадно видеть
разумному, как ничтожный изъян порой портит
великолепное сочетание достоинств, - довольно и
облачка, чтобы затемнить солнце. Родимые пятна на
доброй славе злоба людская сразу подметит - и упорно в них метит. Особенно ценно искусство скрывать свой
недостаток, обращая его в преимущество. Так. Цезарь скрывал свою плешь лавровым венком.
|
Содержимое выходного файла formatd.txt:
23. Не терпеть и малого своего недостатка – вот признак совершенства. От изъянов духовных и телесных редко кто свободен, но часто их лелеют, когда от них легко бы исцелиться. Вчуже досадно видеть разумному, как ничтожный изъян порой портит великолепное сочетание достоинств, - довольно и облачка, чтобы затемнить солнце. Родимые пятна на доброй славе злоба людская сразу подметит - и упорно в них метит. Особенно ценно искусство скрывать свой недостаток, обращая его в преимущество. Так. Цезарь скрывал свою плешь лавровым венком.
|
Протестируйте эту программу на других текстах.
Давайте повторим наиболее важные моменты этого семинара.
- Функция — это именованная последовательность операторов, выполняющая-законченное действие. Функции нужны для упрощения структуры программы.
- Интерфейс грамотно написанной функции определяется ее заголовком.
- Для вызова функции надо указать ее имя и набор аргументов.
- В определении, в объявлении и при вызове функции типы и порядок следования аргументов и параметров должны совпадать.
- Передача параметров в функцию может выполняться по значению или по адресу.
- Входные данные функции надо передавать по значению или по константной ссылке, результаты ее работы — через возвращаемое значение, а при необходимости передать более одной величины — через параметры по ссылке или указателю.
- При написании функции нужно предусмотреть все возможные ошибки и обеспечить пользователя функции средствами их диагностики.
- Печать диагностических сообщений внутри функции нежелательна.
- Функция может иметь несколько параметров со значениями по умолчанию. Они
должны находиться в конце списка параметров. - Массивы всегда передаются в функцию по адресу. Количество элементов в массиве должно передаваться отдельным параметром.
- Рекурсивная функция должна содержать хотя бы одну нерекурсивную ветвь. При использовании рекурсии следует учитывать возникающие при этом проблемы и накладные расходы.
- В многофайловых проектах важно грамотно разбить задачу на подзадачи и распределить функции по файлам.
- Для предотвращения ошибок компиляции, связанных с повторным включением заголовочных файлов, следует использовать так называемые стражи включения.