Учебный центр "ANIT Texno Inform"
Опубликован: 25.06.2014 | Доступ: свободный | Студентов: 2596 / 851 | Длительность: 24:39:00
Специальности: Программист
Лекция 21:

Файлы

< Лекция 20 || Лекция 21: 1234 || Лекция 22 >

Нетипизированные файлы

Такие файлы ассоциируются с переменными типа 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 байт, поскольку файл кончился, читать больше нечего.

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

< Лекция 20 || Лекция 21: 1234 || Лекция 22 >
Инга Готфрид
Инга Готфрид
Александр Скрябнев
Александр Скрябнев

Через WMI, или используя утилиту wmic? А может есть еще какие более простые пути...