Научится работать с различными видами файлов.
То есть, файл - это часть ПЗУ - Постоянного Запоминающего Устройства, которым может быть жесткий диск, дискета, флэш-карта, CD или DVD. В операционных системах существуют различные файловые структуры, или, как еще говорят, таблицы размещения файлов. Например, в Windows это может быть FAT32 или NTFS. В этих таблицах имена файлов соотносятся с реальным адресом на диске. На уровне машинных кодов, реальный адрес файла представляет собой целое число - номер байта, с которого начинается файл. Также в таблице содержится общее количество байтов, занятых файлом. Но при программировании на языках высокого уровня, таких, как Объектный Паскаль, мы можем пользоваться символическими именами файлов, которые автоматически преобразуются операционной системой в реальные адреса и размеры этих файлов.
Файлы бывают трех типов:
Чтобы открыть доступ к файлу, требуется создать файловую переменную одним из трех способов:
var
f1: TextFile; //текстовый файл
f2: File of <тип>; //типизированный файл
f3: File; //нетипизированный файл
Типизированный файл может работать с любыми типами Объектного Паскаля, кроме файлов. Это может быть целое или вещественное число, символ, строка или даже пользовательский тип данных, например, запись (о записях - в следующей лекции). Например, объявить типизированный файл, работающий с символами, можно так:
var
MyF: File of Char; //типизированный символьный файл
Однако для работы с файлами мало объявить переменную файлового типа, нужно еще эту переменную "привязать" к конкретному файлу. Делается это с помощью процедуры AssignFile, в которую передается два параметра - имя файловой переменной, к которой "привязывается" файл, и имя существующего файла, с которым мы хотим работать. Если имя файла указано без адреса, то процедура подразумевает, что файл находится в текущей папке. Если файла нет, программа вызовет ошибку, поэтому рекомендуется всегда перед связыванием файловой переменной с файлом вначале проверить - существует ли файл? Делается это с помощью функции FileExists, с которой нам уже доводилось работать, и которая возвращает True, если указанный файл существует, и False в противном случае. Вот пример связывания файловой переменной с файлом:
var
f1: TextFile; //текстовый файл
begin
if FileExists('c:\01\myfile.txt') then begin
AssignFile(f1, 'c:\01\myfile.txt'); //связали файловую переменную с файлом
... //дальнейшая работа с файлом
end; //if
После того, как мы связали файловую переменную с файлом, мы можем обращаться к ней, как к файлу. Мы можем открыть её, считать или записать информацию, закрыть. А после работы с файлом его обязательно нужно закрыть! Делается это процедурой CloseFile, в которую передается всего один параметр - имя файловой переменной, например:
CloseFile(f1); //закрыли файл, с которым была связана файловая переменная f1
Во время работы с файлом может возникнуть исключительная ситуация. Внимание! Такой термин мы встречаем впервые, и он очень важен для понимания. Что такое исключительная ситуация? Это любая ошибка программы, которая может произойти во время работы. Например, вы открыли какой-то сетевой файл и начали работу с ним. А другой пользователь в это время взял, да удалил этот файл. Или вы пытаетесь работать с файлом, который содержит вирус. Как только вы обратились к нему, ваш антивирус вмешивается, и удаляет файл, или переносит его в карантин. Или же ваш файл "битый" - содержит ошибку, и не может прочитаться.
При попытке чтения из несуществующего файла, или записи в него, или при работе с "битым" файлом произойдет ошибка и возникнет исключительная ситуация. Если вы не обработаете эту ошибку, то ваша программа, скорее всего, намертво повиснет. Вам придется вызывать Диспетчер задач Windows, и снимать задачу - принудительно закрывать вашу программу. Поэтому в любой ситуации, когда имеется риск возникновения исключительной ситуации, программист ВСЕГДА должен ее обработать. Для этого существует блок try-finally-end:
try
//блок кода, в котором может произойти ошибка
finally
//код, который должен выполниться в любом случае, например, код закрытия файла
end;
Между служебными словами try-finally вы вписываете потенциально опасный код. То есть, код, который может вызвать ошибку. А между finally-end указываете тот код, который должен быть выполнен в любом случае, даже при возникновении ошибки. Вот более правильный пример того, как нужно связывать файл с переменной:
var
f1: TextFile; //текстовый файл
begin
try
AssignFile(f1, 'c:\01\myfile.txt'); //связали файловую переменную с файлом
…; //дальнейшая работа с файлом - чтение/запись
finally
CloseFile(f1); //по окончании закрываем файл
end; //try
Как видите, здесь попытку связать файловую переменную с файлом, и дальнейшую работу с ним мы заключили в блок try-finally. Поскольку блок обрабатывает исключительные ситуации, можно не делать дополнительной проверки на существование файла. Получилась ли открытие файла, и работа с ним, или произошла ошибка, в любом случае будет выполнено закрытие файла, что убережет программу от краха.
Есть и другой способ обработки исключительных ситуаций - блок try-except-end:
try
//блок кода, в котором может произойти ошибка
except
//код, который выполнится в том случае, если в блоке между try-except произошла ошибка
end;
Пример работы с файлом можно представить и так:
var
f1: TextFile; //текстовый файл
begin
try
AssignFile(f1, 'c:\01\myfile.txt'); //связали файловую переменную с файлом
…; //дальнейшая работа с файлом - чтение/запись
CloseFile(f1); //по окончании закрываем файл
except
ShowMessage('Внимание! Произошла ошибка открытия файла.');
end; //try
Каким образом обрабатывать исключительные ситуации - решать вам. В зависимости от ситуации, иногда бывает удобней первый способ, иногда - второй.
После связывания файловой переменной с файлом, этот файл нужно инициировать, то есть, указать направление передачи данных - из файла (чтение), в файл (запись), и в обоих направлениях. Если мы открываем файл для чтения, нам следует воспользоваться стандартной процедурой Reset:
Reset(f1);
При этом указатель устанавливается в начало файла, в нулевую позицию.
Если нам нужно создать новый файл, или перезаписать существующий, используется процедура Rewrite:
Rewrite(f2);
Если файл уже существовал, он будет перезаписан без каких либо предупреждений. Если же нам нужно открыть для записи существующий файл, не удаляя содержащейся в нем информации, воспользуемся процедурой Append:
Append(f1);
Файл будет открыт для записи, а указатель переместится в конец файла. Однако, процедура Append применима только к текстовым файлам, то есть к переменным типа TextFile.
Во время работы с файлами можно использовать функции Bof и Eof - первая возвращает истину, если указатель находится в начале файла, вторая - если в конце. Обе функции имеют параметр - файловую переменную:
if Eof(f1) then ShowMessage('Достигнут конец файла.');
Есть еще один способ проверки существования файла, и вообще обработки ошибок ввода-вывода в файл. Это - функция IOResult, которая возвращает ноль при отсутствии ошибок ввода-вывода, и номер ошибки в противном случае. Но использовать эту функцию просто так не получится. Lazarus автоматически обрабатывает ошибки ввода-вывода. Прежде чем использовать IOResult, нужно дать процессору команду отключить автоматическую обработку этих ошибок. Для этого существует директива процессора {$I-}. Затем мы выполняем опасный участок кода, где может произойти ошибка, после чего снова включаем автоконтроль операций ввода-вывода директивой {$I+}. Затем вызываем IOResult. Если операция произошла успешно, то функция вернет ноль. Пример:
var
f1: TextFile;
begin
AssignFile(f1, 'MyText.txt'); //связали файл с переменной
{$I-} //отключили автоконтроль ввода-вывода
Reset(f1); //пытаемся открыть файл для чтения
{$I+} //снова включили автоконтроль ввода-вывода
if IOResult <> 0 then
ShowMessage('Внимание! Произошла ошибка открытия файла.');
Эти файлы связываются с переменной, имеющей тип TextFile, и предназначены для хранения и обработки текста. Фактически, в Паскале текст - это набор строк различной длины.
В начале файла устанавливается специальный системный признак BOF (Begin Of File) - начало файла. В конце каждой строки ставится специальный признак конца строки EOLN (End Of Line) - конец линии, строки. Это два системных, невидимых символа - перевод каретки и новая строка, мы их можем выразить, как символы #13 и #10. В конце файла устанавливается признак EOF (End Of File) - конец файла.
Для доступа к записям применяются процедуры Read и Readln для чтения, Write и Writeln для записи. Мы уже знакомы с этими процедурами по лекции №21, где мы изучали консольные приложения, однако с текстовыми файлами эти процедуры работают несколько иначе. Во всех этих процедурах первым параметром идет файловая переменная, а вторым - список аргументов. Аргументами могут быть одна или несколько переменных строкового, символьного или числового типа. Для примера, создадим простое приложение, которое позволяет работать с текстовым файлом, занося в него построчно текст из строки TEdit.
Откройте Lazarus с новым проектом. В Caption формы напишите "Работа с текстовым файлом". Проект у нас пробный, так что имена формы, проекта и компонентов мы изменять не будем. Просто сохраните проект в папку 23-01. Установите на форму компонент TEdit и удлините его, чтобы пользователь мог ввести длинную строку. Очистите у компонента свойство Text. Ниже установите две кнопки, в Caption первой напишите "Добавить в файл", второй - "Считать из файла". Растяните их, чтобы текст на кнопках не казался прилипшим к краям. Еще ниже установите TMemo. Откройте редактор свойства Lines и удалите оттуда текст. Растяните компонент по размеру Edit1.
В результате, у вас должна получиться примерно такая форма:
(рис 23.1) Форма проекта
С подготовительной частью закончили, займемся программированием кнопок. Сгенерируйте событие OnClick для первой кнопки. Её код будет таким:
procedure TForm1.Button1Click(Sender: TObject);
var
tf: TextFile; //текстовый файл
s: String;
begin
//если пользователь не ввел строку в Edit1, просто выйдем:
if Edit1.Text = '' then exit;
//иначе в s получаем текст:
s:= Edit1.Text;
//затем связываем файловую переменную tf с файлом mytext.txt:
AssignFile(tf, 'mytext.txt');
//далее может возникнуть исключительная ситуация, поэтому
//заключим опасный код в блок try-finally-end:
try
//если файла нет (кнопку нажали первый раз), создадим его:
if not FileExists('mytext.txt') then Rewrite(tf)
//иначе откроем для записи, установив указатель в конец файла:
else Append(tf);
//тут просто записываем строку в файл:
Writeln(tf, s);
//очищаем Edit1, чтобы пользователь видел, что событие произошло:
Edit1.Text:= '';
finally
CloseFile(tf); //закрываем файл
end;
end;
Комментарии достаточно подробны, чтобы понять код. Замечу только пару вещей. Если пользователь ничего не ввел в Edit1, то нам нет нужды открывать файл и сохранять в него пустую строку, поэтому мы просто выходим из процедуры, ничего не делая:
if Edit1.Text = '' then exit;
Когда мы открываем файл, мы вначале делаем проверку на его существование. Файла может не быть в двух случаях: пользователь нажал на кнопку первый раз, либо, поработав с файлом, и не закрывая программы, он удалил этот файл. В этом случае процедурой Rewrite мы создаем новый файл. Если же файл уже существует (кнопка нажималась, либо программа открывается не в первый раз, и файл остался с прошлых сеансов), то процедурой Append мы открываем его для записи, перемещая указатель в конец файла. Далее все просто - мы записываем строку текста в файл, очищаем Edit1 от старого текста и закрываем файл. Код второй кнопки будет похожим:
procedure TForm1.Button2Click(Sender: TObject);
var
tf: TextFile; //текстовый файл
s: String;
begin
//если файла нет, просто выходим:
if not FileExists('mytext.txt') then exit;
//иначе сначала очищаем Memo1:
Memo1.Clear;
//связываем файловую переменную tf с файлом mytext.txt:
AssignFile(tf, 'mytext.txt');
//далее может возникнуть исключительная ситуация, поэтому
//заключим опасный код в блок try-finally-end:
try
Reset(tf); //открыли файл для чтения, указатель в начале файла
//делаем, пока не конец файла:
while not Eof(tf) do begin
Readln(tf, s); //читаем в s очередную строку
Memo1.Lines.Add(s); //добавляем эту строку в Memo1
end;
finally
CloseFile(tf); //закрываем файл
end;
end;
По комментариям понятно, каким образом происходит чтение. Обратите внимание, для записи мы воспользовались процедурой Writeln, а не Write, а при чтении - Readln, а не Read. Это избавило нас от необходимости заботиться о вставке и поиске символов конца строки, рекомендую вам всегда использовать такой, более удобный способ. Весь алгоритм считывания строк мы поместили в блок while-do и считывали текст строка за строкой, пока функция Eof не вернула True, то есть, пока файл не закончился.
(рис 23.2) Программа в действии
Такие файлы предназначены для обработки информации какого-то определенного типа. Это может быть и символ, и число. Если это строка, то её нужно объявлять с определенной длиной, например, так:
var
s: string[50];
Кроме того, файл может принять пользовательский тип данных, например, запись (записи см. в следующей лекции). Поскольку компилятор знает длину типа в байтах, то он может организовать доступ к любому элементу типизированного файла по его порядковому номеру. Как только файл открыт, указатель устанавливается в начало файла и указывает на первый элемент с номером 0. После чтения или записи указатель сдвигается к следующему элементу. Это было бы невозможно, если бы компилятор заранее не знал размер каждого считываемого или записываемого элемента, поэтому и строка должна иметь строго определенный размер.
К типизированным файлам применимы следующие функции и процедуры:
FilePos - функция принимает параметр - файловую переменную, и возвращает позицию указателя, то есть, номер элемента, который будет обрабатываться при последующем чтении/записи. Пример:
FilePos(f1);
FileSize - функция, которая также принимает в качестве параметра файловую переменную, и возвращает количество элементов в файле. Пример:
FileSize(f2);
Read - процедура чтения данных из типизированного файла. В качестве параметров указывается файловая переменная, и одна или несколько переменных с таким же типом данных, как у файла. В эти переменные будет считан один или несколько (по количеству переменных) элементов, при этом указатель будет сдвигаться к следующему элементу. Пример:
Read(f1, MyRealPerem);
Write - процедура записи в типизированный файл. Как и у Read, у Write есть параметры - файловая переменная, и одна или несколько переменных с таким же типом данных, как у файла. Из этой переменной (этих переменных) будут браться данные, которые будут записаны в файл. После каждой записи указатель перемещается. Пример:
Write(f1, MyRealPerem);
Seek - процедура смещения указателя. Процедуре нужно указать файловую переменную и номер элемента, к которому нужно переместить указатель. Примеры:
Seek(f, 0); //указатель в начало файла
Seek(f, 1); //указатель на второй элемент
Seek(f, FileSize(f)); //указатель в конец файла
Еще раз замечу, что такие процедуры, как Readln, Writeln и Append работают только с текстовыми файлами, в типизированных или нетипизированных файлах их использовать нельзя.
Изучим работу с типизированными файлами на простом примере.
Откройте Lazarus с новым проектом. В Caption формы напишите "Типизированный файл" и сохраните проект в папку 23-02, не меняя имен формы, модуля и проекта.
На форму установите компонент TListBox. Полученный ListBox1 немного вытяните по высоте, чтобы в нем уместились 10 чисел. Справа установите одну под другой, две простых кнопки TButton, в Caption которых напишите, соответственно, "Сформировать файл" и "Читать из файла". У вас должна получиться примерно такая форма:
(рис 23.3) Форма проекта
Идея такая. При нажатии на кнопку "Сформировать файл" мы с помощью функции Random будем формировать 10 случайных чисел, от 0 до 1000, и записывать их в типизированный файл. Файл у нас будет иметь тип Integer, то есть, под каждый элемент в файле будет выделено 4 байта. Таким образом, будет сформировано 10 элементов по 4 байта, в результате получится файл размером 40 байт.
При нажатии на кнопку "Читать из файла" мы будем по одному считывать эти элементы, и добавлять их в ListBox1. Таким образом, мы убедимся, что запись в типизированный файл, и чтение из него происходят без ошибок. Код первой кнопки следующий:
procedure TForm1.Button1Click(Sender: TObject);
var
f: File of integer; //файл типа integer
i: integer; //счетчик для цикла
begin
AssignFile(f, 'mytypefile.dat');
Rewrite(f);
for i:= 1 to 10 do //делаем 10 раз
Write(f, Random(1000)); //записываем в файл случайное целое число
CloseFile(f);
end;
Код во многом вам знакомый, чтобы вы смогли с ним разобраться без надоедливых комментариев. Код второй кнопки:
procedure TForm1.Button2Click(Sender: TObject);
var
f: File of integer;
k: integer; //для получения значений элементов
begin
//выходим из процедуры, если файла нет:
if not FileExists('mytypefile.dat') then exit;
ListBox1.Clear;
AssignFile(f, 'mytypefile.dat');
//Открываем для чтения:
Reset(f);
//делаем от первого до последнего элемента:
while not eof(f) do begin
Read(f, k); //считываем очередной элемент в перем. k
ListBox1.Items.Add(IntToStr(k)); //записываем это значение в ListBox
end;
CloseFile(f);
end;
Код, я думаю, также не нуждается в дополнительных комментариях. Однако все же кое-что замечу. Поскольку файл типизированный, информация в него будет записана не в текстовом, а в двоичном виде, поэтому, открыв полученный файл простым текстовым редактором, вы не увидите там тех же чисел, что были у вас в ListBox1. Собственно, это даже хорошо - простой текстовый файл с данными пользователь может, ради интереса, и испортить неразумной попыткой редактирования, а вот в двоичный файл он лезть побоится.
Такие файлы ассоциируются с переменными типа File, и могут обрабатывать побайтово данные любого типа, поэтому их удобно использовать для копирования/переноса файлов. Нетипизированные файлы не поддерживают функции чтения-записи Read, Readln, Write и Writeln. Вместо них типизированным файлам доступны более скоростные функции BlockRead и BlockWrite. Эти функции позволяют производить чтение-запись блоками, используя для этого переменную-буфер.
Открывать файл для чтения или записи нужно функциями Reset и Rewrite, но тут тоже есть отличие: помимо файловой переменной этим функциям можно указывать длину записи в байтах. Если длина записи не указана, принимается длина по умолчанию 128 байт. Однако обычно указывают длину 1 байт, что позволяет обрабатывать любые, даже самые маленькие файлы, например:
Reset(f1, 1);
Rewrite(f2, 1);
Кроме этих различий, нетипизированным файлам доступны все остальные процедуры и функции, что и типизированным. Изучим работу с нетипизированными файлами на примере, для этого создадим программу, которая выполняет копирование файла.
Откройте Lazarus с новым проектом. Имя формы изменим на fMain (это будет полезная программа, так что имена будем давать осмысленные), в Caption напишем "Копирование файла", BorderStyle установим в bsDialog, а Position - в poDesktopCenter. Сохраним проект в папку 23-03 под именем FileCopier, а модулю главной формы дадим имя Main.
Установите на форму метку TLabel, в Caption которой напишите "Выберите файл, который нужно копировать:". Теперь перейдите на вкладку Misc Палитры компонентов, найдите на ней и установите на форму компонент TFileNameEdit - это гибрид TEdit и диалога открытия файла, он облегчит нам взаимодействие с пользователем. Нам придется обращаться к компоненту в коде, сократите его имя (свойство Name) до FNE.
Ниже установите еще одну метку, в Caption которой напишите "Укажите папку, куда копировать файл:". Еще ниже установите компонент TDirectoryEdit с той же вкладки Misc - этот компонент облегчит нам выбор папки, куда будем делать копию. Сократите его имя до DE.
И напоследок, установите в нижней части формы простую кнопку TButton, в Caption которой напишите "Выполнить копирование". Кнопка у нас одна, так что её можно не переименовывать.
Измените размеры формы и компонентов, чтобы у вас получилось примерно следующее:
(рис 23.4) Форма проекта
С подготовительной работой закончили, перейдем к кодированию. Весь код нам нужно будет делать в событии OnClick кнопки, сгенерируйте его. Вот этот код:
procedure TfMain.Button1Click(Sender: TObject);
var
fIn, fOut : File; //нетипизированные файлы: исходник и копия
NumRead, NumWritten : Word; //количество считанных и записанных байт
Buf : Array[1..2048] of byte; //буфер
begin
//если файл не выбран, делаем сообщение и выходим:
if FNE.Text = '' then begin
ShowMessage('Внимание! Требуется указать или выбрать копируемый файл.');
FNE.SetFocus;
Exit;
end;
//если не выбрана папка, делаем сообщение и выходим:
if DE.Text = '' then begin
ShowMessage('Внимание! Требуется указать или выбрать папку для копии.');
DE.SetFocus;
Exit;
end;
//начинаем работу с файлами:
try
AssignFile(fIn, UTF8ToSys(FNE.FileName));
AssignFile(fOut,UTF8ToSys(DE.Directory + '\' + ExtractFileName(FNE.FileName)));
Reset (fIn,1);
Rewrite (fOut,1);
//изменим курсор на песочные часы:
Screen.Cursor:= crHourGlass;
Repeat
BlockRead (fIn,buf,Sizeof(buf),NumRead);
BlockWrite (fOut,Buf,NumRead,NumWritten);
Until (NumRead=0) or (NumWritten<>NumRead);
finally
closeFile(fIn);
closeFile(fOut);
//изменим курсор на обычный вид:
Screen.Cursor:= crDefault;
ShowMessage('Копирование завершено!');
end;
end;
В коде есть кое-что новое, давайте разберем его подробней. Вначале мы объявили переменные fIn и fOut, которые имеют тип File. Эти переменные мы ассоциируем с файлами: fIn - с исходным файлом, а fOut - с создаваемой копией.
Далее, мы создали переменные-счетчики NumRead и NumWritten типа Word. В эти переменные будет заноситься количество реально считанных и записанных байт.
Также мы создали буфер - массив, в который можно записать до 2048 байт. В принципе, массив можно сделать любого размера, хоть 1 байт, но цифру 2048 я взял из справочной системы самого Lazarus.
Далее идет проверка - указал ли пользователь нужные нам данные? Если он оставил пустой строку с файлом - оригиналом, или с папкой, куда нужно делать копию, мы выводим соответствующее сообщение, и методом SetFocus выделяем нужный компонент (тот, который пропустил пользователь), после чего выходим из процедуры.
Если пользователь указал все данные, то дальше начинается работа с файлами. Мы ассоциируем файлы с файловыми переменными:
AssignFile(fIn, UTF8ToSys(FNE.FileName));
AssignFile(fOut,UTF8ToSys(DE.Directory + '\' + ExtractFileName(FNE.FileName)));
Обратите внимание: в свойстве FileName компонента TFileNameEdit находится адрес и имя выбранного пользователем файла. В свойстве Directory компонента TDirectoryEdit находится адрес и имя выбранной пользователем папки. Но здесь мы дополнительно обработали эти строки функцией UTF8ToSys. В принципе, этого можно было и не делать, но тогда программа вызывала бы ошибку, если бы в адресе или имени файла (папки) были неанглийские символы. Функция UTF8ToSys преобразует символы UTF8 в системную кодировку, которая требуется процедуре AssignFile. Теперь имя файла и адрес может содержать хоть кириллицу, хоть китайские иероглифы - программа не вызовет ошибки. Рекомендую во избежание возможных ошибок всегда обрабатывать ассоциируемые файлы этой функцией. Функция UTF8ToSys описана в модуле FileUtil, который по умолчанию уже подключен к проекту в разделе uses.
Далее, мы открыли оригинал для чтения, и создали копию, указав, что за раз будем обрабатывать по одному байту:
Reset (fIn,1);
Rewrite (fOut,1);
А дальше идет интересный код, который нам еще не встречался:
Screen.Cursor:= crHourGlass;
В любом графическом приложении есть системный объект Screen - экран. У этого объекта есть свойство Cursor, которое отвечает за внешний вид указателя мыши. Если присвоить этому свойству значение crHourGlass, то курсор примет вид песочных часов. Так делают, когда программа выполняет какое то длительное действие, и нужно показать пользователю, что компьютер не завис. Конечно, если файл небольшой, он скопируется мгновенно, вы даже глазом не успеете моргнуть. Но если файл большой, например фильм размером в полтора-два гигабайта, то копирование такого файла займет некоторое время. Поэтому мы и изменили внешний вид курсора. В самом конце строчкой
Screen.Cursor:= crDefault;
мы возвращаем курсору обычный вид. Если вам интересно, какие еще виды курсора бывают, выделите форму и посмотрите свойство Cursor - там довольно внушительный список возможных значений.
Далее мы выполняем чтение-запись:
Repeat
BlockRead (fIn,buf,Sizeof(buf),NumRead);
BlockWrite (fOut,Buf,NumRead,NumWritten);
Until (NumRead=0) or (NumWritten<>NumRead);
Цикл Repeat…Until будет выполняться, пока количество считанных байт не станет равным нулю, что означает, что файл закончен, либо пока количество записанных и считанных байт не станет отличаться (произошла какая то ошибка чтения-записи). Внутри цикла мы сначала читаем данные:
BlockRead (fIn,buf,Sizeof(buf),NumRead);
Как видите, процедура чтения BlockRead имеет четыре параметра:
| 1) Файловая переменная. |
| 2) Буфер, куда будут считаны данные. |
3) Количество читаемых байт. У нас указано Sizeof(buf). Функция SizeOf возвращает размер переменной, то есть в данном случае это будет 2048 байт. |
| 4) Переменная, куда будет записано количество реально прочитанных байт, ведь их может быть меньше, чем 2048. |
Теперь о записи:
BlockWrite (fOut,Buf,NumRead,NumWritten);
Процедура копирования BlockWrite также имеет четыре параметра:
| 5) Файловая переменная. |
| 6) Буфер, откуда будут браться данные для записи. |
7) Переменная с количеством данных, которые требуется записать. Процедура BlockRead записала в переменную NumRead количество реально считанных байт, это количество мы и будем писать. |
| 8) Переменная с количеством реально записанных байт. |
Каждый раз, при чтении или записи, указатель устанавливается в новую позицию, так что нам специально его двигать не нужно. Предположим, что мы уже все байты считали. Тогда в следующий раз при попытке считать новую порцию в переменную NumRead попадет 0 байт, поскольку файл кончился, читать больше нечего.
После копирования мы закрываем файлы, делаем указатель мыши обычного вида и выводим сообщение, что файл скопирован. Сохраните проект, запустите его и попробуйте скопировать файл. Если вы все сделали правильно - файл скопируется без проблем. Перенос файла - это то же самое копирование, только с последующим удалением оригинала.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.