SHPORA.net :: PDA | |
Main FAQ гуманитарные науки естественные науки математические науки технические науки Модули и текстовые файлы Модули в Turbo Pascal В Turbo Pascal каждый модуль представляет собой отдельный файл (один файл!), который имеет расширение .pas. Исходный текст модуля имеет следующую структуру: 1. Заголовок 2. Интерфейсная часть 3. Исполнительная часть 4. Секция инициализации Заголовок Заголовок модуля состоит из ключевого слова UNIT и идентификатора – имени модуля. Модуль должен быть сохранен в файле, имя которого совпадает с именем модуля. Например, если модуль начинается со строки Unit MyUnit; тогда этот модуль должен быть сохранен в файле с именем MyUnit.pas Интерфейсная часть Дадим краткое определение: интерфейс – это способ взаимодействия. В данном случае интерфейс – способ взаимодействие основной программы (или другого модуля) с нашим модулем. Взаимодействие с модулем производится через использование определенных в модуле констант, типов, глобальных переменных (чего делать не надо!!!), и через вызов определенных в модуле процедур и функций. Модульный подход подразумевает, что не все константы, типы, переменные, процедуры и функции, определенные в модуле, могут быть использованы вне модуля. Могут быть использованы лишь те из них, которые объявлены в интерфейсной части. Если в интерфейсной части нет упоминания – то такую константу, тип, переменную, процедуру или функцию можно использовать только внутри модуля – извне она недоступна. Интерфейсная часть модуля начинается с ключевого слова interface и заканчивается перед ключевым словом implementation. Интерфейсная часть модуля содержит почти те же разделы что и основная программа: - раздел объявления используемых модулей - раздел объявления констант - раздел объявления типов - раздел объявления переменных - раздел объявления процедур и функций Как и в основной программе, эти разделы могут идти в любом порядке (за исключением первого), каждый из них (опять же, за исключением первого) может встречаться несколько раз, либо вообще не разу. Заметим, что в разделе объявления процедур и функций указываются только заголовки подпрограмм. Исполнительная часть Если в интерфейсной части указываются только заголовки подпрограмм, (подпрограммы объявляются), то в исполнительную часть модуля включаются тела подпрограмм, (подпрограммы определяются). Исполнительная часть модуля начинается с ключевого слова implementation и заканчивается началом секции инициализации, если она есть, либо словом end с точкой. Исполнительная часть модуля содержит те же разделы что и основная программа: - раздел объявления используемых модулей - раздел объявления констант - раздел объявления типов - раздел объявления переменных - раздел определения процедур и функций Как и в основной программе и интерфейсной части эти разделы могут идти в любом порядке, каждый из них может встречаться несколько раз, либо вообще не разу (раздел объявления используемых модулей всегда идет первым, и встречается только один раз). Все что объявлено в исполнительной части может быть использовано только внутри исполнительной части (!!! Обратите на это внимание !!!). С другой стороны в исполнительной части может быть использовано все, что объявлено в интерфейсной части. Секция инициализации Иногда перед использованием подпрограмм, включенных в модуль, требуется произвести инициализацию некоторых глобальных переменных этого модуля. Необходимые действия можно произвести в секции инициализации модуля. Операторы этой секции выполняются единственный раз в момент запуска программы. Секция инициализации размещается в самом конце модуля, начинается она словом begin, заканчивается end (с точкой). Если инициализация в модуле не требуется, то в секции помещается только end (с точкой). Пользовательские модули для работы с текстом Рассмотрим использование модулей в задачах обработки текстов. Разработаем модуль, в котором соберем функции и процедуры, используемые в двух задачах: Задача 1. Переписать входной файл в выходной файл. При этом самое длинное слово в каждой строке выделить угловыми скобками. Ограничение: считаем, что строки в файле не превышают нормальной длины в 70-80 символов. Задача 2. Дан исходный файл с текстом на русском языке. Вывести на экран содержимое исходного файла постранично. При выводе на экран выделить цветом все слова, в которых чередуются гласные/согласные. Переписать содержимое исходного файла в выходной файл, выделяя БОЛЬШИМИ буквами все выделенные при выводе на экран слова. Примечание: Имя входного файла вводится с клавиатуры. Имя выходного файла отличается от имени исходного файла только расширением. Создадим модуль, в котором соберем функции по работе со словами: поиск слова в строке, поиск самого длинного слова в строке, определение, состоит ли слово из чередующихся гласных и согласных букв. Вот этот модуль: {Модуль Words. Содержит функции поиска и анализа слов в строке. } unit Words; interface {интерфейсная часть модуля} {Функция FindNextWord. Ищет в строке S следующее слово начиная с символа Start. Если слово найдено, то возвращается True, и возвращается индекс первого символа слова (через BeginWord) и его длина (через LengthWord). Если слово не найдено, возвращается False.} function FindNextWord( const S : String; Start : Integer; var BeginWord : Byte; var LengthWord : Byte) : Boolean; {Функция FindMaxLenWord. Ищет в строке S самое длинное слово. Если ни одного слова в строке S не найдено, то возвращается False. В противном случае возвращается True, при этом через BeginMaxWord возвращается индекс первого символа самого длинного слова, а через LengthMaxWord - его длина.} function FindMaxLenWord( const S : String; var BeginMaxWord : Byte; var LengthMaxWord : Byte ) : Boolean; {Функция IsChereda. Возвращает True, если S содержит строку, состоящую только из чередующихся русских согласных и русских гласных букв. Возвращает False в противном случае. } function IsChereda(const S:string):Boolean; implementation {Исполнительная часть модуля} const {множества символов} SmallRusLetters : set of char = ['а'..'п','р'..'я','ё']; BigRusLetters : set of char = ['А'..'Я','Ё']; SmallLatLetters : set of char = ['a'..'z']; BigLatLetters : set of char = ['A'..'Z']; Digits : set of char = ['0'..'9']; RusGlasn : set of char = ['а','е','ё','и','о', 'у','ы','э','ю','я', 'А','Е','Ё','И','О', 'У','Ы','Э','Ю','Я']; var RusLetters : set of char; {все русские буквы} LatLetters : set of char; {все латинские буквы} Letters : set of char; {все буквы и цифры, т.е. те символы, из которых могут состоять слова} RusSoglasn : set of char; {русские согласные буквы} {=====================================================} {Функция IsLetter. Возвращает True, если C является символом слова. Возвращает False, если C является разделителем. } function isLetter(c: char): boolean; begin isLetter:=c in Letters; end; {=====================================================} {Функция IsRusGlasn. Возвращает True, если C является русской гласной буквой. Возвращает False в противном случае. } function isRusGlasn(c: char): boolean; begin isRusGlasn := c in RusGlasn; end; {=====================================================} {Функция IsRusSoglasn. Возвращает True, если C является русской согласной буквой. Возвращает False в противном случае. } function isRusSoglasn(c: char): boolean; begin isRusSoglasn := c in RusSoglasn; end; {=====================================================} {Функция IsChereda. Возвращает True, если S содержит строку, состоящую только из чередующихся русских согласных и русских гласных букв. Возвращает False в противном случае. } function IsChereda(const S:string):Boolean; var IsSogl : Boolean; {текущая буква является согласной} IsPrevSogl : Boolean; {предыдущая буква является согласной} i : Byte; {счетчик цикла} len : Byte; {длина строки} begin len := length(S); {вычисляем длину строки} {проверяем, состоит ли строка только из русских гласных и согласных букв} for i := 1 to len do if not IsRusGlasn(s[i]) and not IsRusSoglasn(s[i]) then begin {если нет, то завершаем работу функции} IsChereda := false; exit; end; {начиная со второго символа слова, и до конца слова} for i := 2 to len do {ЭТОТ символ должен быть НЕ ТАКИМ как ПРЕДЫДУЩИЙ} if IsRusSoglasn(s[i-1]) <> IsRusGlasn(s[i]) then begin {если нет, то завершаем работу функции} IsChereda := false; exit; end; IsChereda := true; {все символы чередуются} end; {=====================================================} {Функция FindNextWord. Ищет в строке S следующее слово начиная с символа Start. Если слово найдено, то возвращается True, и возвращается индекс первого символа слова (через BeginWord) и его длина (через LengthWord). Если слово не найдено, возвращается False.} function FindNextWord( const S : String; Start : Integer; var BeginWord : Byte; var LengthWord : Byte) : Boolean; var i : Integer; {индекс может выйти за границы 255 - поэтому Byte использовать нельзя!!!} Len : Byte; {длина строки} Begin {вычисляем длину строки} Len := length(s); {ищем начало слова, начиная со стартового символа строки} i := Start; {в цикле продвигаем i вперед по строке, до тех пор пока не встретиться буква, или пока не кончится строка } while not isLetter(S[i]) and (i <= Len ) do i := i + 1; {сейчас i указывает на первый символ найденного слова} BeginWord := i; {ищем конец слова} {для этого продвигаем i вперед, до тех пор пока не встретиться НЕ БУКВА, или пока i не выйдет за пределы строки} while isLetter(S[i]) and ( i <= Len ) do i := i + 1; {сейчас i указывает на первый символ-разделитель, следующий за словом (или i указывает на символ за пределами границ строки). Длину слова вычисляем как разность между индексами его последнего и первого символов } LengthWord := i - BeginWord; {Если вычисленная длина слова больше 0, значит слово в строке найдено - возвращаем True. Иначе - слова в строке нет - возвращаем False } if LengthWord > 0 then FindNextWord := true else FindNextWord := false; end; {=====================================================} {Функция FindMaxLenWord. Ищет в строке S самое длинное слово. Если ни одного слова в строке S не найдено, то возвращается False. В противном случае возвращается True, при этом через BeginMaxWord возвращается индекс первого символа самого длинного слова, а через LengthMaxWord - его длина.} function FindMaxLenWord( const S : String; var BeginMaxWord : Byte; var LengthMaxWord : Byte ) : Boolean; var i : Integer; {индекс может выйти за границы 255 - поэтому Byte использовать нельзя!!!} Beg : Byte; {начало строки} Len : Byte; {длина строки} begin I := 1; {начинаем поиск слов с начала строки} LengthMaxWord := 0; {длина самого длинного слова в строке =0, потому что еще не одного слова не нашли } {ищем все слова в строке} while FindNextWord(S,i,Beg,Len) do begin {Если найденное слово длиннее всех ранее найденных} if len>lengthMaxWord then begin {запоминаем начало и длину найденного слова} lengthMaxWord:=len; BeginMaxWord:=beg; end; i:=beg+len; {продолжаем поиск с символа, следующего за концом последнего найденного слова} end; {если не одного слова в строке не найдено, то длина самого длинного слова будет равна 0. В этом случае возвращаем False. В противном случае возвращаем True.} if lengthMaxWord=0 then findMaxLenWord:=False else findMaxLenWord:=True; end; begin {Секция инициализации} {"собираем" множества из подмножеств } RusLetters := smallRusLetters + bigRusLetters; LatLetters := smallLatLetters + bigLatLetters; Letters := RusLetters + LatLetters + Digits; RusSoglasn := RusLetters - RusGlasn; end. Теперь приведем текст программы – решение первой задачи: { Задача 1. Переписать входной файл в выходной файл. При этом самое длинное слово в каждой строке выделить угловыми скобками. Ограничение: считаем, что строки в файле не превышают нормальной длины в 70-80 символов. } uses Words; {В программе используется функция findMaxLenWord} var inFileName : string; {имя входного файла} outFileName : string; {имя выходного файла} inFile : text; {входной файл} outFile : text; {выходной файл} S : string; {строка, читаемая из файла} beg : Byte; {начало самого длинного слова} len : Byte; {длина самого длинного слова} begin {Ввод имен входного и выходного файлов} write('Введите имя входного текстового файла : '); readln(inFileName); write('Введите имя выходного текстового файла : '); readln(outFileName); {Открытие входного файла на чтение} assign(inFile, inFileName); reset(inFile); {Открытие выходного файла на запись} assign(outFile, outFileName); rewrite(outFile); {пока не кончится входной файл} while not eof(inFile) do begin {читаем из входного файла строку} readln(inFile, s); {если в строке найдено хоть одно слово} if findMaxLenWord(s, beg, len) then begin {самое длинное слово выделяем угловыми скобками} Insert('<',S,beg); Insert('>',S,beg+len+1); end; {выводим в выходной файл измененную строку S} writeln(outFile, s); end; {Закрываем входной и выходной файлы} close(inFile); close(outFile); end. Как мы видим, в этой программе используется наш модуль Words. За счет этого текст программы достаточно краток. Теперь решим вторую задачу. В ней кроме всего прочего нужно слово переписать большими буквами. Сделаем для этого функцию и поместим эту функцию и вспомогательные функции в модуль Chars: {Модуль Chars. Содержит функции конверсии символов и строк.} unit chars; interface {превратить маленькие буквы в большие} function ToUpper(ch: char): char; {превратить большие буквы в маленькие} function ToLower(ch: char): char; {превратить маленькие буквы в большие для строки S} function StringToUpper(const S: string): string; implementation {превратить маленькие буквы в большие} function ToUpper(ch:char):char; begin {Для понимания алгоритма загляни в таблицу ASCII кодов} {Общая схема такая : ord(ch)-ord('x') - вычисление "расстояния" до опорного символа + ord('X') - вычисленное расстояние прибавляется к новому опорному символу chr(...) - превращения порядкового номера символа в символ } if ch in ['а'..'п'] then ToUpper := chr(ord(ch) - ord('а') + ord('А')) else if ch in ['р'..'я'] then ToUpper := chr(ord(ch) - ord('р') + ord('Р')) else if ch = 'ё' then ToUpper := 'Ё' else if ch in ['a'..'z'] then ToUpper := chr(ord(ch) - ord('a') + ord('A')) else ToUpper := ch; end; {превратить большие буквы в маленькие} function ToLower(ch:char):char; begin if ch in ['А'..'П'] then ToLower := chr(ord(ch) - ord('А') + ord('а')) else if ch in ['Р'..'Я'] then ToLower := chr(ord(ch) - ord('Р') + ord('р')) else if ch = 'Ё' then ToLower := 'ё' else if ch in ['A'..'Z'] then ToLower := chr(ord(ch) - ord('A') + ord('a')) else ToLower := ch; end; {превратить маленькие буквы в большие для строки S} function StringToUpper(const S:string):string; var i : Byte; resString: string; begin {результат заносится в строку resString} resString:=''; {по очереди все символы исходной строки S превращаются в большие} for i := 1 to length(s) do resString := resString + ToUpper(s[i]); {получившаяся строка возвращается} StringToUpper := resString; end; begin end. И наконец, текст программы – решение второй задачи: { Задача 2. Дан исходный файл с текстом на русском языке. Вывести на экран содержимое исходного файла постранично. При выводе на экран выделить цветом все слова, в которых чередуются гласные/согласные. Переписать содержимое исходного файла в выходной файл, выделяя БОЛЬШИМИ буквами все выделенные при выводе на экран слова. Примечание: Имя входного файла вводится с клавиатуры. Имя выходного файла отличается от имени исходного файла только расширением. } uses Words, {В программе используется функция FindNextWord и IsChereda} Chars, {используется StringToUpper} Crt; {используется ReadKey и др.} {Процедура PrintFile. Вывод содержимого файла FileName на экран постранично. Одна страница - 20 строк. При выводе на экран выделяются цветом все слова, в которых чередуются гласные/согласные.} procedure PrintFile(const FileName:string); var InFile : text; {входной файл} S : string; {строка, читаемая из файла} NumLines : LongInt; {номер строки, читаемой из файла} NumPages : LongInt; {номер страницы, выводимой на экран} i : Integer; {счетчик} beg, len : byte; {начало и длина слова} w : string; {слово} begin {Открытие входного файла на чтение} assign(inFile, FileName); reset(inFile); {Инициализация количества прочитанных строк и выведенных страниц} NumLines := 0; NumPages := 0; {пока не кончится входной файл} while not eof(inFile) do begin readln(inFile, s); {читаем из входного файла строку} inc(NumLines); {увеличиваем номер прочитанной строки} write(NumLines:3,'> '); {выводим номер строки на экран} {ищем по очереди все слова} i:=1; while FindNextWord(s,i,beg,len) do begin {серым цветом выводим все разделители перед очередным словом} textcolor(lightgray); write( copy(s, i, beg-i) ); {в W заносим найденное слово} w:=copy(s,beg,len); {если в слове W чередуются гласные и согласные} {тогда устанавливаем ДРУГОЙ цвет вывода} if IsChereda(w) then textcolor(yellow); {выводим слово} write(w); {продолжать поиск слов...} i:=beg+len; end; {разделители за последим словом выводим серым цветом} textcolor(lightgray); writeln(copy(s,i,length(s)-i+1)); {Если очередные 20 строк выведены - страница} if NumLines mod 20 = 0 then begin {увеличиваем номер страницы} inc(NumPages); {выводим номер страницы } write('======================= '); write('Страница ', NumPages:4); write(' ======================='); {выводим пустые строки} writeln; writeln; writeln; writeln; {ждем нажатия какой-нибудь клавиши} ReadKey; end; end; {после последней страницы выводим сообщение об окончании вывода} inc(NumPages); write('======================='); write('Страница ', NumPages:4); writeln('======================='); writeln('Весь текст выведен!'); ReadKey; {Закрываем входной файл} close(inFile); end; { Функция CreateOutFileName. Создает и возвращает имя выходного файла путем замены расширения в имени входного файла InFileName.} function CreateOutFileName( const InFileName : string ) : string; var OutFileName: string; {имя выходного файла} PosPoint: Byte; {позиция точки в имени входного файла} begin {ищем точку в имени входного файла} PosPoint := Pos('.', InFileName); if PosPoint = 0 then {точки в имени нет} OutFileName := InFileName + '.' else {точка в имени файла есть} OutFileName := Copy(InFileName, 1, PosPoint); {считаем, что входной файл имеет расширение отличное от OUT!!!} OutFileName := OutFileName + 'out'; CreateOutFileName := OutFileName; end; {Процедура CopyText. Копирует содержимое исходного файла InFileName в выходной файл, выделяя БОЛЬШИМИ буквами все слова, в которых чередуются гласные/согласные.} procedure CopyText(const InFileName: string); var InFile : text; {входной файл} OutFileName : string; {имя выходного файла} OutFile : Text; {выходной файл} S : string; {строка, читаемая из файла} i : Integer; {счетчик} beg, len : byte; {начало и длина слова} w : string; {слово} begin {создаем имя выходного файла на основании имени входного} OutFileName := CreateOutFileName(InFileName); {Открытие входного файла на чтение} assign(inFile, InFileName); reset(inFile); {Открытие выходного файла на запись} assign(outFile, OutFileName); rewrite(outFile); {пока не кончится входной файл} while not eof(inFile) do begin readln(inFile, s); {читаем из входного файла строку} {ищем в строке по очереди все слова} i:=1; while FindNextWord(s,i,beg,len) do begin {выводим все разделители перед очередным словом} write(outFile, copy(s,i,beg-i)); {в W заносим найденное слово} w:=copy(s,beg,len); {если в слове W чередуются гласные и согласные} {тогда делаем все буквы этого слова большими} if IsChereda(w) then W:=StringToUpper(W); {выводим слово} write(outFile, w); {продолжать поиск слов...} i:=beg+len; end; {выводим разделители за последним словом} writeln(outFile, copy(s,i,length(s)-i+1)); end; {закрытие файлов} close(inFile); close(outFile); end; var inFileName : string; {имя входного файла} begin {Ввод имени входного файла} write('Введите имя входного текстового файла : '); readln(inFileName); {Печатаем входной файл (на экране, постранично)} PrintFile(inFileName); {Копируем входной файл в выходной} CopyText(InFileName); end. ЗАМЕЧАНИЯ: Использование модулей позволяет улучшить качество программы, уменьшить затраты на ее разработку лишь в том случае, когда соблюдаются следующие принципы: 1. Пользователь модуля имеет полную информацию об использовании подпрограмм модуля. И при этом он не имеет никакой информации о способе реализации подпрограмм. 2. Модули должны быть, как можно меньше зависимы друг от друга (в идеале модули должны быть полностью независимы друг от друга) Первый принцип реализуется за счет: - разбиения констант, типов, переменных и подпрограмм модуля на те, которые необходимо открыть пользователям (их надо разместить в интерфейсной части), и на те, которые должны быть закрыты от пользователя (их надо разместить в исполнительной части); - наиподробнейшего комментирования интерфейсной части модуля - использование смысловых имен для констант, типов, переменных и подпрограмм. Второй принцип реализуется за счет отказа от использования глобальных переменных – все данные, обрабатываемые в подпрограмме, должны передаваться через параметры. |