Тема №7. Файловый ввод-вывод

Тема №6. Строковый тип данных (string).

Задание.Введите с клавиатуры предложение в переменную типа строка символов (string), выделите из предложения отдельные слова в массив слов, проведите преобразование предложения и слов в соответствии с индивидуальным заданием, распечатайте результат обработки на экране монитора. Длина вводимого предложения не более 80 символов. При выполнении задания необходимо во всех случаях, где это можно, использовать процедуры и функции для работы со строками. Все задания подразумевают необходимость применения не менее трех разных библиотечных процедур или функций. Программа не должна содержать глобальных переменных. Каждая из процедур и функций должна получать исходные данные в виде параметров, результат также должен быть параметром или возвращаться функцией.

Пример выполнения задания

Примерное задание: во введенном с клавиатуры предложении, расположите слова в обратной последовательности, не меняя расположение разделительных знаков.

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

1. Подпрограмму (функцию или процедуру) для ввода предложения.

2. Подпрограмму разбиения предложения на массив слов и массив разделительных знаков между словами. Каждый из массивов будет представлять собой массив строк.

3. Подпрограмму преобразования слов (если есть по заданию).

4. Подпрограмму сборки предложения из массива слов и массива разделительных знаков.

5. Подпрограмму распечатки исходного предложения и результата.

Уже сейчас можно провести запись программы на языке Паскаль. Но каждая из подпрограмм будет записана в фиктивном виде, то есть у нее будут все параметры, но она не будет выполнять никаких действий (исключением будет подпрограмма ввода предложения, которая будет возвращать тестовое значение). Основная часть программы будет определена полностью.

// Подпрограмма ввода предложения

function Input : string;

begin Input := 'Верите ли вы, что задача решена?'; end;

// Подпрограмма выделения слов из предложения в массив слов

procedure Select ( S : string; R : string; var M : array of string; var n : shortint );

begin end;

// Подпрограмма сборки результирующего предложения

function Sborka ( Slova, Znaki : array of string; Ns,Nr : shortint ) : string;

begin Sborka := ''; end;

// Подпрограмма распечатки результата

procedure PrintResult ( Predl, Res : string );

begin end;

// Основная часть программы

const N = 20; // Максимальное количество букв в каждом слове предложения

var

Predl, // Исходное предложение

Razd, // Строка разделительных знаков

Alph, // Строка букв алфавита

Res : // Результат работы программы

string;

Slova : array [1..N] of string; // Массив слов в предложении

Znaki : array [1..N] of string; // Массив строк разделительных знаков между словами

Ns, Nr : shortint; // Количество слов и количество строк разделительных знаков

begin

// Исходные данные

Predl := Input;

Razd := ' ,./;!?';

Alph := 'ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ'+

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

// Выделение массивов слов и строк разделительных знаков

Select ( Predl, Razd, Slova, Ns );

Select ( Predl, Alph, Znaki, Nr );

// Сборка результирующего предложения

Res := Sborka ( Slova, Znaki, Ns, Nr );

// Распечатка результата

PrintResult ( Predl, Res );

Readln;

end.

Простые подпрограммы предлагается рассмотреть самостоятельно по приведенному ниже полному тексту программы. Для подробного рассмотрения составляют интерес только самые сложные части.

Наиболее сложной будет подпрограмма выделения (Select) из исходного предложения массива слов или массива строк разделительных знаков (алгоритм для них один и тот же). Она будет принимать в качестве исходных данных предложение и строку из разделительных знаков. Результат работы подпрограммы – массив слов и массив строк разделительных знаков между ними, а также количество элементов в каждом из этих массивов.

Для разбиения исходного предложения на массив слов можно предложить множество алгоритмов. Один из вариантов приведен здесь (попутно заметим, что все действия будут проводиться над копией исходного предложения):

1. Установить счетчик слов равным -1, то есть пока не выделено ни одного слова.

2. Если строка содержит символы, то перейти на пункт 3, иначе завершить работу подпрограммы.

3. Увеличить счетчик слов.

4. Выделить слово из предложения подпрограммой выделения одного слова (SelectWord) и записать его в массив слов. После выделения слова оно будет удалено из предложения.

5. Повторить алгоритм, начиная с пункта 2.

В данном алгоритме не определена подпрограмма выделения одного слова (SelectWord). Для начала ее можно сделать фиктивной, как это делалось со всеми подпрограммами основной части программы, и реализовать алгоритм подпрограммы Select. Но, логичнее, завершить проект программы, разработав алгоритм подпрограммы выделения одного слова, который может быть следующим:

1. Удалить разделительные знаки в начале строки.

2. Найти индекс первого разделительного знака.

3. Копировать слово из предложения от начала строки до разделительного знака (сам знак не копируется).

Каждое из указанных действий будет реализовано по своему алгоритму. Так функция удаления разделительных знаков принимает в качестве параметров строку исходного предложения и строку разделительных знаков и делает два действия:

1. Заполняет множество символами-разделителями (в примере программы ниже по тексту это действие выполняется отдельной функцией).

2. До тех пор, пока первый символ строки входит во множество символов-разделителей и строка не пустая, удаляет первый символ.

Функция поиска первого разделительного знака в предложении принимает в качестве параметров строку исходного предложения и строку разделительных знаков и реализует следующий алгоритм:

1. Вначале считаем, что индекс первого разделительного знака не найден, то есть значение индекса, записанное в переменной n, равно 0.

2. Для всех символов строки разделительных знаков выполняем пункты, начиная с 3-го.

3. Ищем номер позиции разделительного знака в строке предложения.

4. Если знак найден (его позиция не равна 0), то, если n=0 или номер найденной позиции меньше, чем значение переменной n, то сохраняем в ней новое значение.

Функция копирования слова из исходного предложения до позиции разделительного знака (CopyWord) принимает в качестве параметров исходное предложение и номер позиции разделительного знака. Если номер позиции не равен 0, то копируется часть предложения от 1-го символа, до знака (сам знак не копируется). В противном случае копируется весь остаток предложения. Следующим действием удаляется скопированная часть исходного предложения.

По данному проекту выполнена следующая программа:

type

TCharSet = set of char;

// Подпрограмма ввода предложения

function Input : string;

var s : string;

begin

writeln ( 'Введите предложение' );

readln ( s );

Input := s;

end;

// Заполняет множество символами строки

function FillCharSet ( s : string ) : TCharSet;

var i : byte; Rset : TCharSet;

begin

Rset := [];

for i := 1 to length(s) do Rset := Rset + [s[i]];

FillCharSet := Rset;

end;

// Удаляет разделительные знаки в начале строки

procedure DelRazd ( var S : string; R : string );

var i : byte; Rset : TCharSet;

begin

// Заполнить множество символов-разделителей

Rset := FillCharSet(R);

// Удалить разделительные знаки в начале строки

while (S<>'')and(S[1] in Rset) do Delete(S,1,1);

end;

// Ищет первый разделительный знак

function FindIndex ( S, R : string ) : byte;

var i,k,n : byte;

begin

n := 0;

for i := 1 to length(R) do

begin

k := Pos(R[i],S);

if k > 0 then

begin

if (n = 0) or (k < n)

then n := k;

end;

end;

FindIndex := n;

end;

// Копировать слово

function CopyWord ( var S : string; n : byte ) : string;

var Sl : string;

begin

if n > 0

then Sl := Copy(S,1,n-1)

else Sl := S;

// Удалить слово

if n > 0

then Delete(S,1,n)

else S := '';

CopyWord := Sl;

end;

//Выделяет слово до первого разделительного знака

function SelectWord( var S : string; R : string ) : string;

var i,n : byte; Rset : TCharSet;

begin

// Удалить разделительные знаки в начале строки

DelRazd(S,R);

// Найти индекс первого разделительного знака

n := FindIndex(S,R);

// Копировать слово

SelectWord := CopyWord(S,n);

end;

// Выделяет все слова из предложения в массив

procedure Select(S : string; R : string;

var M : array of string; var n : shortint);

begin

n := -1;

while S <> '' do

begin

inc(n);

M[n] := SelectWord(S,R);

end;

end;

function Sborka(Slova, Znaki : array of string;

Ns,Nr : shortint) : string;

var Rez : string; s,z : shortint;

begin

Rez := '';

s := Ns; z := -1;

while ( s >= 0 ) or ( z <= Nr ) do

begin

if s >= 0 then

begin

Rez := Rez + Slova[s];

dec(s);

end;

if z <= Nr then

begin

inc(z);

Rez := Rez + Znaki[z];

end;

end;

Sborka := Rez;

end;

procedure PrintResult( Predl, Res : string);

begin

writeln('Исходное предложение - '#13, Predl);

writeln('Результат - '#13, Res);

end;

const N = 20;

var

Predl, Razd, Alph, Res : string;

Slova : array [1..N] of string;

Znaki : array [1..N] of string;

Ns, Nr : shortint;

begin

// Исходные данные

Predl := Input;

Razd := ' ,./;!?';

Alph := 'ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ'+

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

// Выделение массивов слов и разделительных знаков

Select ( Predl, Razd, Slova, Ns );

Select ( Predl, Alph, Znaki, Nr );

// Сборка результирующего предложения

Res := Sborka ( Slova, Znaki, Ns, Nr );

// Распечатка результата

PrintResult ( Predl, Res );

Readln;

end.

Индивидуальные задания

1. В двух предложениях найти и удалить совпадающие слова.

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

3. Расположить в предложении слова в порядке возрастания длины слова.

4. В предложении найти и удалить все однокоренные слова. Корень слова также задан с клавиатуры отдельной строкой.

5. Удалить в предложении все повторные вхождения слов и распечатать получившееся предложение.

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

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

8. Расположить слова, входящие в предложение, в алфавитном порядке.

9. Удалить из предложения все предлоги. Список предлогов задан во втором предложении.

10. Все буквы каждого слова в предложении записать в обратном порядке и распечатать получившееся предложение.

11. Во всех словах предложения удалить гласные буквы и распечатать получившееся предложение.

12. Дополнить каждое слово предложения знаком нижнего подчеркивания так, чтобы его длина была равна длине самого длинного слова.

13. Проверить на совпадение два предложения. Количеством пробелов между словами пренебрегать. Знаки препинания - учитывать.

14. Удалить в предложении все слова, имеющие символы цифр и распечатать получившееся предложение.

15. Предложение состоит из записи символов цифр, например:

123434 53423 2344 6564.

Расположить слова в порядке возрастания чисел.

Тема №7. Файловый ввод-вывод.

Задание. Измените программы по индивидуальным заданиям по темам №5 и №6, так чтобы из файла считывалось несколько блоков данных (массивов или предложений), каждое из которых обрабатывалось по разработанному ранее алгоритму.

При рассмотрении данной темы подразумевается освоение студентами следующих вопросов:

1. Перенаправление потоков стандартного ввода и стандартного вывода средствами операционной системы.

2. Текстовые файлы в языке Паскаль.

3. Параметры, передаваемые в программу из командной строки

(ParamCnt, ParamStr).

4. Типизированные файлы в языке Паскаль.

5. Нетипизированные файлы языка Паскаль.

1. Для перенаправления стандартного ввода и стандартного вывода необходимо убедится, что программа не использует модуль управления монитором и клавиатурой (CRT), а также, что в результате компиляции программы на внешнем носителе создается исполняемый файл (для операционной системы Windows – это файл с расширением «exe», для операционной системы Unix – без расширения). Например, если исходный текст программы был в файле 6.pas, то после компиляции будет образован файл 6.exe.

Перед выполнением программы необходимо подготовить в текстовом редакторе файл с теми исходными данными, которые обычно вводились с клавиатуры. Например, это будет файл 6.txt. Запуск программы на исполнение производится из командной строки, причем можно заменить один или оба потока стандартного направления передачи данных. Вид командной строки:

имя_программы <файл_входных_данных >файл_выходных_данных

Командная строка для приведенных в качестве примера файлов запишется в виде:

6.exe <6.txt >6_1.txt

Задание: продемонстрируйте перенаправление ввода-вывода в командной строке.

2. При работе с текстовым файлом необходимо сделать изменения только в двух подпрограммах: подпрограмме ввода предложения (Input) и распечатки результата (PrintResult). А в основной части программы введите цикл для считывания и обработки нескольких предложений.

В качестве примера рассмотрите программу копирования одного текстового файла в другой:

var

s : string; // строка текста

Fi, Fo : Text; // файловые переменные

begin

Assign ( Fi, '7.pas' ); // установить соответствие переменной Fi файлу 7.pas

Reset ( Fi ); // открыть файл для чтения

Assign ( Fo, '7.txt' ); // установить соответствие переменной Fo файлу 7.txt

Rewrite ( Fo ); // открыть файл для записи

while not ( eof(Fi) ) do // цикл повторяет пока не закочится файл Fi

begin

Readln ( Fi, s); // читать строку из файла

Writeln ( Fo, s ); // записать строку в файл

end;

Close ( Fo ); // закрыть файл Fo

Close ( Fi ); // закрыть файл Fi

end.

3. Для передачи в командной строке параметров в программу используются две функции: ParamCount, возвращающая количество параметров (строк в командной строке), и ParamStr(Num), принимающая в качестве параметра Num номер параметра командной строки и возвращающая строку под этим номером. Используя эти функции можно, например, сделать такую программу, которая сможет не только копировать файлы, но и добавлять к одному текстовому файлу другой. Какие необходимо выполнить действия, а также имена файлов будут задаваться в командной строке. Основная часть программы будет содержать анализ командной строки, который в приведенном ниже примере состоит только лишь в контроле количества передаваемых параметров и выборе в соответствии с этим имен файлов, указанных в командной строке.

Program p7_1;

// Подпрограмма копирования файла

procedure CopyFile ( var Fi, Fo : Text );

var

s : string;

begin

while not ( eof(Fi) ) do

begin

Readln ( Fi, s); Writeln ( Fo, s );

end;

Close ( Fo ); Close ( Fi );

end;

// Подпрограмма открытия файла

procedure OpenFile ( var F : Text; Name : string; Mode : char );

begin

Assign (F, Name);

case Mode of

'r' : Reset ( F );

'w' : Rewrite ( F );

'a' : Append ( F );

end;

end;

var

Fi, Fo : Text; Name1, Name2 : string;

begin

// Анализ командной строки

if ParamCount = 2 then

begin

Name1 := ParamStr ( 1 );

Name2 := ParamStr ( 2 );

OpenFile ( Fo, Name2, 'w');

end;

if ParamCount = 3 then

begin

Name1 := ParamStr ( 2 );

Name2 := ParamStr ( 3 );

OpenFile ( Fo, Name2, 'a');

end;

OpenFile ( Fi, Name1, 'r');

CopyFile ( Fi, Fo );

end.

Если после компиляции данной программы исполняемый файл был под именем p7_1.exe, то командная строка может быть для «простого» копирования одного файла в другой (первая строка) или для добавления текста первого файла в конец второго файла. Еще раз отметим, что приведенный пример программы намеренно не содержит подробного анализа командной строки, а показывает вариант использования параметров командной строки.

p7_1.exe file1 file2

p7_1.exe append file1 file2

Задание: в программу по теме №6 добавьте обработку командной строки.

4. Для типизированных и нетипизированных файлов приведите видоизмененные программы по теме №5, считывающие исходные данные из файла и записывающие в файл. Контроль правильности записи в файл проведите по размеру результирующего файла.

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

1. Назначение и формат использования процедуры assign.

2. Назначение и формат использования процедуры reset.

3. Назначение и формат использования процедуры rewrite.

4. Назначение и формат использования процедуры append.

5. Назначение и формат использования процедуры close.

6. Назначение и формат использования функции eof.