Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Слева от оператора in может быть указано выражение любого перечислимого типа T, а справа -- набор с типом, совместимым с типом T.

Любые совместимые по типам данных множества можно объединять операций "+", вычитать операцией "-" и пересекать операцией "*". При этом результаты операций с множествами соответствуют правилам логики множеств:

·  порядковое значение c находится в множестве A+B только в том случае, если c находится в A или B;

·  порядковое значение c находится в множестве A-B только в том случае, если c находится в A и не находится в B;

·  порядковое значение c находится в множестве A*B только в том случае, если c находится и в A, и в B.

Если самое маленькое порядковое значение, являющееся элементом результата операции с множеством обозначить A, а самое большое -- за B, то тип результата становится равным A..B.

В следующем примере множество латинских букв получается операцией сложения подмножеств латинских прописных и латинских строчных букв.

type Latin = set of 'A'..'z';

const SmallLatin : Latin = ['A'..'Z'];

BigLatin : Latin = ['a'..'z'];

var LatinLetters : Latin;

c:char;

begin

LatinLetters := BigLatin + smallLatin;

repeat

write ('Введите символ или ',

'пробел для выхода:');

reset (input);

readln (c);

if c in LatinLetters then

writeln (c,' - латинская буква');

until c=' ';

end.

Как в примере выше, полезно бывает создавать из множеств подмножества при указании конструктора, содержащего выражения диапазонов в квадратных скобках [ ... ]:

НЕ нашли? Не то? Что вы ищете?

type Digits = set of 0..9; {Множество цифр}

Letters = set of 'A'..'Z';

{Множество латинских букв}

const EvenDigits : Digits =

[0, 2, 4, 6, 8];

{Подмножество четных цифр}

Vowels : Letters =

['A', 'E', 'I', 'O', 'U', 'y'];

{Подмножество гласных букв}

HexDigits : set of '0'..'z' =

['0'..'9', 'A'..'F', 'a'..'f'];

{Символы 16-ричных чисел}

type shortWeekDays =

(Pn, Vt, sr, ch, Pt, sb, Vs);

{Перечислимый тип "дни недели"}

const Holidays : set of shortWeekDays

= [sb, Vs];

{Подмножество "Выходные" дней недели}

var wd:shortWeekDays;

{Переменная типа "Дни недели"}

i:integer;

begin

wd:=Pn;

for i:=1 to 7 do begin

if wd in Holidays then

writeln (ord(wd), ' - Выходной день')

else writeln (ord(wd), ' - Будний день');

Inc(wd);

end;

end.

Тип данных shortWeekDays в этом примере является перечислимым типом. Перечислимые типы определяют упорядоченные наборы значений, перечисляя идентификаторы, которые обозначают эти значения. Их порядок следует из последовательности, в которой они были перечислены. Оператор перечисления имеет общий вид

type имя = (идентификатор,

идентификатор,..., идентификатор);

Возможные значения перечисления, заданные оператором type, должны быть идентификаторами Паскаля, поэтому назвать дни недели по-русски в последнем описании type было бы невозможно.

Идентификаторы, указанные в определении типа, становятся константами перечислимого типа, первая константа имеет порядковый номер 0, вторая -- номер 1, и так далее:

type suit = (Club, Diamond, Heart, Spade);

При этом объявлении Heart является константой типа suit. Стандартная функция ord возвращает порядковый номер перечислимой константы, в нашем примере

ord(Club) = 0

ord(Diamond) = 1

ord(Heart) = 2

Как показано в листинге, переменным перечислимого типа можно присваивать константы, входящие в описание типа и увеличивать их значения как любые порядковые числа оператором Inc(wd), но эти значения нельзя читать или записывать "напрямую" операторами семейства read/write. В качестве альтернативы их можно приводить к целочисленным значениям стандартной функцией ord, при этом всегда первая константа списка имеет значение 0 (в нашем случае -- константа Pn). Оператор in в листинге позволяет проверить, попадает ли величина в подмножество, созданное для элементов исходного типа множества. Таким образом, основное назначение множеств и перечислимых типов -- удобная для человека запись выражений с "понятными" названиями констант вместо чисел. С точки зрения компилятора данные типа множества и перечисления являются целочисленными величинами.

Для ограничения диапазона исходных данных можно также непосредственно объявить тип-диапазон:

type Hour=0..23; minute=0..59;

Здесь объявлены переменные типов "час" и "минута", для переменных этих типов будут проверяться ограничения на попадание в указанные при описании диапазоны значений. Если переменной типа-диапазона присвоено недопустимое значение, программа отреагирует на это сообщением “Constant out of range”.

20. Обработка символьных и строковых данных

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

20.1. Работа с символами

Для работы с отдельными символами описываются переменные типа char:

var ch:char;

{ ... }

ch:='y';

Одна переменная типа char хранит информацию об одном коде ASCII-символа. Это можно использовать, например, для отслеживания действий пользователя по нажатию клавиш. Приведем пример одного из вариантов такой программы:

var ch:char;

begin

repeat

writeln;

write ('Продолжить? (Y/N)');

readln (ch);

if (ch='Y') or (ch='y') then begin

{Здесь программируется

нужный вычислительный процесс}

end

else if (ch='N') or (ch='n') then halt

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

else writeln ('Нажмите Y или N');

until false;

end.

Для работы с кодами символов существуют 2 основных функции:

function ord(x):char;

— возвращает ASCII-код символа x

function chr(x : byte):char;

— возвращает символ с указанным ASCII-кодом x.

Применим последнюю функцию для того, чтобы узнать, как выглядит таблица символов кодировки DOS, используемой Паскалем:

var i, j:integer;

begin

writeln;

write ('ASCII-коды [32..255]');

for i:=2 to 15 do begin

writeln;

write (i*16:3,' ');

for j:=0 to 16 do write(chr(i*16+j),' ');

end;

writeln;

write ('ENTER для выхода...');

readln;

end.

Здесь печатаются только символы с кодами от 32 до 255 включительно, т. к. первые 32 символа с кодами от 0 донепечатаемые (например, табуляция, перевод строки).

Для определения того, попадает ли код символа в некоторый диапазон значений, удобно использовать оператор in, как это делает следующий фрагмент программы:

write ('Введите символ: '); readln (ch);

if ch in ['A'..'Z'] then

write ('Большая латинская;')

else if ch in ['a'..'z'] then

write ('Малая латинская;')

else if (ch in ['А'..'Я']) then

write ('Большая русская;')

else if (ch in ['а'..'п']) or

(ch in ['р'..'я']) then

write ('Малая русская;')

else if ch in ['0'..'9'] then

write ('Цифра;')

else write

('Это не алфавитно-цифровой символ;');

write (' Код Вашего символа= ',ord(ch));

Работая с алфавитными символами, приходится отдельно учитывать ввод строчных и прописных букв. Удобнее сначала преобразовать все символы к прописным с помощью функции upcase:

var ch:char; i:integer;

begin

repeat

for i:=1 to random(72) do write ('*');

writeln;

write ('Продолжить? (Y/N)');

readln (ch);

until upcase(ch)='N';

end.

К сожалению, эта функция бесполезна при работе с символами русского и других национальных алфавитов, для ее замены напишем и протестируем собственную подпрограмму c названием upcase_ru:

procedure upcase_ru (var s:string);

var i, l,c:integer;

begin

l:=length(s);

for i:=1 to l do begin

c:=ord(s[i]);

if (c>=ord('а')) and (c<=ord('п'))

then c:=c-32

else if (c>=ord('р')) and (c<=ord('я'))

then c:=c-80;

s[i]:=Upcase(chr(c));

end;

end;

var s:string;

begin

writeln ('Введите строку текста:');

readln (s);

upcase_ru (s);

writeln ('Преобразованная строка:');

writeln (s);

end.

Программа учитывает, что в кодировке DOS не все символы кириллицы закодированы идущими подряд числами (см. Приложение 1).

Кроме того, в программе уже применяется массив символов, которому в Паскале соответствует тип данных string (строка). Мы упоминали этот тип данных, но еще не работали с ним. Как раз строкам посвящен п. 20.2.

20.2. Работа со строками

Строка -- это массив символов, т. е., элементов типа char. Нумерация символов в строке всегда выполняется с единицы. В Паскале строке соответствует тип данных string. Строка описывается оператором следующего вида:

var Имя_строки : string [длина];

Если положительная целочисленная величина "длина" не указана, выделяется память под строку длиной до 255 символов. Приведем примеры описания строк:

var s1:string;

s2:string[20];

s3:array [1..20] of string;

Здесь s1 -- строка с длиной по умолчанию, s2 -- строка из 20 символов, s3 -- массив из 20 строк, каждая из которых может занимать до 256 байт памяти (дополнительный байт нужен для хранения длины строки).

Со строками можно выполнять операцию присваивания. Покажем это на примере описанных выше строк.

s1:='';

— строке s1 присвоено значение строковой константы.

s1[3]:=’В’;

— отдельному символу строки s1 присвоили символьную константу.

s2:='2009';

— строке s2 присвоили строку, являющуюся записью целого числа. Позднее такая строка может быть преобразована в число стандартной процедурой Val.

s3[1]:='Информатика';

s3[2]:='';

— s3 является строковым массивом. Его первому элементу присвоена строковая константа, второму -- пустая строка.

Для ввода строк с клавиатуры следует применять оператор readln, т. к. ввод строки должен завершиться нажатием клавиши Enter:

writeln ('Введите имя пользователя:');

readln (s1);

Для вывода строк на экран или принтер можно использовать как оператор write, так и writeln:

s2:='SUMMA';

write (s2) ;

— на экран будет выведена строка "SUMMA".

writeln ('Сумма':10);

— будет выведена строка "_____Сумма" (5 пробелов перед словом) и курсор переведен на следующую строку экрана.

Оператор сложения "+" переопределен для строк таким образом, что выполняет их сцепление (конкатенацию):

s1:='2009' + ' год'; s2:='н. э.';

s3[3]:=s1+' '+s2;

После этих действий значение строки s1 будет равно ''2009_год", а строка s3[3] -- ''2009_год_н. э.".

Если при сложении строк превышена максимальная длина результирующей строки, лишние символы отсекаются. Для сцепления строк можно также использовать стандартную функцию concat.

Операция отношения "=" позволяет посимвольно сравнить строки. При этом действуют следующие правила:

·  строки считаются равными только при одинаковом наборе символов и одинаковой длине;

·  иначе происходит поэлементное сравнение символов по их кодам. При этом, согласно таблице ASCII-кодов (см. Приложение 1) старшинство отдельных символов следующее: '0' < '1' < ... < '9' < 'A' < ... < 'Z' < 'a' < ... < 'z' < символы кириллицы.

Остальные операции отношения также применимы к строкам.

В ядро Паскаля включен ряд стандартных подпрограмм для обработки строковых данных. Опишем их прототипы, то есть, перечислим заголовки подпрограмм с указанием типа и количества формальных параметров.

function Length (s:string):integer;

— определяет и возвращает длину строки s в символах;

function copy

(s:string; N, L:integer): string;

— возвращает часть строки s длиной L символов, начиная с позиции N;

procedure Insert

(s0:string; var s: string; N: integer);

— в строку s вставляет строку s0, начиная с позиции N;

procedure Delete

(var s:string; N, L:integer);

— в строке s удаляет L символов, начиная с позиции N;

function Pos (s0, s:string): integer;

— возвращает позицию, начиная с которой строка s0 содержится в строке s или значение 0, если s0 не содержится в s;

procedure str (x: числовой; var s:string);

— преобразует число x в строку s, параметр x может иметь любой числовой тип;

procedure Val (s:string;

var x: числовой; var error:integer);

— преобразует строку s в число x. Параметр x может иметь любой числовой тип. Параметр-переменная error служит для контроля правильности преобразования. Если преобразовать удалось, то error=0, иначе error будет равен номеру первого непреобразуемого символа строки s.

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

1. Разобрать предложение на слова и вывести каждое слово на новой строке экрана.

Алгоритм работы этой программы очень прост -- до тех пор, пока в исходной строке предложения s есть хотя бы один пробел, вся часть строки до пробела копируется в строковую переменную w (слово). Если пробелов уже нет (или не было изначально), то вся строка -- это одно слово. После обработки очередного слова (в нашем случае -- это вывод его на новую строку экрана оператором writeln) обработанная часть строки вместе с пробелом удаляются из s -- чтобы следующий шаг цикла не нашел то же самое слово.

var s, w:string; {предложение и слово}

p:integer; {позиция пробела}

begin

writeln ('Введите текст');

readln (s);

repeat

p:=pos (' ',s);

if p>0 then w:=copy (s,1,p-1)

else w:=s;

writeln (w);

delete (s,1,p);

until p=0;

end.

Приведенная программа имеет ряд недостатков. Самый заметный из них -- не учтены дополнительные пробелы между словами, которые будут восприниматься программой как лишние "пустые слова". Избавимся от них с помощью следующего примера.

2. Удалить лишние пробелы между словами.

Алгоритм решения задачи также несложен. Пока в строке есть два подряд идущих пробела, следует удалять один из них. После этого остается проверить, нет ли в начале и конце строки по одному лишнему пробелу. Приведем только основную часть программы:

repeat

p:=pos (' ',s);

if p>0 then delete (s, p,1);

until p=0;

if s[1]=' ' then delete (s,1,1);

if s[length(s)]=' ' then

delete (s, length(s),1);

writeln (s);

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

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

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

var s, word:string;

c, c0:char;

i, l,start:integer;

inword:boolean;

begin

writeln ('Enter string:');

reset (input); readln (s);

s:=' '+s+' ';

l:=Length (s);

inword:=false;

for i:=2 to l do begin

c0:=s[i-1];

c:=s[i];

if (c0=' ') and (c<>' ') then begin

inword:=true;

start:=i;

end;

if c=' ' then begin

if inword=true then begin

word:=copy (s, start, i-start);

writeln ('''',word,''' is word');

end;

inword:=false;

end;

end;

end.

По сути дела, у нашей программы всего 2 состояния -- внутри слова и вне его. Переключением состояний управляет флаг inword. Номер символа, с которого начинается очередное слово, запоминается в переменной start. Программа не учитывает знаки препинания и возможность разделения слов другими символами, кроме пробела. Тем не менее, избавившись еще и от функции copy, совершающей лишний проход по части строки (например, сразу же накапливая слово word по мере сканирования), можно было бы получить действительно эффективный алгоритм. Как обычно, платой за эффективность алгоритма является сложность программы.

4. Подсчитать количество пробелов в строке.

Это пример реализует работу со строкой как с массивом символов.

var s:string; k, i:integer;

begin

writeln ('text?');

readln (s);

k:=0;

for i:=1 to length (s) do

if s[i]=' ' then k:=k+1;

writeln ('k=',k);

end.

21. Текстовые файлы

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

21.1. Общие операции

Для работы с каждым файлом описывается переменная типа text:

var f:text;

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

assign (f,'имя_файла');

где f -- ранее описанная файловая переменная.

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

assign (f,'data. txt');

-- будет открываться файл с именем data. txt из текущей папки;

assign (f,'a:\my. dat');

-- будет открыт файл с именем my. dat из корневой папки дискеты.

Имя файла также может быть введено пользователем с клавиатуры:

var name:string; f:text;

begin

writeln ('Введите имя файла:');

readln (name);

assign (f, name);

.

Наконец, имя файла можно передать программе параметром командной строки (см. п. 5.3).

После связывания следует открыть файл. Каждый файл может быть открыт только в одном из трех режимов -- для чтения данных, для записи новых данных (при этом, если файл уже существовал, его прежнее содержимое будет стерто) или для добавления данных в конец файла. Если требуется сначала прочитать данные из файла, а потом переписать этот же файл, следует открыть файл для чтения, после чтения закрыть его и вновь открыть для записи. Открытие файла выполняется одним из трех операторов:

reset (f); -- открыть для чтения;

rewrite (f); -- открыть для записи;

append (f); -- открыть для добавления.

Чтение или запись данных осуществляется знакомыми нам операторами read, readln, write и writeln, но первым параметром этих стандартных процедур указывается имя файловой переменной:

var a, b,c:real; f1,f2:text;

begin

assign (f1,'read. txt');

assign (f2,'write. txt');

reset (f1);

{открыли файл read. txt для чтения,}

rewrite (f2);

{а файл write. txt для записи}

read (f1,a, b);

{Прочитали 2 числа из файла read. txt}

c:=(a+b)/2;

writeln (f2,c:6:2); {записали значение c

и перевод строки в файл write. txt}

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

close(f);

— закрыли файл, связанный с файловой переменной f.

При работе с файлами могут возникнуть ошибки, связанные как с отсутствием нужного файла на диске, так и с проблемами чтения или записи (например, мы пытаемся открыть файл для записи на защищенном диске). Поэтому операторы открытия файла и чтения или записи данных следует защищать директивой компилятора {$I-}...{$I+}, а после оператора проверять статус операции ввода-вывода с помощью стандартной функции IoResult:

var f:text; name, s:string;

begin

writeln ('Введите имя файла:');

readln (name);

assign (f, name);

{$I-}reset(f);{$I+}

if IoResult<>0 then begin

writeln ('Не могу открыть файл ',name,

' для чтения!');

writeln ('Нажмите Enter для выхода');

readln;

halt;

end;

readln(f, s);

writeln ('Первая строка файла:');

writeln (s);

close(f);

writeln ('Нажмите Enter для выхода');

readln; end.

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

При чтении из файлов зачастую объем читаемых данных неизвестен заранее. Поэтому необходима функция, умеющая определять, прочитаны ли уже все данные:

function Eof(var F : text) : boolean;

-- возвращает true, если при чтении достигнут конец файла.

function Eoln (var F : text) : boolean;

-- возвращает true, если при чтении достигнут конец строки.

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

while not eof (f) do begin

{операторы для чтения строк файла

и работы с ними}

end;

При чтении из одного файла "смешанных" строковых и числовых данных следует проверять, действительно ли в нужных строках файла содержатся числа. Для этого можно использовать стандартную процедуру val. Как правило, формат файлов, понимаемых той или иной программой, выбирается программистом, и важно предусмотреть реакцию программы на ошибочный формат исходных данных. Допустим, наша программа представляет собой простейший телефонный справочник, включающий имена абонентов и по одному телефонному номеру на каждое имя. Формат файла, хранящего данные справочника, также выберем простейшим:

Фамилия1

Номер1

Фамилия2

Номер2

и т. д., то есть, в строках 1, 3, 5, ... файла содержатся фамилии абонентов, а в строках 2, 4, 6, их номера телефонов. Примем также, что файл справочника называется phones. txt, существует и находится в той же папке, откуда запускается программа. Полный листинг программы приводится ниже.

var f:text;

name, phone, search:string;

number, strings:longint;

error:integer;

found:boolean;

begin

assign (f,'phones. txt');

reset(f);

writeln (Фамилия абонента для поиска:');

readln (search);

strings:=1; {Счетчик прочитанных строк}

found:=false; {Переключатель

"найдено"-"не найдено"}

while not eof(f) do begin

readln (f, name); {Прочитали фамилию}

readln (f, phone); {Прочитали номер}

val (phone, number, error); {Пробуем

номер-строку преобразовать в число}

if error<>0 then begin {если это

не удалось сделать - ошибка}

writeln('Ошибка - нет номера телефона!',

' Номер строки=', strings);

writeln ('Нажмите Enter для выхода');

reset (input); readln; halt;

end;

if name=search then begin

writeln ('Телефон найден:',number);

found:=true;

break;

end;

strings:=strings+1;

end;

close (f);

if found=false then

writeln ('Телефон не найден!');

writeln ('Нажмите Enter для выхода');

reset (input); readln;

end.

Этой учебной программе недостает как умения редактировать открываемый ей справочник, так и гибкости при поиске -- например, она различает как разные символы строчные и прописные буквы во вводимой фамилии.

21.2. Примеры работы с файлами

Рассмотрим несколько типовых задач на обработку текстовых файлов.

1. Чтение числовых данных из файла. Массив данных имеет фиксированную размерность, примем ее равной 5.

var f, w:text;

a: array [1..5] of integer; i:integer;

begin

assign (f,'data. txt');

reset (f);

for i:=1 to 5 do read (f, a[i]);

close (f);

assign (w, 'result. dat');

rewrite (w);

writeln (w,'Результаты:');

for i:=1 to 5 do

writeln (w, a[i]:5,sqr(a[i]):5);

close(w); end.

Файл с именем data. txt может быть, например, таким:

1 2 3

4 5

Файл результатов result. dat будет таким:

Результаты:

1 1

2 4

3 9

4 16

5 25

2. Просмотр любого текстового файла на экране. По заполнении экрана до 24 строк программа делает паузу.

var f:text; s:string; count:integer;

begin

repeat

write ('Имя файла или 0 для выхода: ');

readln (s);

if s='0' then halt;

assign (f, s);

{$I-}reset (f);{$I+}

if IoResult<>0 then begin

writeln ('Не могу открыть файл ',s);

write ('Нажмите Enter для продолжения');

readln;

continue; {повторяем цикл с начала}

end;

count:=1;

while not eof(f) do begin

readln (f, s);

writeln (s);

count:=count+1;

if count=24 then begin

count:=1;

write('Нажмите Enter для продолжения');

readln;

end;

end;

write ('Нажмите Enter для нового ввода');

readln;

close (f);

until false;

end.

Строка s здесь используется как для ввода имени файла, так и для чтения строки файла -- ведь после выполнения связывания оператором assign имя файла нам больше не нужно. Обратите внимание также на оператор continue, в данном случае он позволяет не завершать выполнение программы после неверного ввода пользователя.

3. Работаем со "смешанным" файлом данных, строки которого содержат значения разных типов.

Пусть файл data. txt имеет следующий вид:

Иванов 2

Петров 1

Сидоров 3

Попов 2

...

В каждой строке файла находится фамилия рабочего и через пробел -- номер участка, на котором он работает.

Напишем программу для вывода фамилий всех работников выбранного участка и подсчета их количества.

var f:text; s, fam:string;

u, uch, p,kol, i:integer;

begin

writeln ('Участок?'); read (uch);

assign (f,'data. txt'); reset (f);

kol:=0;

while not eof (f) do begin

readln (f, s);

p:=pos(' ',s);

if p>0 then begin

fam:=copy (s,1,p-1);

delete (s,1,p);

val (s, u,i);

if i<>0 then begin

writeln ('Ошибка в числе ',s,

' – нет номера участка');

halt;

end;

if u=uch then begin

{подсчет рабочих на участке}

writeln (fam);

kol:=kol+1;

end;

end

else begin

writeln ('Ошибка в строке ',s,

' – нет пробела');

halt;

end;

end;

close (f);

writeln ('kol=',kol);

end.

Значительная часть этой программы посвящена анализу прочитанной из файла строки "смешанных" данных. Программу можно существенно упростить, изменив формат исходных данных -- например, на одной строке файла может быть набрана фамилия, а на другой -- номер участка. Тогда основной цикл чтения файла будет выглядеть так:

while not eof (f) do begin

readln (f, fam);

readln (f, u);

if (u=uch) then begin

{ обработка }

end;

end;

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

21.3. Работа с параметрами командной строки

Программа в DOS или Windows может запускаться с параметрами командной строки, через которые обычно передают имена файлов или указывают режимы работы программы:

turbo. exe vasya. pas

— здесь программе Турбо Паскаль передано имя файла vasya. pas;

my /s /a

— программе с именем my переданы параметры /s и /a.

Существует две стандартных функции для работы с параметрами:

Paramcount -- вернет общее число параметров командной строки;

Paramstr(i) -- вернет строку, содержащую параметр номер i.

В качестве примера реализуем программу просмотра текстового файла на экране, которой параметром передается имя нужного файла.

var fil:text; name:string;

begin

if Paramcount<>1 then begin

writeln ('Запускайте так: ',

'FILEVIEW имя_файла');

halt; end;

assign (fil, Paramstr(1));

reset (fil);

while not eof(fil) do begin

readln (fil, name);

writeln (name);

end;

end.

Второе полезное свойство функции Paramstr -- вызванная из исполняемого файла с аргументом 0, она возвращает полный путь к нему.

Из оболочки Турбо Паскаля параметры командной строки можно передать запускаемой программе через пункт Parameters меню Run.

22. Записи. Бинарные файлы

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

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

Описанию переменной типа запись предшествует определение типа с помощью оператора type. Для нашей записи это описание могло бы выглядеть следующим образом:

type zap = record

fam: string;

m, inf, h: integer;

end;

Здесь zap -- имя нового типа данных, а fam, m, inf и h -- имена полей записи. Служебные слова record... end в данном случае играют роль операторных скобок, внутри которых записываются поля записи с указанием их типов.

Не очень удобно то, что для каждой дисциплины введено отдельное поле. Введем тип данных student, где эта проблема решена использованием в качестве поля массива balls (баллы студента по трем дисциплинам):

type student = record

fam: string;

balls: array [1..3] of integer;

end;

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

var str: student;

или массив таких переменных:

var students: array [1..20] of student;

Для обращения к отдельным полям переменной типа запись используется селектор записи -- символ точки, разделяющий наименования записи и поля:

str. fam

— обращение к полю "фамилия" записи str;

students[1].fam

— обращение к полю "фамилия" первого элемента массива записей students;

students[i].balls[2]

— вторая оценка i-го элемента массива записей students.

С полем записи, как и с элементом массива, разрешены все допустимые в языке операции над переменной соответствующего типа.

Для того чтобы узнать объем в байтах, занимаемый записью в оперативной памяти или на жестком диске, следует воспользоваться стандартной функцией sizeof, передав ей в качестве аргумента имя типа записи:

sizeof (student) -- вернет размер памяти в байтах, занимаемый одной записью типа student.

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

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

·  Типизированные файлы. Для них тип компонент указывается непосредственно в описании файловой переменной. Описание в этом случае имеет вид: var файловая_переменная: file of тип_компонент_файла;

Например, если компоненты файла имеют тип записи zap, то разделе описания переменных можно объявить файловую переменную следующим образом:

var f: file of zap;

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

var f2: file of real;

Здесь объявлен файл, содержащий вещественные числа во внутреннем представлении. Размер одной записи такого файла будет равен 6 байт (размер памяти, отводимый под величину типа real).

·  Нетипизированные файлы. Задаются стандартным типом file без указания типа компонент, например:

var f: file;

Чтение и запись данных в такой файл может осуществляться произвольными "порциями" данных, что удобно, например, при организации временных и буферных файлов. Рассмотрение нетипизированных файлов выходит за рамки нашего курса.

·  Текстовые файлы. Знакомые нам по гл. 21 текстовые файлы задаются стандартным типом text, например:

var f: text;

Компонентами текстового файла являются строки переменной длины. После ввода каждой строки нажимается клавиша Enter. Исторически сложилось так, что при этом в конец каждой строки дописывается два невидимых символа: символ с кодом 13 (CR, возврат каретки) и символ с кодом 10 (LF, перевод строки). Доступ к строкам осуществляется последовательно, начиная с первой. Число строк в текстовом файле может быть произвольным. Последним символом файла может быть специальный маркер EOF (End Of File) с кодом #26.

Существенно подчеркнуть то, что первый и второй вид файлов -- бинарные, а не текстовые:

Текстовый файл содержит только алфавитно-цифровые символы и ряд специальных (такие, как возврат каретки, перевод строки, табуляция). Содержимое файла непосредственно доступно для чтения в любом редакторе текста или в окне консоли.

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

Например, следующая несложная программа объявляет бинарный файл, состоящий из вещественных чисел и записывает в него числа от 1 до 10 включительно:

var f:file of real; r:real;

begin

assign (f,'real. dat');

rewrite (f);

r:=1;

while r<11 do begin

write (f, r); r:=r+1;

end;

close (f);

end.

Так как размер величины типа real в памяти равен 6 байтам, полученный файл real. dat будет иметь размер 60 байт и содержать внутренние машинные представления указанных чисел (рис. 22.1).

Рис. 22.1. Бинарный файл

Никакого "текстового" смысла в этой записи нет, чтобы ее интерпретировать, нужно открыть файл в редакторе, поддерживающем 16-ричное "машинное" представление чисел или дамп (рис. 22.2).

Рис. 22.2. Шестнадцатеричный дамп бинарного файла

В этом представлении видно, что каждое число занимает по 6 байт.

Переписав программу так, чтобы числа писались в текстовом представлении, получаем следующее:

var f:text; r:real;

begin

assign (f,'real. txt');

rewrite (f);

r:=1;

while r<11 do begin

write (f, r); r:=r+1;

end;

close (f);

end.

Файл real. txt состоит из одной строки (так как мы писали только оператором write), в этой строке приведены вещественные числа в экспоненциальной форме (поскольку мы не указывали ширину и точность вывода). Файл изображен на рис. 22.3.

Рис. 22.3. Текстовый файл

Размер файла real. txt равен 170 байт, он текстовый и его можно открыть в "Блокноте" Windows.

Важно усвоить и то, что для бинарных файлов не имеет смысла понятие "строки текста". Поэтому чтения и запись при работе с бинарными файлами осуществляются только процедурами read и write, но не readln и writeln.

Все изученные нами ранее приемы и средства работы с файлами (кроме цикла с функцией eof), вообще говоря, применимы лишь к текстовым файлам, так как существенно зависели от понятия строки данных. Для работы с бинарными файлами необходимы иные средства, позволяющие удобно перемещаться по их записям.

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11