Вы находитесь на странице: 1из 20

Лекция 15-16.

Работа с файлами

Файловые переменные
Под файлом понимается либо именованная область внешней памяти ПК
(жесткого диска, гибкой дискеты, электронного «виртуального» диска), либо
логическое устройство – потенциальный источник или приемник
информации.
Любой файл имеет три характерные особенности. Во-первых, у него
есть имя, что дает возможность программе работать одновременно с не-
сколькими файлами. Во-вторых, он содержит компоненты одного типа.
Типом компонентов может быть любой тип Турбо Паскаля, кроме файлов.
Иными словами, нельзя создать «файл файлов». В-третьих, длина вновь
создаваемого файла никак не оговаривается при его объявлении и ограни-
чивается только емкостью устройств внешней памяти.
Файловый тип или переменную файлового типа можно задать одним из
трех способов:

<имя> = FILE OF <mun>;


<имя> = TEXT:
<имя> = FILE;

Здесь <имя> - имя файлового типа (правильный идентификатор);


FILE, OF - зарезервированные слова (файл, из);
TEXT - имя стандартного типа текстовых файлов;
<тип> - любой тип Турбо Паскаля, кроме файлов.
Например:

type
product = record name string;
соde : word;
cost : comp;
end;
105
text80 = file of string[80];
var
f1 : file of char;
f2 : text;
f3 : file;
f4 : text80;
f5 : file of product;
В зависимости от способа объявления можно выделить три вида файлов:
− типизированные файлы (задаются предложением FILE OF...);
− текстовые файлы (определяются типом TEXT);
− нетипизированные файлы (определяются типом FILE).
В наших примерах FI, F4 и F5 - типизированные файлы, F2 -текстовый
файл, F3 - нетипизированный файл. Вид файла, вообще говоря, определяет
способ хранения информации в файле. Однако в Турбо Паскале нет средств
контроля вида ранее созданных файлов. При объявлении уже существующих
файлов программист должен сам следить за соответствием вида объявления
характеру файла.
Любой программе доступны два предварительно объявленных файла со
стандартными файловыми переменными: INPUT - для чтения данных с
клавиатуры и OUTPUT - для вывода на экран. Стандартный Паскаль требует
обязательного упоминания этих файлов в заголовке программы, например,
так

PROGRAM NameOfProfgram( input, output);

В Турбо Паскале это необязательно, вот почему заголовок программы


можно опускать.

Связывание файловой переменной с именем файла


Любые другие файлы, а также логические устройства становятся
доступны программе только после выполнения особой процедуры открытия
файла (логического устройства). Эта процедура заключается в связывании
106
ранее объявленной файловой переменной с именем существующего или
вновь создаваемого файла, а также в указании направления обмена
информацией: чтение из файла или запись в него.
Файловая переменная связывается с именем файла в результате
обращения к стандартной процедуре ASSIGN:

ASSIGN (<ф.п.>, <имя файла или, л.у.>);

Здесь <ф.п.> - файловая переменная (правильный идентификатор,


объявленный в программе как переменная файлового типа);
<имя файла или л.у.> - текстовое выражение, содержащее имя файла или
логическое устройство.
Если имя файла задается в виде пустой строки, например, ASSIGN(f,"),
то в зависимости от направления обмена данными файловая переменная
связывается со стандартным файлом INPUT или OUTPUT.
Имя файла - это любое выражение строкового типа, которое строится
по правилам определения имен в MS DOS (операционной системе ПК):
− имя содержит до восьми разрешенных символов; разрешенные
символы - это прописные и строчные латинские буквы, цифры и
символы: !@#$%^&()'~-_
− имя начинается с любого разрешенного символа;
− за именем может следовать расширение - последовательность до
трех разрешенных символов; расширение, если оно есть, отделяется
от имени точкой.
Перед именем может ставиться так называемый путь к файлу: имя диска
и/или имя текущего каталога и имена каталогов вышестоящих уровней.
Имя диска - это один из символов A...Z , после которого ставится
двоеточие. Имена А: и В: относятся к дисковым накопителям на гибких
дискетах, имена С:. D: и т.д. - к жестким дискам.

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

var
finp:text;
fout : file of string;
const
name = ' c:\dir\subdir\out.txt';
assign(finp, '123.dat');
assign(fout, name);

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

RESET (<ф. п.>);

Здесь <ф.п.> - файловая переменная, связанная ранее процедурой


ASSIGN с уже существующим файлом или логическим устройством-при-
емником информации.
При выполнении этой процедуры дисковый файл или логическое
устройство подготавливается к чтению информации. В результате специ-

108
альная переменная-указатель, связанная с этим файлом, будет указывать на
начало файла, т.е. на компонент с порядковым номером 0.
Если делается попытка инициировать чтение из несуществующего
файла или из логического устройства PRN, возникает ошибка периода
исполнения, которая может быть сообщена программе ненулевым значением
встроенной функции IORESULT типа WORD. Например, следующий
фрагмент программы позволяет установить, существует ли требуемый файл
на диске:

var
f : file of char;
assign(f,'myfile.dat');
{$I-} {Отключить контроль ошибок ввода-вывода}
reset(f):
{SI+} {Включить контроль ошибок ввода-вывода)
If IOResult о 0 then
..... {Файл не существует}
else
..... {Файл существует}

В этом фрагменте с помощью директивы компилятора {$I-} отключается


автоматический контроль ошибок ввода-вывода. Если этого не сделать, то
отсутствие файла приведет к аварийному завершению программы.
В Турбо Паскале разрешается обращаться к типизированным файлам,
открытым процедурой RESET (т.е. для чтения информации), с помощью
процедуры WRITE (т.е. для записи информации). Такая возможность
позволяет легко обновлять ранее созданные типизированные файлы и при
необходимости расширять их. Для текстовых файлов, открытых процедурой
RESET, нельзя использовать процедуру WRITE или WRITELN.
Стандартная процедура REWRITE (<ф.п.>) инициирует запись
информации в файл или в логическое устройство, связанное ранее с
файловой переменной <ф. п. >. Процедурой REWRITE нельзя инициировать

109
запись информации в ранее существовавший дисковый файл: при
выполнении этой процедуры старый файл уничтожается и никаких
сообщений об этом в программу не передается. Новый файл
подготавливается к приему информации и его указатель принимает значение
0.
Стандартная процедура APPEND (<ф.п.>) инициирует запись в ранее
существовавший текстовый файл для его расширения, при этом указатель
файла устанавливается в его конец. Процедура APPEND применима только к
текстовым файлам, т.е. их файловая переменная должна иметь тип TEXT (см.
выше). Процедурой APPEND нельзя инициировать запись в типизированный
или нетипизированный файл. Если текстовый файл ранее уже был открыт с
помощью RESET или REWRITE, использование процедуры APPEND приведет
к закрытию этого файла и открытию его вновь, но уже для добавления
записей.

Процедуры и функции для работы с файлами


Ниже описываются процедуры и функции, которые можно использовать
с файлами любого вида. Специфика работы с типизированными, текстовыми
и нетипизированными файлами рассматривается в следующих разделах.
Процедура CLOSE. Закрывает файл, однако связь файловой переменной
с именем файла, установленная ранее процедурой ASSIGN, сохраняется.
Формат обращения:

CLOSE (<ф.п.>)

При создании нового или расширении старого файла процедура


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

110
файл можно повторно открыть без дополнительного использования
процедуры ASSIGN.
Процедура RENAME. Переименовывает файл. Формат обращения:

RENAME (<ф.п.>, <новое имя>)

Здесь <новое имя> - строковое выражение, содержащее новое ими


файла. Перед выполнением процедуры необходимо закрыть файл, если он
ранее был открыт процедурами RESET, REWRITE или APPEND.
Процедура ERASE. Уничтожает файл. Формат обращения:

ERASE(<ф.п.>)

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


был открыт процедурами RESET, REWRITE или APPEND.
Следующий фрагмент программы показывает, как можно использовать
процедуры RENAME и CLOSE при работе с файлами. Предположим, что
требуется отредактировать файл, имя которого содержит переменная NAME.
Перед редактированием необходимо убедиться, что нужный файл имеется на
диске, и переименовать его - заменить расширение этого файла на .ВАК
(страховочная копия). Если файл с таким расширением уже существует, его
надо стереть.

var
fi : text; {Исходный файл}
fo : text; {Отредактированный файл}
name : string;
name_bak: string;
k,i : word;
const
bak = '.bak';
........
{ Получить в name_bak имя файла с расширением .ВАК: }

111
k := pos('.',name):
If k=0 then k := length(name) + 1;
name_bak := copy(name,1,k-1) + bak;
{ Проверить существование исходного файла: }
assign(fi,name):
{$I-} reset(fi);
If IOResult о 0 then halt; {Файл не существует}
close(fi):
{ Проверить существование .ВАК-файла: } assign(fo,name_bak);
reset(fo);
{$I+} If IOResult = 0 then
begin {Файл .ВАК существует}
close(fo);
erase(fo);
end;
{ Проверки закончены, подготовка к работе: }
rename(fi,name_bak);
reset(fi);
assign(fo,name);
rewrite(fo);

Обратите внимание: проверка на существование BAK-файла в данном


примере необходима, так как обращение

rename(fi,name_bak);

вызовет ошибку в случае, если такой файл существует.


Функция EOF(<ф.п.>): BOOLEAN. Логическая функция, тестирующая
конец файла. Возвращает TRUE, если файловый указатель стоит в конце
файла. При записи это означает, что очередной компонент будет добавлен в
конец файла, при чтении - что файл исчерпан.
Функция IORESULT : WORD. Возвращает условный признак последней
операции ввода-вывода.

112
Если операция завершилась успешно, функция возвращает ноль. Коды
ошибочных операций ввода-вывода представлены в прил.З. Следует
помнить, что IORESULT становится доступной только при отключенном
автоконтроле ошибок ввода-вывода. Директива компилятора {$I-} отклю-
чает, а директива {$I+} включает автоконтроль. Если автоконтроль от-
ключен, а операция ввода-вывода привела к возникновению ошибки,
устанавливается флаг ошибки и все последующие обращения к вводу-выводу
блокируются, пока не будет вызвана функция IORESULT.
Ряд полезных файловых процедур и функций становится доступным при
использовании библиотечного модуля DOS.TPU, входящего в стандартную
библиотеку TURBO.TPL . Эти процедуры и функции указаны ниже. Доступ к
ним возможен только после объявления USES DOS в начале программы.

Текстовые файлы
Текстовый файл трактуется в Турбо Паскале как совокупность строк
переменной длины. Доступ к каждой строке возможен лишь последова-
тельно, начиная с первой. При создании текстового файла в конце каждой
записи (строки) ставится специальный признак EOLN (End Of LiNe -конец
строки). а в конце всего файла - признак EOF (End Of File - конец файла).
Для доступа к записям применяются процедуры READ, READLN,
WRITE, WRITELN. Они отличаются возможностью обращения к ним с
переменным числом фактических параметров, в качестве которых могут
использоваться символы, строки и числа. Первым параметром в любой из
перечисленных процедур может стоять файловая переменная. В этом случае
осуществляется обращение к дисковому файлу или логическому устройству,
связанному с переменной процедурой ASSIGN. Если файловая переменная
не указана, происходит обращение к стандартным файлам Input и Output.
Процедура READ. Обеспечивает ввод символов, строки чисел. Формат
обращения:

READ (<ф.п.>,<сп.ввода>);

113
или

READ (<сп.ввода>);

Здесь <сп.ввода> - список ввода: последовательность из одной или более


переменных типа CHAR, STRING, а также любого целого или вещественного
типа.
Процедура READ прекрасно приспособлена к вводу чисел. При об-
ращении к ней за вводом очередного целого или вещественного числа
процедура «перескакивает» маркеры конца строк, т.е. фактически весь файл
рассматривается ею как одна длинная строка, содержащая текстовое
представление чисел. В сочетании с проверкой конца файла функцией EOF
процедура READ позволяет организовать простой ввод массивов данных,
например, так:

const
N = 1000; { максимальная длина ввода }
var
f : text;
m : array [1..N] of real;
i : Integer;
BEGIN
assign(f, 'prog.dat');
reset(f);
i := 1;
while not EOF(f) and (I <= N) do
begin
read(f,m[i]);
inc(i);
end;
сlоse(f);
end.

114
Процедура READLN. Обеспечивает ввод символов, строк и чисел. Эта
процедура идентична процедуре READ за исключением того, что после
считывания последней переменной оставшаяся часть строки до маркера
EOLN пропускается, поэтому следующее обращение к READLN или READ
начинается с первого символа новой строки. Кроме того, эту процедуру
можно вызвать без параметра <сп.ввода> (см. процедуру READ), что
приведет к пропуску всех символов текущей строки вплоть до EOLN.
Процедура WRITE. Обеспечивает вывод информации в текстовый файл
или передачу ее на логическое устройство. Формат обращения:

WRITE(<ф.п.>,<сп.вывода>) или WRITE(<сп.вывода>);

Здесь <сп.вывода> - список вывода: последовательность из одного или


более выражений типа CHAR, STRING, BOOLEAN, а также любого целого
или вещественного типа.
Файловая переменная <ф.п.>, если она указана, должна быть пред-
варительно описана как переменная типа TEXT и связана с именем файла
или логическим устройством процедурой ASSIGN. Если файловая пере-
менная отсутствует, подразумевается вывод в стандартный файл OUTPUT,
который обычно связан с экраном ПК.
Любой элемент списка вывода может иметь форму

OutExpr [ : MInWidth [ : DecPlaces ] ]

Здесь OUTEXPR - выводимое выражение;


MINWIDTH, DECPLACES - выражения типа WORD (квадратные скобки
означают возможность отсутствия заключенных в них параметров).
Подпараметр MINWIDTH , если он присутствует, указывает мини-
мальную ширину поля, в которое будет записываться символьное пред-
ставление значения OUTEXPR. Если символьное представление имеет
меньшую длину, чем MINWIDTH, оно будет дополнено слева пробелами,

115
если - большую длину, то подпараметр MINWIDTH итерируется и выводится
необходимое число символов.
Подпараметр DECPLACES задает количество десятичных знаков в
дробной части вещественного числа. Он может использоваться только
совместно с MINWIDTH и только по отношению к выводимому выражению
одного из вещественных типов.
Если ширина поля вывода не указана, соответствующий параметр
выводится вслед за предыдущим без какого-либо их разделения.
Процедура WRITELN. Эта процедура полностью идентична процедуре
WRITE за исключением того, что выводимая строка символов завершается
кодами CR и LF. При вызове WRITELN можно опускать параметр
<сп.вывода>: в этом случае в файл передается маркер EOLN, что при выводе
на экран приведет к переводу курсора в начало следующей строки.
Логическая функция EOLN. Возвращает TRUE, если во входном
текстовом файле достигнут маркер конца строки. Формат обращения:

EOLN(<ф.n.>);

Если параметр <ф.п.> опущен, функция проверяет стандартный файл


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

var
f: text;
s: string;
const
Sum: longint = 0; {Здесь будет количество символов}
BEGIN
Writе('Имя файла: '); {Запросить}

116
Readln(s); {и ввести имя файла}
assign(f ,s);
Reset(f); {Открыть файл}
while not EOF(f) do {Подсчитать}
begin {количество}
Readln(f,s); {символов}
inc(Sum, Length(s)) {в тексте}
end; {этой программы}
Close(f); {Закрыть файл}
Writeln('Объем - ',Sum/40000:6:2,' уч.изд.л.');
END.

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

READ (<ф.п.>,<сп.ввода>);

Здесь <сп.ввода> - список ввода, содержащий одну или более


переменных такого же типа, что и компоненты файла.
Файловая переменная <ф. п. > должна быть объявлена предложением
FILE OF... и связана с именем файла процедурой ASSIGN. Файл необходимо
117
открыть процедурой RESET. Если файл исчерпан, обращение к READ
вызовет ошибку ввода-вывода.
Процедура WRITE. Используется для записи данных в типизированный
файл. Формат обращения:

WRITE (<ф.п.>,<сп.вывода>);

Здесь <сп. вывода> - список вывода, содержащий одно или более


выражений того же типа, что и компоненты файла.
Процедура SEEK. Смещает указатель файла к требуемому компоненту.
Формат обращения:

SEEK (<ф. п. >,<N компонента>);

Здесь <N компонента> - выражение типа LONGINT, указывающее


номер компонента файла.
Первый компонент файла имеет номер 0. Процедуру нельзя применять к
текстовым файлам.
Функция FILESIZE. Возвращает значение типа LONGINT, которое
содержит количество компонентов файла. Формат обращения:

FILESIZE(<ф.п.>);

Функцию нельзя использовать для текстовых файлов. Чтобы пере-


местить указатель в конец файла, можно написать:

seek(FileVar, FileSize(FileVar));

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


Функция FILEPOS. Возвращает значение типа LONGINT, содержащее
порядковый номер компонента файла, который будет обрабатываться
следующей операцией ввода-вывода. Формат обращения:

118
FILEPOS(<ф.п.>);

Функцию нельзя использовать для текстовых файлов. Первый ком-


понент файла имеет порядковый номер 0.

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

var
f : file;
..........
assign(f, 'myfile.dat');
reset(f,5l2);

Длина записи нетипизированного файла указывается вторым


параметром при обращении к процедурам RESET или REWRITE, в качестве
которого может использоваться выражение типа WORD. Если длина записи
не указана, она принимается равной 128 байтам.
При работе с нетипизированными файлами могут применяться все
процедуры и функции, доступные типизированным файлам, за исключением
READ и WRITE, которые заменяются соответственно высокоскоростными
процедурами BLOCKREAD и BLOCKWRITE . Для вызова этих процедур
используются следующие предложения:

119
BLOCKREAD(<ф.п.>,<буф>,<N>[,<NN>]);
BLOCKWRITE(<ф.п.>,<буф>,<N>[,<NN>]);

Здесь <буф> - буфер: имя переменной, которая будет участвовать в


обмене данными с дисками;
<N> - количество записей, которые должны быть прочитаны или
записаны за одно обращение к диску;
<NN> - необязательный параметр, содержащий при выходе из про-
цедуры количество фактически обработанных записей.
За одно обращение к процедурам может быть передано до N*RECS байт,
где RECS - длина записи нетипизированного файла. Передача идет, начиная с
первого байта переменной <буф>. Программист должен позаботиться о том,
чтобы длина внутреннего представления переменной <буф> была
достаточной для размещения всех N*RECS байт при чтении информации с
диска. Если при чтении у казана переменная <буф> недостаточной длины
или если в процессе записи на диск не окажется нужного свободного
пространства, возникнет ошибка ввода-вывода, которую можно за-
блокировать, указав необязательный параметр <NN> (переменная типа
WORD).
После завершения процедуры указатель смещается на <NN> записей.
Процедурами SEEK, FILEPOS и FILESIZE можно обеспечить доступ к любой
записи нетипизированного файла.

Лекция 17. Структурированный тип данных - множество

Определение множеств
Множества – это наборы однотипных логически связанных друг с
другом объектов. Характер связей между объектами лишь подразумевается
программистом и никак не контролируется Турбо Паскалем. Количество
элементов, входящих в множество, может меняться от 0 до 256 (множество,

120
не содержащее элементов, называется пустым). Именно непостоянством
количества своих элементов множества отличаются от массивов и записей.
Два множества считаются эквивалентными тогда и только тогда, когда
все их элементы одинаковы, причем порядок следования элементов в
множестве безразличен. Если все элементы одного множества входят также и
в другое, говорят о включении первого множества во второе. Пустое
множество включается в любое другое.
Пример определения и задания множеств:

type
DigitChar = set of '0'..'9';
Digit = set of 0..9;
var
s1,s2,s3: DigitChar;
s4,s5,s6: Digit;
...
s1:=['1','2','3'];
s2:=['3','2','1'];
s1:=['2','3'];
s1:=[0..3,6];
s1:=[4,5];
s1:=[3..9];
...

В этом примере множества s1 и s2 эквивалентны, а множество s3


включено в s2, но не эквивалентно ему.
Описание типа множество имеет вид
<имя типа> = set of <базовый тип>;
Здесь <имя типа> – правильный идентификатор;
set, of – зарезервированные слова (множество, из);
<базовый тип> – базовый тип элементов множества, в качестве которого
может использоваться любой порядковый тип, кроме Word, Integer, Longint.

121
Для задания множества используется так называемый конструктор
множества: список спецификаций элементов множества, отделяемых друг от
друга запятыми; список обрамляется квадратными скобками.
Спецификациями элементов могут быть константы или выражения базового
типа, а также – тип-диапазон того же базового типа.

Операции над множествами


Над множествами определены следующие операции:
* – пересечение множеств; результат содержит элементы, общие для
обоих множеств. Например, s4*s6 содержит [3], s4*s5 – пустое множество.
+ – объединение множеств; результат содержит элементы из первого
множества дополненные недостающими элементами из второго множества:
s4+s5 содержит [0,1,2,3,4,5,6];
s5+s6 содержит [3,4,5,6,7,8,9];
- – разность множеств; результат содержит те элементы из первого
множества, которые не принадлежат второму:
s6-s5 содержит [3,6,7,8,9];
s4-s5 содержит [0,1,2,3,6];
= – проверка эквивалентности; возвращает true, если множества
эквивалентны.
<> – проверка неэквивалентности; возвращает true, если множества
неэквивалентны.
<= – проверка вхождения; возвращает true, если первое множество
является подмножеством второго.
>= – проверка вхождения; возвращает true, если второе множество
является подмножеством первого.
in – проверка принадлежности; в этой бинарной операции первый
элемент – выражение, а второй – множество того же типа; возвращает true,
если выражение имеет значение, принадлежащее множеству:
3 in s6 возвращает true;

122
2*2 in s1 возвращает false;

Пример
В следующем примере реализуется алгоритм выделения из первой сотни
натуральных чисел всех простых. В его основе (также как и в примере из
лабораторной работы №3) лежит прием, известный под названием «решето
Эратосфена». Сравните программы, приведенные здесь и в лабораторной
работе №3.

{ Выделение всех простых чисел из первых N целых }


const
N = 100; {Количество элементов исходного множества}
type
SetOfNumber = set of 1..N:
var
n1,next,i : word; {Вспомогательные переменные}
BeginSet, {Исходное множество}
PrimerSet : SetOfNumber; {Множество простых чисел} BEGIN
BeginSet := [2..N]; {Создать исходное множество}
PrimerSet := [1]; {Первое простое число}
Next := 2; {Следующее простое число}
while BeginSet <> [] do {Начало основного цикла}
begin
n1:= next; {n1-число,кратное очередному
простому (next)}
while n1 <- N do {Цикл удаления из исходного
множества непростых чисел:}
begin
BeginSet := BeginSet-[n1];
n1 :=n1+next; {Следующее кратное}
end; {Конец цикла удаления}
PrimerSet := PrimerSet+[next];
repeat {Получить следующее простое, которое есть
первое невычеркнутое из исходного множества}
inc(next)
123
until (next In BeginSet) or (next > N)
end; {Конец основного цикла)

{ Вывод результата: }
for i := 1 to N do
if I in PrimerSet then write(i:8);
writeln;
END.

124