ChangeFileExt — Функция Delphi


Содержание

ChangeFileExt — Функция Delphi

������ 1
function GetFileName(fname : String) : String;
var i : integer;
begin
Result := ExtractFileName(fname);
for i := Length(Result) downto 1 do
if Result = ‘.’ then
begin
Result := copy(Result, 1, i — 1);
break;
end;
end;

������ 2
function GetFileName(fname : String) : String;
begin
Result := ExtractFileName(fname);
while (Length(Result) > 0) and Result[Length(Result)] <> ‘.’ do
Delete(Result, Length(Result, 1));
end;

ChangeFileExt Routine

Unit Edit

Description Edit

Definition (Delphi 6, 2007):

Definition (Delphi 2010):

Technical Comments Edit

(Known issues / Documentation clarifications / Things to be aware of)

Examples Edit

(Please provide links to articles/source code that show how to use this item.)

See Also Edit

(Please provide links to items specifically related to this item.)

User Comments/Tips Edit

(Please leave your name with your comment.)

Мониторинг изменений в директориях и файлах средствами Delphi. Часть #2.

В предыдущем посте мы остановились на том. что разработали небольшое приложение, которое проводило мониторинг изменений в определенной директории и, в случае обнаружения какого-либо изменения, «сигналило» нам. Для организации мониторинга мы использовали поток (TThread) в котором использовалось три взаимосвязанные функции Windows: FindFirstChangeNotification, FindNextChangeNotification и FindCloseChangeNotification.

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

Итак, сначала вспомним как выглядел TURLClient.Execute

Здесь мы получили всего один хэндл события с помощью которого отслеживали сразу несколько возможных изменений в директории — изменение имени файла, изменение имени директории и изменение размера. Так как хэндл был всего один, то нам вполне достаточно было использовать функцию WaitForSingleObject для ожидания события. Но нам ведь никто не запрещает получить не один, а, например, три хэндла событий каждое из которых будет срабатывать на конкретно заданное изменение? Давайте сделаем это, а заодно и разберемся с работой ещё одной функции для обработки событий. Итак, пишем новый Execute потока:

В приведенном выше Execute мы определили массив на 3 элемента, где каждый элемент — это хэндл события рассчитанного на определенный вид изменений в директории. Таким образом мы получили возможность пусть и не полностью, но конкретизировать изменения. Так как мы планируем ожидать несколько событий, то вместо метода WaitForSingleObject мы воспользовались методом WaitForMultipleObjects, который в MSDN имеет следующее описание:

WaitForMultipleObjects

nCount: DWORD — количество хэндлов событий. Этот параметр не может быть равен нулю. Максимальное количество ожидаемых событий ограничивается значением константы MAXIMUM_WAIT_OBJECTS = 64 ;
lpHandles: PWOHandleArray — указатель на массив дескрипторов. Массив не должен содержать повторяющихся THandle.
bWaitAll: boolean — если значение этого параметра равно True , то функция вернет значение только в том случае, если будет получен сигнал от всех событий хэндлы которых определены в массиве. В случае, если значение флага равно False , то функция будет возвращать значения при срабатывание любого из событий, при этом возвращаемое значение будет указывать на объект, который изменил состояние.
dwMilliseconds:DWORD — время в мс ожидания события. Если значение этого параметра равно INFINITE , то функция будет возвращать значение только при срабатывании одного из событий.

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

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

Использование функций CreateFile и ReadDirectoryChangesW

Прежде, чем перейдем к примеру на Delphi, посмотрим на описание этих двух функций.
Функция CreateFile создает или открывает файл или другое устройство ввода/вывода (файл, файловый поток, каталог и т.д.

CreateFile

lpFileName: PChar — имя файла или устройства ввода/вывода, которое будет создано или открыто.
dwDesiredAccess: cardinal — флаг, определяющий режим доступа к файлу или устройству (чтение, запись, чтение и запись). Наиболее часто используемые значения GENERIC_READ, GENERIC_WRITE, или сразу оба (GENERIC_READ or GENERIC_WRITE).
dwShareMode:cardinal — один или несколько флагов, определяющих режим совместного доступа к файлу или устройству. Для установки значений могут использоваться следующие константы:
FILE_SHARE_DELETE (0x00000004) — разрешает другим процессам получать доступ к файлу или устройству в том числе и выполнять операции удаления.
FILE_SHARE_READ (0x00000001) — разрешает другим процессам выполнять операции чтения.
FILE_SHARE_WRITE (0x00000002) — разрешает другим процессам выполнять операции записи.
lpSecurityAttributes:PSecurityAttributes — указатель на структуру SECURITY_ATTRIBUTES, содержащую дополнительные параметры безопасности работы с файлом или устройством. Этому параметру можно присваивать значение nil.
dwCreationDisposition:cardinal — флаг, который определяет какие действия следует применить к файлу или устройству. Для всех устройств, кроме файлов, этот параметр обычно устанавливается в значение OPEN_EXISTING. Также может принимать следующие значения:
CREATE_ALWAYS (2) — всегда создавать новый файл. Если файл уже существует и доступен для записи, то код последней ошибки устанавливается в значение ERROR_ALREADY_EXISTS (183) . Если файл не существует и указан правильный к нему путь, то создается новый файл. а код последней ошибки устанавливается в 0.
CREATE_NEW (1) — создать новый файл. Если файл с таким именем уже существует, то вернется код ошибки ERROR_FILE_EXISTS (80) .
OPEN_ALWAYS (4) — всегда открывать файл. Если файл существует, то функция завершится успешно, а код последней ошибки будет установлен в значение ERROR_ALREADY_EXISTS (183) . Если же файл не существует и указан верный путь, то создастся новый файл, а значение код последней ошибки будет установлено в 0.
OPEN_EXISTING (3) — открыть файл или устройство, только если файл или устройство существуют. Если объект не найден, то вернется код ошибки ERROR_FILE_NOT_FOUND (2) .
TRUNCATE_EXISTING (5) — открыть файл и урезать его размер до 0 байт. Если файл не существует, то вернется код ошибки ERROR_FILE_NOT_FOUND (2).
dwFlagsAndAttributes:cardinal — сочетание флагов и атрибутов файла или устройства. Значения обычно начинаются с FILE_ATTRIBUTE_* или FILE_FLAG_*. Наиболее часто используется значение FILE_ATTRIBUTE_NORMAL. При использовании функции CreateFile совместно с ReadDirectoryChangesW необходимо использовать также флаг FILE_FLAG_BACKUP_SEMANTICS.
hTemplateFile: THandle — дескриптор временного файла с режимом доступа GENERIC_READ. Значение этого параметра может быть равно nil.

ReadDirectoryChangesW

hDirectory:THandle — дескриптор директории за которой будет проводится наблюдение. Это значение мы получим, выполнив CreateFile.
lpBuffer:pointer — указатель на буфер в который буду записываться обнаруженные изменения. Структура записей в буфере соответствует структуре FILE_NOTIFY_INFORMATION (см. описание ниже). Буфер может записываться как синхронно, так и асинхронно в зависимости от заданных параметров.
nBufferLength:cardinal — размер буфера lpBuffer в байтах.
bWatchSubtree:booleanTrue указывает на то, что в результаты мониторинга будут попадать также изменения в подкаталогах.
dwNotifyFilter:cardinal — фильтр событий. Может состоять из одного или нескольких флагов:
FILE_NOTIFY_CHANGE_FILE_NAME (0x00000001) — любое изменение имени файла. Сюда же относятся и операции удаления или добавления файла в каталог или подкаталог.
FILE_NOTIFY_CHANGE_DIR_NAME (0x00000002) — любое изменение имени подкаталога, включая добавление и удаление.
FILE_NOTIFY_CHANGE_ATTRIBUTES (0x00000004) — изменение атрибутов файла или каталога.
FILE_NOTIFY_CHANGE_SIZE (0x00000008) — изменение размера файла. Событие будет вызвано только после того как размер файла изменится и файл будет записан.
FILE_NOTIFY_CHANGE_LAST_WRITE (0x00000010) — изменение времени последней записи в файл или каталог.
FILE_NOTIFY_CHANGE_LAST_ACCESS (0x00000020) — изменение времени последнего доступа к файлу или каталогу.
FILE_NOTIFY_CHANGE_CREATION (0x00000040) — изменение времени создания файла или каталога.
FILE_NOTIFY_CHANGE_SECURITY (0x00000100) — изменение параметров безопасности файла или каталога.
lpBytesReturned:cardinal — в случае синхронного вызова функции этот параметр будет содержать количество байт информации, записанной в буфер. Для асинхронных вызовов значение этого параметра остается неопределенным.
lpOverlapped:POVERLAPPED — указатель на структуру OVERLAPPED, которая поставляет данные, которые будут использоваться во время асинхронной операции. Это значение может быть равным nil .
lpCompletionRoutine:POVERLAPPED_COMPLETION_ROUTINE — указатель на callback-функцию. Этот параметр может устанавливаться в значение nil .

Описания функций есть. Теперь напишем с помощью этих двух функций новую заготовку для Execute нашего потока для мониторинга изменений в директориях и файлах Windows:

ChangeFileExt — Функция Delphi

Пользуюсь стандартной функцией

А работать не хочет. Может что-то не так делаю?

Тему неправильно указал. Надо изменить расширение..
MaTBeu: все правильно указано, где вы увидели ошибку в названии темы?
Спасибо

Так это я и хотел спросить.. чего недостает? Кинул кнопку на форму, а при нажатии в обработчике событий OnClick записал:
ChangeFileExt(‘test.txt’,’.csv’);

В корневой директории лежит файл test.txt, однако расширение меняться не хочет..

25.08.2009, 12:51 #4
25.08.2009, 12:57 #5
Вавел из ГМТУ
Посмотреть профиль
Найти ещё сообщения от Вавел из ГМТУ
25.08.2009, 13:03 #6
25.08.2009, 13:07 #7
25.08.2009, 13:08 #8

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

Что никаких операций с файлами тут нет.
Так что не парься. Это функция для работы со строками а не с файлами.

Функции Delphi модуля SysUtils

Модуль SysUtils

Предлагаем список функций модуля SysUtils, используемого в среде разработки Delphi.

AnsiCompareStr Сравнение двух строк на равенство
AnsiCompareText Сравнение двух строк на равенство
AnsiLowerCase Символы верхнего регистра изменяются в строку со строчными буквамм
AnsiPos Находит позицию одной строки в другой
ChangeFileExt Изменяет расширение имени файла
CompareStr Сравнивает две строки, чтобы увидеть, какая из них больше
CompareText Сравнивает две строки, игнорируя регистр
CreateDir Создаёт директорию
CurrToStr Преобразует денежную величину в строку
CurrToStrF Преобразует денежную величину в строку с форматированием
Date Возвращает текущую дату
DateTimeToFileDate Преобразует значение TDateTime в формат date/time формат файла
DateTimeToStr Конвертирует значение даты и времени TDateTime в строку
DateToStr Преобразует значение даты TDateTime в строку
DayOfWeek Выдает индекс дня недели для значения TDateTime
DeleteFile Удаляет файл, указанный в параметре
DirectoryExists Возвращает true, если указанная директория существует
DiskFree Выдает число свободных байтов на указанном диске
DiskSize Выдает размер указанного диска в байтах
EncodeDate Формирует значение TDateTime из значений года, месяца и дня
EncodeTime Формирует значение TDateTime из значений часа, минуты, секунды и миллисеккунды
ExtractFileDir Извлекает из полного имени файла название папки
ExtractFileDrive Извлекает из полного имени файла название диска
ExtractFileExt Извлекает из полного имени файла его расширение
ExtractFileName Извлекает из полного имени файла краткое имя файла
ExtractFilePath Извлекает из полного имени файла название патча
FileAge Получение дата/время последнего изменения файла, не открывая его
FileDateToDateTime Конвертирует формат даты/времени файла в значение TDateTime
FileExists Возвращает True если указанный файл существует
FileGetAttr Выдаёт атрибуты файла
FileSearch Поиск файла в одной или более папках
FileSetAttr Устанавливает атрибуты файла
FindClose Закрывает успешный FindFirst поиск файла
FindCmdLineSwitch Определяет, был передан некоторый параметр выключатель
FindFirst Находит все файлы, соответствующие маске файла и атрибутов
FindNext Находит следующий файл после успешного FindFirst
FloatToStr Преобразует значение с плавающей запятой в строку
FloatToStrF Преобразует значение с плавающей запятой в строку с форматированием
ForceDirectories Создаёт новый путь каталогов
Format Богатое форматирование чисел и текста в строке
FormatCurr Богатое форматирование значений валюты в строку
FormatDateTime Богатое форматирование переменной TDateTime в строку
FormatFloat Богатое форматирование числа с плавающей запятой в строку
GetCurrentDir Возвращает текущий каталог (диск плюс каталог)
IncMonth Увеличивает TDateTime переменную на некоторое число месяцев
IntToHex Преобразует целое число в шестнадцатеричную строку
IntToStr Конвертирует целое число в строку
IsLeapYear Возвращает True, если данный календарный год високосный
LastDelimiter Находит последнюю позицию указанных символов в строке
LowerCase Изменяет символы верхнего регистра в строке в строчные буквы
Now Выдает текущую дату и время
RemoveDir Позволяет удалить директорию
Rename Переименовка фала
RenameFile Переименование файла или директории
SetCurrentDir Изменяет текущую директорию
StrScan Ищет заданные символы в строке
StrToCurr Преобразует числовую строку в денежное выражение
StrToDate Конвертирует строку с датой в значение типа TDateTime
StrToDateTime Конвертирует строку с датой и временем в значение типа TDateTime
StrToFloat Преобразует числовую строку в значение с плавающей запятой
StrToInt Преобразует строку с целым значением в Integer
StrToInt64 Преобразует строку с целым значением в Int64
StrToInt64Def Преобразует строку с целым значением в Int64, учитывая значение по умолчанию
StrToIntDef Преобразует строку с значение с типом Integer, учитывая значение по умолчанию
Time Возвращает текущее время
TimeToStr Конвертирует значение времени типа TDateTime в строку
Trim Удаляет начальные и конечные пробелы в строке
TrimLeft Удаляет начальные пробелы в строке
TrimRight Удаляет конечные пробелы в строке
UpperCase Изменяет символы в строке из нижнего регистра в верхний
WrapText Добавьте перенос строки в строку, чтобы имитировать перенос слов
Abort Прерывает обработку команд и выходит к последнему исключительному блоку
AppendStr Конкатенация одной строки в конец другой
Beep Делает звук гудка
DateTimeToString Огромные возможности форматирования даты в строку
DecodeDate Извлекает значения года, месяца, дня из TDateTime переменной
DecodeDateTime Разбивает TDateTime переменную на ее части даты/времени
DecodeTime Разбивает значение TDateTime на отдельные значения времени
FreeAndNil Освобождение памяти объекта и установка его в nil
FreeMem Освобождает память, используемую переменной
GetLocaleFormatSettings Получает региональные значения для безопасных потоков функций.
ReplaceDate Изменяет только часть даты TDateTime переменной
ReplaceTime Изменяет только часть времени TDateTime переменной

Delphi программирование

В предыдущем посте мы остановились на том что разработали небольшое приложение, которое проводило мониторинг изменений в определенной директории и, в случае обнаружения какого-либо изменения, «сигналило» нам. Для организации мониторинга мы использовали поток (TThread) в котором использовалось три взаимосвязанные функции Windows: FindFirstChangeNotification , FindNextChangeNotification и FindCloseChangeNotification .

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

Итак, сначала вспомним как выглядел Execute нашего потока:

Здесь мы получили всего один хэндл события с помощью которого отслеживали сразу несколько возможных изменений в директории — изменение имени файла, изменение имени директории и изменение размера. Так как хэндл был всего один, то нам вполне достаточно было использовать функцию WaitForSingleObject для ожидания события. Но нам ведь никто не запрещает получить не один, а, например, три хэндла событий каждое из которых будет срабатывать на конкретно заданное изменение? Давайте сделаем это, а заодно и разберемся с работой ещё одной функции для обработки событий. Итак, пишем новый Execute потока:

В приведенном выше Execute мы определили массив на 3 элемента, где каждый элемент — это хэндл события рассчитанного на определенный вид изменений в директории. Таким образом мы получили возможность пусть и не полностью, но конкретизировать изменения. Так как мы планируем ожидать несколько событий, то вместо метода WaitForSingleObject мы воспользовались методом WaitForMultipleObjects , который в MSDN имеет следующее описание:

WaitForMultipleObjects

nCount: DWORD — количество хэндлов событий. Этот параметр не может быть равен нулю. Максимальное количество ожидаемых событий ограничивается значением константы MAXIMUM_WAIT_OBJECTS = 64 ;
lpHandles: PWOHandleArray — указатель на массив дескрипторов. Массив не должен содержать повторяющихся THandle .
bWaitAll: boolean — если значение этого параметра равно True , то функция вернет значение только в том случае, если будет получен сигнал от всех событий хэндлы которых определены в массиве. В случае, если значение флага равно False , то функция будет возвращать значения при срабатывание любого из событий, при этом возвращаемое значение будет указывать на объект, который изменил состояние.
dwMilliseconds:DWORD — время в мс ожидания события. Если значение этого параметра равно INFINITE , то функция будет возвращать значение только при срабатывании одного из событий.

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

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

Использование функций CreateFile и ReadDirectoryChangesW

Прежде, чем перейдем к примеру на Delphi, посмотрим на описание этих двух функций.
Функция CreateFile создает или открывает файл или другое устройство ввода/вывода (файл, файловый поток, каталог и т.д.

CreateFile

lpFileName: PChar — имя файла или устройства ввода/вывода, которое будет создано или открыто.
dwDesiredAccess: cardinal — флаг, определяющий режим доступа к файлу или устройству (чтение, запись, чтение и запись). Наиболее часто используемые значения GENERIC_READ , GENERIC_WRITE , или сразу оба ( GENERIC_READ or GENERIC_WRITE ).
dwShareMode:cardinal — один или несколько флагов, определяющих режим совместного доступа к файлу или устройству. Для установки значений могут использоваться следующие константы:
FILE_SHARE_DELETE (0x00000004) — разрешает другим процессам получать доступ к файлу или устройству в том числе и выполнять операции удаления.
FILE_SHARE_READ (0x00000001) — разрешает другим процессам выполнять операции чтения.
FILE_SHARE_WRITE (0x00000002) — разрешает другим процессам выполнять операции записи.
lpSecurityAttributes:PSecurityAttributes — указатель на структуру SECURITY_ATTRIBUTES , содержащую дополнительные параметры безопасности работы с файлом или устройством. Этому параметру можно присваивать значение nil.
dwCreationDisposition:cardinal — флаг, который определяет какие действия следует применить к файлу или устройству. Для всех устройств, кроме файлов, этот параметр обычно устанавливается в значение OPEN_EXISTING . Также может принимать следующие значения:
CREATE_ALWAYS (2) — всегда создавать новый файл. Если файл уже существует и доступен для записи, то код последней ошибки устанавливается в значение ERROR_ALREADY_EXISTS (183) . Если файл не существует и указан правильный к нему путь, то создается новый файл. а код последней ошибки устанавливается в 0.
CREATE_NEW (1) — создать новый файл. Если файл с таким именем уже существует, то вернется код ошибки ERROR_FILE_EXISTS (80) .
OPEN_ALWAYS (4) — всегда открывать файл. Если файл существует, то функция завершится успешно, а код последней ошибки будет установлен в значение ERROR_ALREADY_EXISTS (183) . Если же файл не существует и указан верный путь, то создастся новый файл, а значение код последней ошибки будет установлено в 0.
OPEN_EXISTING (3) — открыть файл или устройство, только если файл или устройство существуют. Если объект не найден, то вернется код ошибки ERROR_FILE_NOT_FOUND (2) .
TRUNCATE_EXISTING (5) — открыть файл и урезать его размер до 0 байт. Если файл не существует, то вернется код ошибки ERROR_FILE_NOT_FOUND (2).
dwFlagsAndAttributes:cardinal — сочетание флагов и атрибутов файла или устройства. Значения обычно начинаются с FILE_ATTRIBUTE_* или FILE_FLAG_*. Наиболее часто используется значение FILE_ATTRIBUTE_NORMAL . При использовании функции CreateFile совместно с ReadDirectoryChangesW необходимо использовать также флаг FILE_FLAG_BACKUP_SEMANTICS .
hTemplateFile: THandle — дескриптор временного файла с режимом доступа GENERIC_READ . Значение этого параметра может быть равно nil.

ReadDirectoryChangesW

hDirectory:THandle — дескриптор директории за которой будет проводится наблюдение. Это значение мы получим, выполнив CreateFile .
lpBuffer:pointer — указатель на буфер в который буду записываться обнаруженные изменения. Структура записей в буфере соответствует структуре FILE_NOTIFY_INFORMATION (см. описание ниже). Буфер может записываться как синхронно, так и асинхронно в зависимости от заданных параметров.
nBufferLength:cardinal — размер буфера lpBuffer в байтах.
bWatchSubtree:boolean — True указывает на то, что в результаты мониторинга будут попадать также изменения в подкаталогах.
dwNotifyFilter:cardinal — фильтр событий. Может состоять из одного или нескольких флагов:
FILE_NOTIFY_CHANGE_FILE_NAME (0x00000001) — любое изменение имени файла. Сюда же относятся и операции удаления или добавления файла в каталог или подкаталог.
FILE_NOTIFY_CHANGE_DIR_NAME (0x00000002) — любое изменение имени подкаталога, включая добавление и удаление.
FILE_NOTIFY_CHANGE_ATTRIBUTES (0x00000004) — изменение атрибутов файла или каталога.
FILE_NOTIFY_CHANGE_SIZE (0x00000008) — изменение размера файла. Событие будет вызвано только после того как размер файла изменится и файл будет записан.
FILE_NOTIFY_CHANGE_LAST_WRITE (0x00000010) — изменение времени последней записи в файл или каталог.
FILE_NOTIFY_CHANGE_LAST_ACCESS (0x00000020) — изменение времени последнего доступа к файлу или каталогу.
FILE_NOTIFY_CHANGE_CREATION (0x00000040) — изменение времени создания файла или каталога.
FILE_NOTIFY_CHANGE_SECURITY (0x00000100) — изменение параметров безопасности файла или каталога.
lpBytesReturned:cardinal — в случае синхронного вызова функции этот параметр будет содержать количество байт информации, записанной в буфер. Для асинхронных вызовов значение этого параметра остается неопределенным.
lpOverlapped:POVERLAPPED — указатель на структуру OVERLAPPED , которая поставляет данные, которые будут использоваться во время асинхронной операции. Это значение может быть равным nil .
lpCompletionRoutine:POVERLAPPED_COMPLETION_ROUTINE — указатель на callback-функцию. Этот параметр может устанавливаться в значение nil .

Описания функций есть. Теперь напишем с помощью этих двух функций новую заготовку для Execute нашего потока для мониторинга изменений в директориях и файлах Windows:

В приведенном выше коде присутствуют переменные, которые мы ранее не использовали в работе, поэтому приведу дополнительно их описание:

Теперь при изменении в директории будет срабатывать событие по которому мы будем получать в буфер информацию по проведенным изменениям. Но получить информацию — это одно, а разобрать е и предоставить в удобном для нас виде — другое. Поэтому я специально вынес весь разбор содержимого буфера в отдельный метод FindChanges. Прежде, чем рассмотрим этот метод немного отвлечемся от Delphi и снова вернемся к MSDN, а именно — посмотрим, что представляет из себя структура FILE_NOTIFY_INFORMATION.

FILE_NOTIFY_INFORMATION

Так как в Delphi описания этой структуры я не обнаружил, но приведу е описание прямо из MSDN:

NextEntryOffset — сдвиг в байтах, начиная с которого начинается следующая запись. Если это значение равно нулю, то текущая запись является последней.
Action — сдержит информацию по произведенным изменениям и может принимать одно из следующих значений:
FILE_ACTION_ADDED (0x00000001) — новый файл был добавлен в директорию или поддиректорию
FILE_ACTION_REMOVED (0x00000002) — файл или директория были удалены
FILE_ACTION_MODIFIED (0x00000003) — файл был изменен
FILE_ACTION_RENAMED_OLD_NAME (0x00000004) — файл или директория были переименованы и FileName содержит старое имя файла или директории
FILE_ACTION_RENAMED_NEW_NAME (0x00000005) — файл или директория были переименованы и FileName содержит новое имя файла или директории
FileNameLength — длина имени файла.
FileName[1] — указатель на строку, содержащую имя файла.
То, что структура FILE_NOTIFY_INFORMATION не определена ещё не значит, что мы её не способны определить сами. Давайте определим в модуле Monitor.pas следующий тип данных:

Теперь остается только рассмотреть метод FindChanges:

В целом состав этого метода будет зависеть от ваших целей, поэтому я не стал перегружать исходник какими-то своими переменными и дополнительными методами, но думаю, что смысл чтения изменений будет Вам понятен.
На этом рассмотрение темы » Мониторинг изменений в директориях и файлах средствами Delphi » можно было бы считать законченным, но есть несколько моментов, о которых следует сказать. В целом можно отметить, что использование этого способа контроля изменений в директориях вместо использования всякого рада таймеров, речь о которых шла в первой части, является более предпочтительным и правильным выбором. Однако иногда можно столкнуться с некоторыми моментами, когда решения проблемы не «лежит на поверхности». К примеру, может возникнуть проблема отслеживания изменений в файлах Microsoft Office. Дело в том, что Word, Excel и PowerPoint, несмотря на то, что относятся к одному пакету программ, сохраняют свои файлы разными способами. Тот же Word при открытии файла создает временный скрытый файл, куда заносятся изменения, а после нажатия кнопки «Сохранить», вначале копирует всё содержимое файла в новый временный файл и только потом переименовывает его. У Excel алгоритм сохранения и работы с файлом ещё более «замороченный». И все эти создания временных файлов, пересохранения и переименования временных файлов каждый раз будут «провоцировать» наш поток выдавать новые и новые записи по изменениям. И тут, по крайней мере пока, я ещё не нашел более-менее универсального способа «отсеивания» лишних событий на этапе их получения, кроме как воспользоаться способом представленным в самом первом примере этого поста, т.е. разбивать фильтр на несколько событий и каждое событие анализировать по отдельности. Так что, если у Вас есть более правильное решение вопроса » Как правильно отлавливать изменение в файле MS Office? «, то буду благодарен, если предложите его решение в комментариях к этому посту.

25.08.2009, 13:12 #9
Популярные статьи
Информационная безопасность Microsoft Офисное ПО Антивирусное ПО и защита от спама Eset Software


Бестселлеры
Курсы обучения «Atlassian JIRA — система управления проектами и задачами на предприятии»
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год. Электронный ключ
Microsoft Windows 10 Профессиональная 32-bit/64-bit. Все языки. Электронный ключ
Microsoft Office для Дома и Учебы 2020. Все языки. Электронный ключ
Курс «Oracle. Программирование на SQL и PL/SQL»
Курс «Основы TOGAF® 9»
Microsoft Windows Professional 10 Sngl OLP 1 License No Level Legalization GetGenuine wCOA (FQC-09481)
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год. Электронный ключ
Windows Server 2020 Standard
Курс «Нотация BPMN 2.0. Ее использование для моделирования бизнес-процессов и их регламентации»
Антивирус ESET NOD32 Antivirus Business Edition
Corel CorelDRAW Home & Student Suite X8

О нас
Интернет-магазин ITShop.ru предлагает широкий спектр услуг информационных технологий и ПО.

На протяжении многих лет интернет-магазин предлагает товары и услуги, ориентированные на бизнес-пользователей и специалистов по информационным технологиям.

Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.

ChangeFileExt — Функция Delphi

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

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

В зависимости от типа элементов различают три вида файла:

  • файл из элементов фиксированного размера; элементами такого файла чаще всего являются записи;
  • файл из элементов переменного размера (нетипизированный файл); такой файл рассматривается просто как последовательность байтов;
  • текстовый файл; элементами такого файла являются текстовые строки.

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

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

К моменту такого объявления тип TPerson должен быть уже описан (см. выше).

Объявление переменной для работы с нетипизированным файлом выполняется с помощью отдельного слова file:

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

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

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

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

В результате этого действия поля файловой переменной F инициализируются начальными значениями. При этом в поле имени файла заносится строка ‘MyFile.txt’.

Так как файла еще нет на диске, его нужно создать:

Теперь запишем в файл несколько строк текста. Это делается с помощью хорошо вам знакомых процедур Write и Writeln:

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

После работы файл должен быть закрыт:

Рассмотрим теперь, как прочитать содержимое текстового файла. После инициализации файловой переменной (AssignFile) файл открывается с помощью процедуры Reset:

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

Так как обычно размер файла заранее не известен, перед каждой операцией чтения вызывается функция Eof, которая возвращает True, если достигнут конец файла.

Внимание! Текстовые файлы можно открывать только для записи или только для чтения, но не для того и другого одновременно. Для того чтобы сначала записать текстовый файл, а потом прочитать, его нужно закрыть после записи и снова открыть, но уже только для чтения.

Стандартные подпрограммы управления файлами

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

  • AssignFile(var F; FileName: string) — связывает файловую переменную F и файл, имя которого указано в FileName.
  • Reset(var F [: File; RecSize: Word ] ) — открывает существующий файл. Если открывается нетипизированный файл, то RecSize задает размер элемента файла.
  • Rewrite(var F [: File; RecSize: Word ] ) — создает и открывает новый файл.
  • Append(var F: TextFile) — открывает текстовый файл для добавления текста.
  • Read(F, V1 [, V2, . Vn ]) — начиная с текущей позиции, читает из типизированного файла подряд расположенные элементы в переменные V1, V2, . Vn.
  • Read(var F: TextFile; V1 [, V2, . Vn ] ) — начиная с текущей позиции, читает из текстового файла символы или строки в переменные V1, V2, . Vn.
  • Write(F, V1 [, V2, . Vn ]) — начиная с текущей позиции, записывает в типизированный файл значения V1, V2, . Vn.
  • Write(var F: TextFile; V1 [, V2, . Vn ] ) — начиная с текущей позиции указателя чтения-записи, записывает в текстовый файл значения V1, V2, . Vn.
  • CloseFile(var F) — закрывает ранее открытый файл.
  • Rename(var F; NewName: string) — переименовывает неоткрытый файл F любого типа. Новое имя задается в NewName.
  • Erase(var F) — удаляет неоткрытый внешний файл любого типа, заданный переменной F.
  • Seek(var F; NumRec: Longint) — устанавливает позицию чтения-записи на элемент с номером NumRec; F — типизированный или нетипизированный файл.
  • SetTextBuf(var F: TextFile; var Buf [; Size: Word]) — назначает текстовому файлу F новый буфер ввода-вывода Buf объема Size.
  • SetLineBreakStyle(var T: Text; Style: TTextLineBreakStyle) — устанавливает способ переноса строк в файле (одиночный символ #10 или пара символов #13#10).
  • Flush(var F: TextFile) — записывает во внешний файл все символы, переданные в буфер для записи.
  • Truncate(var F) — урезает файл, уничтожая все его элементы, начиная с текущей позиции.
  • IOResult: Integer — возвращает код, характеризующий результат (была ошибка или нет) последней операции ввода-вывода.
  • FilePos(var F): Longint — возвращает для файла F текущую файловую позицию (номер элемента, на которую она установлена, считая от нуля). Не используется с текстовыми файлами.
  • FileSize(var F): Longint — возвращает число компонент в файле F. Не используется с текстовыми файлами.
  • Eoln(var F: Text): Boolean — возвращает булевское значение True, если текущая позиция чтения-записи находится на маркере конца строки. Если параметр F не указан, функция применяется к стандартному устройству ввода с именем Input.
  • Eof(var F): Boolean — возвращает булевское значение True, если текущая позиция чтения-записи находится сразу за последним элементом, и False в противном случае.
  • SeekEoln(var F: Text): Boolean — возвращает True при достижении маркера конца строки. Все пробелы и знаки табуляции, предшествующие маркеру, пропускаются.
  • SeekEof(var F: Text): Boolean — возвращает значение True при достижении маркера конца файла. Все пробелы и знаки табуляции, предшествующие маркеру, пропускаются.

Для работы с нетипизированными файлами используются процедуры BlockRead и BlockWrite. Единица обмена

  • BlockRead(var F: File; var Buf; Count: Word [; Result: Word] ) — считывает из файла F определенное число блоков в память, начиная с первого байта переменной Buf. Параметр Buf представляет любую переменную, используемую для накопления информации из файла F. Параметр Count задает число считываемых блоков. Параметр Result является необязательным и содержит после вызова процедуры число действительно считанных записей. Использование параметра Result подсказывает, что число считанных блоков может быть меньше, чем задано параметром Count.
  • BlockWrite(var F: File; var Buf; Count: Word [; Result: Word]) — предназначена для быстрой передачи в файл F определенного числа блоков из переменной Buf. Все параметры процедуры BlockWrite аналогичны параметрам процедуры BlockRead.
  • ChDir(const S: string) — устанавливает текущий каталог.
  • CreateDir(const Dir: string): Boolean — создает новый каталог на диске.
  • MkDir(const S: string) — аналог функции CreateDir. Отличие в том, что в случае ошибки при создании каталога функция MkDir создает исключительную ситуацию.
  • DeleteFile(const FileName: string): Boolean — удаляет файл с диска.
  • DirectoryExists(const Directory: string): Boolean — проверяет, существует ли заданный каталог на диске.
  • FileAge(const FileName: string): Integer — возвращает дату и время файла в числовом системно-зависимом формате.
  • FileExists(const FileName: string): Boolean — проверяет, существует ли на диске файл с заданным именем.
  • FileIsReadOnly(const FileName: string): Boolean — проверяет, что заданный файл можно только читать.
  • FileSearch(const Name, DirList: string): string — осуществляет поиск заданого файла в указанных каталогах. Список каталогов задается параметром DirList; каталоги разделяются точкой с запятой для операционной системы Windows и запятой для операционной системы Linux. Функция возвращает полный путь к файлу.
  • FileSetReadOnly(const FileName: string; ReadOnly: Boolean): Boolean — делает файл доступным только для чтения.
  • FindFirst/FindNext/FindClose
  • ForceDirectories(Dir: string): Boolean — создает новый каталог на диске. Позволяет одним вызовом создать все каталоги пути, заданного параметром Dir.
  • GetCurrentDir: string — возвращает текущий каталог.
  • SetCurrentDir(const Dir: string): Boolean — устанавливает текущий каталог. Если это сделать невозможно, функция возвращет значение False.
  • RemoveDir(const Dir: string): Boolean — удаляет каталог с диска; каталог должен быть пустым. Если удалить каталог невозможно, функция возвращет значение False.
  • RenameFile(const OldName, NewName: string): Boolean — изменяет имя файла. Если это сделать невозможно, функция возвращет значение False.
  • ChangeFileExt(const FileName, Extension: string): string — возвращает имя файла с измененным расширением.
  • ExcludeTrailingPathDelimiter(const S: string): string — отбрасывает символ-разделитель каталогов (символ ‘/’ — для Linux и ‘\’ — для Windows), если он присутствует в конце строки.
  • IncludeTrailingPathDelimiter(const S: string): string — добавляет символ-разделитель каталогов (символ ‘/’ — для Linux и ‘\’ — для Windows), если он отсутствует в конце строки.
  • ExpandFileName(const FileName: string): string — возвращает полное имя файла (с абсолютным путем) по неполному имени.
  • ExpandUNCFileName(const FileName: string): string — возвращает полное сетевое имя файла (с абсолютным сетевым путем) по неполному имени. Для операционной системы Linux эта функция эквивалентна функции ExpandFileName.
  • ExpandFileNameCase(const FileName: string; out MatchFound: TFilenameCaseMatch): string — возвращает полное имя файла (с абсолютным путем) по неполному имени, допуская несовпадения заглавных и строчных букв в имени файла для тех файловых систем, которые этого не допускают (например, файловая система ОС Linux).
  • ExtractFileDir(const FileName: string): string — выделяет путь из полного имени файла; путь не содержит в конце символ-разделитель каталогов.
  • ExtractFilePath(const FileName: string): string — выделяет путь из полного имени файла; путь содержит в конце символ-разделитель каталогов.
  • ExtractRelativePath(const BaseName, DestName: string): string — возвращает относительный путь к файлу DestName, отсчитанный от каталога BaseName. Путь BaseName должен заканчиваться символом-разделителем каталогов.
  • ExtractFileDrive(const FileName: string): string — выделяет имя диска (или сетевого каталога) из имени файла. Для операционной системы Linux функция возвращает пустую строку.
  • ExtractFileExt(const FileName: string): string — выделяет расширение файла из его имени.
  • ExtractFileName(const FileName: string): string — выделяет имя файла, отбрасывая путь к нему.
  • IsPathDelimiter(const S: string; Index: Integer): Boolean — проверяет, является ли символ S[Index] разделителем каталогов.
  • MatchesMask(const Filename, Mask: string): Boolean — проверяет, удовлетворяет ли имя файла заданной маске.

Указатели

Понятие указателя

Все переменные, с которыми мы имели дело, известны уже на этапе компиляции. Однако во многих задачах нужны переменные, которые по мере необходимости можно создавать и удалять во время выполнения программы. С этой целью в языке Delphi организована поддержка так называемых указателей, для которых введен специальный тип данных Pointer.

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

Переменная P занимает 4 байта и может содержать адрес любого участка памяти, указывая на байты со значениями любых типов данных: Integer, Real, string, record, array и других. Чтобы инициализировать переменную P, присвоим ей адрес переменной N. Это можно сделать двумя эквивалентными способами:

В дальнейшем мы будем использовать более краткий и удобный второй способ.

Если некоторая переменная P содержит адрес другой переменной N, то говорят, что P указывает на N. Графически это обозначается стрелкой, проведенной из P в N (рисунок 12 выполнен в предположении, что N имеет значение 10):

Рисунок 12. Графическое изображение указателя P на переменную N

Теперь мы можем изменить значение переменной N, не прибегая к идентификатору N. Для этого слева от оператора присваивания запишем не N, а P вместе с символом ^:

Символ ^, записанный после имени указателя, называется оператором доступа по адресу. В данном примере переменной, расположенной по адресу, хранящемуся в P, присваивается значение 10. Так как в переменную P мы предварительно занесли адрес N, данное присваивание приводит к такому же результату, что и

Однако в примере с указателем мы умышленно допустили одну ошибку. Дело в том, что переменная типа Pointer может содержать адреса переменных любого типа, не только Integer. Из-за сильной типизации языка Delphi перед присваиванием мы должны были бы преобразовать выражение P^ к типу Integer:

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

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

PInteger — это указательный тип данных. Чтобы отличать указательные типы данных от других типов, будем назначать им идентификаторы, начинающиеся с буквы P (от слова Pointer). Объявление указательного типа данных является единственным способом введения указателей на составные переменные, такие как массивы, записи, множества и другие. Например, объявление типа данных для создания указателя на некоторую запись TPerson может выглядеть так:

Переменная P, описанная с типом данных PPerson, является указателем и может содержать адрес любой переменной типа TPerson. Впредь все указатели мы будем вводить через соответствующие указательные типы данных. Типом Pointer будем пользоваться лишь тогда, когда это действительно необходимо или оправдано.

Динамическое распределение памяти

После объявления в секции var указатель содержит неопределенное значение. Поэтому переменные-указатели, как и обычные переменные, перед использованием нужно инициализировать. Отсутствие инициализации указателей является наиболее распространенной ошибкой среди новичков. Причем если использование обычных неинициализированных переменных приводит просто к неправильным результатам, то использование неинициализированных указателей обычно приводит к ошибке «Access violation» (доступ к неверному адресу памяти) и принудительному завершению приложения.

Один из способов инициализации указателя состоит в присваивании ему адреса некоторой переменной соответствующего типа. Этот способ мы уже рассмотрели. Второй способ состоит в динамическом выделении участка памяти под переменную соответствующего типа и присваивании указателю его адреса. Работа с динамическими переменными и есть основное назначение указателей. Размещение динамических переменных производится в специальной области памяти, которая называется Heap (куча). Ее размер равен размеру свободной памяти компьютера.

Для размещения динамической переменной вызывается стандартная процедура

Она выделяет требуемый по размеру участок памяти и заносит его адрес в переменную-указатель P. В следующем примере создаются 4 динамических переменных, адреса которых присваиваются переменным-указателям P1, P2, P3 и P4:

Далее по адресам в указателях P1, P2, P3 и P4 можно записать значения:

В таком контексте динамические переменные P1^, P2^, P3^ и P4^ ничем не отличаются от обычных переменных соответствующих типов. Операции над динамическими переменными аналогичны подобным операциям над обычными переменными. Например, следующие операторы могут быть успешно откомпилированы и выполнены:

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

Например, в приведенной выше программе явно не хватает следующих строк:

После выполнения данных утверждений указатели P1, P2, P3 и P4 опять перестанут быть связаны с конкретными адресами памяти. В них будут случайные значения, как и до обращения к процедуре New. Не стоит делать попытки присвоить значения переменным P1^, P2^, P3^ и P4^, ибо в противном случае это может привести к нарушению нормальной работы программы.

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

Поэтому следует четко придерживаться последовательности действий при работе с динамическими переменными:

  • создать динамическую переменную;
  • выполнить с ней необходимые действия;
  • разрушить динамическую переменную.

Операции над указателями

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

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

Использование одинаковых значений в разных указателях открывает некоторые интересные возможности. Так после оператора P3 := P1 изменение значения переменной P3^ будет равносильно изменению значения P1^.

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

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

Чаще всего операции сравнения указателей используются для проверки того, связан ли указатель с динамической переменной. Если еще нет, то ему следует присвоить значение nil (зарезервированное слово):

Установка P1 в nil однозначно говорит о том, что указателю не выделена динамическая память. Если всем объявленным указателям присвоить значение nil, то внутри программы можно легко выполнить тестирование наподобие этого:

Процедуры GetMem и FreeMem

Для динамического распределения памяти служат еще две тесно взаимосвязанные процедуры: GetMem и FreeMem. Подобно New и Dispose, они во время вызова выделяют и освобождают память для одной динамической переменной:

  • GetMem(var P: Pointer; Size: Integer) — создает в динамической памяти новую динамическую переменную c заданным размером Size и присваивает ее адрес указателю P. Переменная-указатель P может указывать на данные любого типа.
  • FreeMem(var P: Pointer [; Size: Integer] ) — освобождает динамическую переменную. Если в программе используется этот способ распределения памяти, то вызовы GetMem и FreeMem должны соответствовать друг другу. Обращения к GetMem и FreeMem могут полностью соответствовать вызовам New и Dispose.

Следующий отрывок программы даст тот же самый результат:

С помощью процедуры GetMem одной переменной-указателю можно выделить разное количество памяти в зависимости от потребностей. В этом состоит ее основное отличие от процедуры New.

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

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

ReallocMem(var P: Pointer; Size: Integer) — освобождает блок памяти по значению указателя P и выделяет для указателя новый блок памяти заданного размера Size. Указатель P может иметь значение nil, а параметр Size — значение 0, что влияет на работу процедуры:

  • если P = nil и Size = 0, процедура ничего не делает;
  • если P = nil и Size <> 0, процедура выделяет новый блок памяти заданного размера, что соответствует вызову процедуры GetMem.
  • если P <>nil и Size = 0, процедура освобождает блок памяти, адресуемый указателем P и устанавливает указатель в значение nil. Это соответствует вызову процедуры FreeMem, с той лишь разницей, что FreeMem не очищает указатель;
  • если P <>nil и Size <> 0, процедура перевыделяет память для указателя P. Размер нового блока определяется значением Size. Данные из прежнего блока копируются в новый блок. Если новый блок больше прежнего, то приращенный участок остается неинициализированным и содержит случайные данные.

Представление строк в памяти

В некоторых случаях динамическая память неявно используется программой, например для хранения строк. Длина строки может варьироваться от нескольких символов до миллионов и даже миллиардов (теоретический предел равен 2 ГБ). Тем не менее, работа со строками в программе осуществляется так же просто, как работа с переменными простых типов данных. Это возможно потому, что компилятор автоматически генерирует код для выделения и освобождения динамической памяти, в которой хранятся символы строки. Но что стоит за такой простотой? Не идет ли она в ущерб эффективности? С полной уверенностью можем ответить, что эффективность программы не только не снижается, но даже повышается.

Физически переменная строкового типа представляет собой указатель на область динамической памяти, в которой размещаются символы. Например, переменная S на самом деле представляет собой указатель и занимает всего четыре байта памяти (SizeOf(S) = 4):

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

За оператором присваивания строковых переменных на самом деле кроется копирование значения указателя, а не копирование блока памяти, в котором хранятся символы.

Такой подход весьма эффективен как с точки зрения производительности, так и с точки зрения экономного использования оперативной памяти. Его главная проблема состоит в том, чтобы обеспечить удаление блока памяти, содержащего символы строки, когда все адресующие его строковые переменные прекращают свое существование. Эта проблема эффективно решается с помощью механизма подсчета количества ссылок (reference counting). Для понимания его работы рассмотрим формат хранения строк в памяти подробнее.

Пусть в программе объявлены две строковые переменные:

И пусть в программе существует оператор, присваивающий переменной S1 значение некоторой функции:

Для хранения символов строки S1 по окончании ввода будет выделен блок динамической памяти. Формат этого блока после ввода значения ‘Hello’ показан на рисунке 13:

Рисунок 13. Представление строковых переменных в памяти

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

Если в программе встречается оператор присваивания значения одной строковой переменной другой строковой переменной,

то, как мы уже сказали, копия строки в памяти не создается. Копируется только адрес, хранящийся в строковой переменной, и на единицу увеличивается количество ссылок на строку (рисунок 14).

Рисунок 14. Результат копирования строковой переменной S1 в строковую переменную S2

При присваивании переменной S1 нового значения (например, пустой строки):

количество ссылок на предыдущее значение уменьшается на единицу (рисунок 15).

Рисунок 15. Результат присваивания строковой переменной S1 нового значения (пустой строки)

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

Интересно, а что происходит при изменении символов строки, с которой связано несколько строковых переменных? Правила семантики языка требуют, чтобы две строковые переменные были логически независимы, и изменение одной из них не влияло на другую. Это достигается с помощью механизма копирования при записи (copy-on-write).

Например, в результате выполнения операторов

получим следующую картину в памяти (рисунок 16):

Рисунок 16. Результат изменения символа в строке S1

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

Все, что было сказано выше о представлении в памяти строк, относится только к строкам формата AnsiString. Строки формата WideString тоже хранятся в динамической памяти, но для них не поддерживаются механизм подсчета количества ссылок и механизм копирования по записи. Операция присваивания строковых переменных формата WideString означает выделение нового блока динамической памяти и полное копирование в него всех символов исходной строки. Что же касается коротких строк, то они целиком хранятся по месту объявления: или в области данных программы (если это глобальные переменные), или на стеке (если это локальные переменные). Динамическая память вообще не используется для хранения коротких строк.

Динамические массивы

Одним из мощнейших средств языка Delphi являются динамические массивы. Их основное отличие от обычных массивов заключается в том, что они хранятся в динамической памяти. Этим и обусловлено их название. Чтобы понять, зачем они нужны, рассмотрим пример:

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

На этапе написания программы невозможно предугадать, какие именно объемы данных захочет обрабатывать пользователь. Тем не менее, Вам придется ответить на два важных вопроса:

  • На какое количество элементов объявить массив?
  • Что делать, если пользователю все-таки понадобится большее количество элементов?

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

Такое решение проблемы является неоптимальным. Если пользователю необходимо всего 10 элементов, программа работает без проблем, но всегда использует объем памяти, необходимый для хранения 100 элементов. Память, отведенная под остальные 90 элементов, не будет использоваться ни Вашей программой, ни другими программами (по принципу «сам не гам и другому не дам»). А теперь представьте, что все программы поступают таким же образом. Эффективность использования оперативной памяти резко снижается.

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

Динамический массив объявляется без указания границ:

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

Работа с динамическими массивами напоминает работу с длинными строками. В частности, создание динамического массива (выделение памяти для его элементов) осуществляется той же процедурой, которой устанавливается длина строк — SetLength.

Изменение размера динамического массива производится этой же процедурой:

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

При уменьшении размера динамического массива лишние элементы теряютяся.

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

Определение количества элементов производится с помощью функции Length:

Элементы динамического массива всегда индексируются от нуля. Доступ к ним ничем не отличается от доступа к элементам обычных статических массивов:

К динамическим массивам, как и к обычным массивам, применимы функции Low и High, возвращающие минимальный и максимальный индексы массива соответственно. Для динамических массивов функция Low всегда возвращает 0.

Освобождение памяти, выделенной для элементов динамического массива, осуществляется установкой длины в значение 0 или присваиванием переменной-массиву значения nil (оба варианта эквивалентны):

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

Также, как и при работе со строками, при присваивании одного динамического массива другому, копия уже существующего массива не создается.

В приведенном примере, в переменную B заносится адрес динамической области памяти, в которой хранятся элементы массива A (другими словами, ссылочной переменной B присваивается значение ссылочной переменной A).

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

Для работы с динамическими массивами вы можете использовать знакомую по строкам функцию Copy. Она возвращает часть массива в виде нового динамического массива.

Не смотря на сильное сходство динамических массивов со строками, у них имеется одно существенное отличие: отсутствие механизма копирования при записи (copy-on-write).

Нуль-терминированные строки

Кроме стандартных строк ShortString и AnsiString, в языке Delphi поддерживаются нуль-терминированные строки языка C, используемые процедурами и функциями Windows. Нуль-терминированная строка представляет собой индексированный от нуля массив ASCII-символов, заканчивающийся нулевым символом #0. Для поддержки нуль-терминированных строк в языке Delphi введены три указательных типа данных:

Типы PAnsiChar и PWideChar являются фундаментальными и на самом деле используются редко. PChar — это обобщенный тип данных, в основном именно он используется для описания нуль-терминированных строк.

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

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

переменная S3 получит адрес уже существующей строки ‘Object Pascal’.

Для удобной работы с нуль-терминированными строками в языке Delphi предусмотрена директива $EXTENDEDSYNTAX. Если она включена (ON), то появляются следующие дополнительные возможности:

  • массив символов, в котором нижний индекс равен 0, совместим с типом PChar;
  • строковые константы совместимы с типом PChar.
  • указатели типа PChar могут участвовать в операциях сложения и вычитания с целыми числами; допустимо также вычитание (но не сложение!) указателей.

В режиме расширенного синтаксиса допустимы, например, следующие операторы:

В языке Delphi существует богатый набор процедур и функций для работы с нуль-терминированными строками (см. справочник по среде Delphi).

Переменные с непостоянным типом значений

Тип данных Variant

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

Значения переменных с типом Variant

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

Значение Unassigned показывает, что переменная является нетронутой, т.е. переменной еще не присвоено значение. Оно автоматически устанавливается в качестве начального значения любой переменной с типом Variant.

Значение Null показывает, что переменная имеет неопределенное значение. Если в выражении участвует переменная со значением Null, то результат всего выражения тоже равен Null.

Переменная с типом Variant занимает в памяти 16 байт. В них хранятся текущее значение переменной (или адрес значения в динамической памяти) и тип этого значения.

Тип значения выясняется с помощью функции

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

Код типа

Значение

Описание

varEmpty $0000 Переменная содержит значение Unassigned.
varNull $0001 Переменная содержит значение Null.
varSmallint $0002 Переменная содержит значение типа Smallint.
varInteger $0003 Переменная содержит значение типа Integer.
varSingle $0004 Переменная содержит значение типа Single.
varDouble $0005 Переменная содержит значение типа Double.
varCurrency $0006 Переменная содержит значение типа Currency.
varDate $0007 Переменная содержит значение типа TDateTime.
varOleStr $0008 Переменная содержит ссылку на строку формата Unicode в динамической памяти.
varDispatch $0009 Переменная содержит ссылку на интерфейс IDispatch (интерфейсы рассмотрены в главе 6).
varError $000A Переменная содержит системный код ошибки.
varBoolean $000B Переменная содержит значение типа WordBool.
varVariant $000C Элемент варьируемого массива содержит значение типа Variant (код varVariant используется только в сочетании с флагом varArray).
varUnknown $000D Переменная содержит ссылку на интерфейс IUnknown (интерфейсы рассмотрены в главе 6).
varShortint $0010 Переменная содержит значение типа Shortint
varByte $0011 Переменная содержит значение типа Byte.
varWord $0012 Переменная содержит значение типа Word
varLongword $0013 Переменная содрежит значение типа Longword
varInt64 $0014 Переменная содержит значение типа Int64
varStrArg $0048 Переменная содержит строку, совместимую со стандартом COM, принятым в операционной системе Windows.
varString $0100 Переменная содержит ссылку на длинную строку.
varAny $0101 Переменная содержит значение любого типа данных технологии CORBA
Флаги
varTypeMask $0FFF Маска для выяснения типа значения.
varArray $2000 Переменная содержит массив значений.
varByRef $4000 Переменная содержит ссылку на значение.

Таблица 10. Коды и флаги варьируемых переменных

позволяет вам преобразовать значение варьируемой переменной к нужному типу, например:

Пока это все, что нужно знать о типе Variant, но мы к нему еще вернемся при обсуждении технологии COM Automation.

Delphi + ассемблер

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

Встроенный ассемблер

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

К встроенному ассемблеру можно обратиться с помощью зарезервированного слова asm, за которым следуют команды ассемблера и слово end:

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

В языке Delphi имеется возможность не только делать ассемблерные вставки, но писать процедуры и функции полностью на ассемблере. В этом случае тело подпрограммы ограничивается словами asm и end (а не begin и end), между которыми помещаются инструкции ассемблера. Перед словом asm могут располагаться объявления локальных констант, типов, и переменных. Например, вот как могут быть реализованы функции вычисления минимального и максимального значения из двух целых чисел:

Обращение к этим функциям имеет привычный вид:

Подключение внешних подпрограмм

Программисту предоставляется возможность подключать к программе или модулю отдельно скомпилированные процедуры и функции, написанные на языке ассемблера или C. Для этого используется директива компилятора $LINK и зарезервированное слово external. Директива указывает подключаемый объектный модуль, а external сообщает компилятору, что подпрограмма внешняя.

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

В модулях внешние подпрограммы подключаются в разделе implementation.

Итоги

Все, что вы изучили, называется языком Delphi. Мы надеемся, что вам понравились стройность и выразительная сила языка. Но это всего лишь основа. Теперь пора подняться на следующую ступень и изучить технику объектно-ориентированного программирования, без которого немыслимо стать профессиональным программистом. Именно этим вопросом в рамках применения объектов в среде Delphi мы и займемся в следующей главе.

Использование файловых переменных

Лабораторная работа №8

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

Задание:

Разработать приложение в среде Delphi, реализующие работу с файлами 4-мя способами: через файловые переменные, с использованием функций Windows API, c использование файловых потоков и с помощью файлов, отображаемых на память. Для всех способов доступа использовать один и тат же файл, одинаковый размер блока данных. Определить время, затрачиваемое программой на обработку файла (результат вывести в поле мемо). Ход обработки файла отобразить с помощью элемента Progress Bar. При нажатии на кнопку старт последовательно выполняются только отмеченные пользователем тесты.

Почему полученные результаты могут меняться? Предложите способ корректного измерения результатов и реализуйте его (подсказка: посмотреть функцию GetThreadTimes).

Отображение файлов на память:

function TfrmFileTest.CopyByMapping:Integer;

Type

TBuf=array[0..1024-1] of Byte;

Var

Begin

if FileExists(DestinationFileName) then DeleteFile(DestinationFileName);

hSrc:=CreateFile(PAnsiChar(SourceFileName),GENERIC_READ,FILE_SHARE_READ ,nil,OPEN_EXISTING ,FILE_ATTRIBUTE_NORMAL,0);

hDst:=CreateFile(PAnsiChar(DestinationFileName),GENERIC_WRITE or GENERIC_READ,FILE_SHARE_WRITE ,nil,CREATE_NEW ,FILE_ATTRIBUTE_NORMAL,0);

prbStatus.Max:=SrcSize div 1024;

for i:=1 to SrcSize div 1024 do

Begin

end;

end;

Использование Windows API:

function TfrmFileTest.CopyByWindowsAPI: integer;

Var

Begin

if FileExists(DestinationFileName) then DeleteFile(DestinationFileName);

hSrc:=CreateFile(PAnsiChar(SourceFileName),GENERIC_READ,FILE_SHARE_READ ,nil,OPEN_EXISTING ,FILE_ATTRIBUTE_NORMAL,0);

hDst:=CreateFile(PAnsiChar(DestinationFileName),GENERIC_WRITE,FILE_SHARE_WRITE ,nil,CREATE_NEW ,FILE_ATTRIBUTE_NORMAL,0);

prbStatus.Max:=FileSize div 1024;

for I:=1 to FileSize div 1024 do

Begin

// вставить операторы для чтения из файла источника и записи в файл приемник

end;

end;

Использование файловых потоков:

Использование файловых переменных:

Генерация тестового файла:

procedure TfrmFileTest.CreateTestFile;

var i,FileLen:integer;

Begin

for i:=1 to FileLen*1024 do

end;

end;

Среда Delphi предоставляет возможность выбрать один из четырех вариантов работы c файлами:

· использование традиционного набора функций работы с файлами, унаследованного от Turbo Pascal;

· использование функций ввода/вывода из Windows API;

· использование потоков (TStream и его потомки);

· использование отображаемых файлов.

Использование файловых переменных

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

Естественно, что сведения о типе хранящихся в файле данных важно изначально задать. Для этого используются специальные файловые переменные, определяющие тип файла. Они делятся на нетипизированные и типизированные.

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

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

var UntypedFile: file;

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

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

var ByteFile: file of byte;

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

type Country = record

end;

var CountryFile: file of Country;

Для работы с текстовыми файлами используется специальная файловая переменная TextFile или Text:

Теперь рассмотрим две самые распространенные операции, выполняемые при работе с файлами. Это чтение и запись. Для их осуществления применяются специальные функции файлового ввода/вывода.

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

  1. Объявить файловую переменную необходимого типа.
  2. При помощи функции AssignFile связать эту переменную с требуемым файлом.
  3. Открыть файл при помощи функций Append, Reset, Rewrite.
  4. Выполнить операции чтения или записи. При этом, в зависимости от сложности задачи и структуры данных, может использоваться целый ряд вспомогательных функций.
  5. Закрыть файл при помощи функции CloseFile.

В качестве примера рассмотрим небольшой фрагмент исходного кода.

var F:TextFile; S:string;

Begin

if OpenDlg.Execute then

else Exit;

while Not EOF(F) do

Begin

end;

Если в диалоге открытия файла OpenDlg был выбран файл, то его имя связывается с файловой переменной F при помощи процедуры AssignFile. В качестве имени файла рекомендуется всегда передавать полное имя файла (включая его маршрут). Как раз в таком виде возвращают результат выбора файла диалоги работы с файлами TOpenDialog, TOpenPictureDiaiog. Затем при помощи процедуры Reset этот файл открывается для чтения и записи.

В цикле выполняется чтение из файла текстовых строк и запись их в компонент TMemo. Процедура Readin осуществляет чтение текущей строки файла и переходит на следующую строку. Цикл выполняется, пока функция EOF не сообщит о достижении конца файла.

После завершения чтения файл закрывается.

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

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

Открытие файла может осуществляться тремя процедурами — в зависимости от типа его дальнейшего использования.

procedure Reset(var F: File [; RecSize: Word ]); открывает существующий файл для чтения и записи, текущая позиция устанавливается на первой строке файла.
procedure Append(var F: Text); открывает файл для записи информации после его последней строки, текущая позиция устанавливается на конец файла.
procedure Rewrite(var F: File [; RecSize: Word ]); создает новый файл и открывает его, текущая позиция устанавливается в начало файла. Если файл с таким именем уже существует, то он перезаписывается. Переменная RecSize используется только при работе с нетипизированными файлами и определяет размер одной записи для операции передачи данных. Если этот параметр опущен, то по умолчанию RecSize равно 128 байт.

Чтение данных из типизированных и текстовых файлов выполняют процедуры Read и Readln. Для записи используются процедуры в файл write и writeln.

procedure Read([var F: Text;] V1 [, V2. Vn]); procedure Read(F, V1 [, V2. Vn]); При одном вызове процедуры можно читать данные в произвольное число переменных. Естественно, что тип переменных должен совпадать с типом файла. При чтении в очередную переменную читается ровно столько байтов из файла, сколько занимает тип данных. В следующую переменную читается столько же байтов, расположенных следом. После выполнения процедуры текущая позиция устанавливается на первом непрочитанном байте. Аналогично работают несколько процедур Read для одной переменной, выполненных подряд.
procedure Readln([ var F: Text; ] VI [, V2. Vn ]); считывает одну строку текстового файла и устанавливает текущую позицию на следующей строке. Если использовать процедуру без переменных v1. .vn, то она просто передвигает текущую позицию на очередную строку файла.
procedure Write([var F: Text; ] PI [, P2. Pn]) ; procedure Writeln([ var F: Text; ] PI [, P2. Pn ]); Параметры P1, P2, . Pn могут быть одним из целых или вещественных типов, одним из строковых типов или логическим типом. Но у них есть возможность дополнительного форматирования при выводе.

Для контроля за текущей позицией в файле применяются две основные функции. Функция EOF(F) возвращает значение True, если достигнут конец файла. Функция EOLN(F) аналогично сигнализирует о достижении конца строки. Естественно, в качестве параметра в функции необходимо передавать файловую переменную.

Процедура procedure Seek(var F; N: Longint); обеспечивает смещение текущей позиции на N элементов. Размер одного элемента в байтах зависит от типа данных файла (от типизированной переменной).

Рассмотрим теперь режим блочного ввода/вывода данных между файлом и областью адресного пространства (буфером). Этот режим отличается значительной скоростью передачи данных, причем скорость пропорциональна размеру одного передаваемого блока — чем больше блок, тем больше скорость.

Для реализации этого режима необходимо использовать только нетипизированные файловые переменные. Размер блока определяется в процедуре открытия файла (Reset, Rewrite). Непосредственно для выполнения операций используются процедуры BlockRead и BlockWrite.

procedure BlockRead(var F:File; var Buf; Count:Integer [; var AmtTransferred:Integer]); выполняет запись блока из файла в буфер. Параметр F ссылается на нетипизированную файловую переменную, связанную с нужным файлом.

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

При использовании блочного чтения или записи размер блока необходимо выбирать таким образом, чтобы он был кратен размеру одного значения того типа, который хранится в файле. Например, если в файле хранятся значения типа Double (8 байт), то размер блока может быть равен 8, 16, 24, 32 и т. д. Фрагмент исходного кода блочного чтения при этом выглядит следующим образом:

procedure TForm2.Button1Click(Sender: TObject);

var F: File;

DoubleArray: array [0..255] of Double;

Begin

if OpenDlg.Execute then AssignFile(F, OpenDlg.FileName) else Exit;

BlockRead(F, DoubleArray, 32, Transferee!);

ShowMessage(‘Ñ÷èòàíî ‘+IntToStr(Transfered)+’ áëîêîâ’);

end;

end;

Как видно из примера, размер блока установлен в процедуре Reset и кратен размеру элемента массива DoubleArray, в который считываются данные. В переменной Transfered возвращается число считанных блоков. Если размер файла меньше заданного в процедуре BlockRead числа блоков, ошибка не возникает, а в переменной Transfered передается число реально считанных блоков.

procedure BlockWrite(var f: File; var Buf; Count: Integer [; var AmtTransferred: Integer]); используется аналогично.

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

Объявление Описание
ChangeFileExt Функция позволяет изменить расширение файла. При этом сам файл не переименовывается.
ChDir Процедура изменяет текущий каталог на другой, путь к которому задается параметром
CloseFile Вызов процедуры разрывает связь между файловой переменной и файлом на диске.
DeleteFile Функция производит удаление файла с диска и возвращает значение False, если файл удалить не удалось или файл не существует
ExtractFileExt Функция возвращает расширение файла
ExtractFileName Извлекает имя и расширение файла, содержащегося в параметре FileName
ExtractFilePath Функция возвращает полный путь к файлу
Erase Удаляет файл, связанный с файловой переменной
FileSearch Данная процедура производит поиск в каталогах DirList файла Name. Если в процессе выполнения FileSearch обнаруживается искомое имя файла, то функция возвращает в строке типа string полный путь к найденному файлу. Если файл не найден, то возвращается пустая строка
FileSetAttr Присваивает файлу с именем FileName атрибуты Attr. Функция возвращает 0, если присвоение атрибутов прошло успешно. В противном случае возвращается код ошибки
FilePos Возвращает текущую позицию файла. Функция используется для нетекстовых файлов. Перед вызовом FilePos файл должен быть открыт
MkDir Процедура создает новый каталог
Rename Процедура изменяет имя файла, связанного с файловой переменной F. Переменная NewName является строкой типа string или PChar (если включена поддержка расширенного синтаксиса)
RmDir Процедура удаляет пустой каталог, путь к которому задается в строке S. Если указанный каталог не существует или он не пустой, то возникает сообщение об ошибке ввода/вывода

Потоки

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

Не нашли то, что искали? Воспользуйтесь поиском:

ChangeFileExt — Функция Delphi

Теперь рассмотрим две самые распространенные операции, выполняемые при работе с файлами. Это чтение и запись. Для их осуществления применяются специальные функции файлового ввода/вывода.

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

1. Объявить файловую переменную необходимого типа.

2. При помощи функции AssignFile связать эту переменную с требуемым файлом.

3. Открыть файл при помощи функций Append, Reset, Rewrite.

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

5. Закрыть файл при помощи функции CloseFile .

По сравнению с Turbo Pascal изменились названия только двух функций: Assign стала AssignFile , a Close превратилась в CloseFile .

В качестве примера рассмотрим небольшой фрагмент исходного кода.

then AssignFiie(F, OpenDlg.FileName)

else Exit; Reset(F);

while Not EOF(F) do

Если в диалоге открытия файла OpenDlg был выбран файл, то его имя связывается с файловой переменной F при помощи процедуры AssignFiie . В качестве имени файла рекомендуется всегда передавать полное имя файла (включая его маршрут). Как раз в таком виде возвращают результат выбора файла диалоги работы с файлами TOpenDialog, TOpenPictureDiaiog . Затем при помощи процедуры Reset этот файл открывается для чтения и записи.

В цикле выполняется чтение из файла текстовых строк и запись их в компонент TMemo . Процедура Readin осуществляет чтение текущей строки файла и переходит на следующую строку. Цикл выполняется, пока функция EOF не сообщит о достижении конца файла.

После завершения чтения файл закрывается.

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

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

Открытие файла может осуществляться тремя процедурами — в зависимости от типа его дальнейшего использования.

procedure Reset(var F: File [; RecSize: Word ]);

открывает существующий файл для чтения и записи, текущая позиция устанавливается на первой строке файла.

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

procedure Rewrite(var F: File [; RecSize: Word ]);

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

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

Чтение данных из типизированных и текстовых файлов выполняют процедуры Read И Readin.

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

  • procedure Read([var F: Text;] VI [, V2. Vn]);
  • procedure Read(F, VI [, V2. Vn]);

для других типизированных файлов.

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

procedure Readln([ var F: Text; ] VI [, V2. Vn ]);

считывает одну строку текстового файла и устанавливает текущую позицию на следующей строке. Если использовать процедуру без переменных vi. .vn, то она просто передвигает текущую позицию на очередную строку файла.

Процедуры для записи в файл write и writein описаны аналогично:

procedure Write([var F: Text; ] PI [, P2. Pn]) ; procedure Writein([ var F: Text; ] PI [, P2. Pn ]);

Параметры P1, P2, . Pn могут быть одним из целых или вещественных типов, одним из строковых типов или логическим типом. Но у них есть возможность дополнительного форматирования при выводе. Каждый параметр записи может иметь форму:

Р n — выводимая переменная или выражение;

MinWidth — минимальная ширина поля в символах, которая должна быть больше 0;

DecPlaces — содержит количество десятичных символов после запятой при отображении вещественных чисел с фиксированной точкой.

Обратите внимание, что для текстовых файлов в функциях Read и write файловая переменная F может быть опущена. В этом случае чтение и запись осуществляются в стандартные файлы ввода/вывода. Когда программа компилируется как консольное приложение (флаг <$APPTYPE CONSOLE>), Delphi автоматически связывает входной и выходной файлы с окном консоли.

Для контроля за текущей позицией в файле применяются две основные функции. Функция EOF (F) возвращает значение True , если достигнут конец файла. Функция EOLN(F) аналогично сигнализирует о достижении конца строки. Естественно, в качестве параметра в функции необходимо передавать файловую переменную.

обеспечивает смещение текущей позиции на N элементов. Размер одного элемента в байтах зависит от типа данных файла (от типизированной переменной).

Рассмотрим теперь режим блочного ввода/вывода данных между файлом и областью адресного пространства (буфером). Этот режим отличается значительной скоростью передачи данных, причем скорость пропорциональна размеру одного передаваемого блока — чем больше блок, тем больше скорость.

Для реализации этого режима необходимо использовать только нетипизированные файловые переменные. Размер блока определяется в процедуре открытия файла ( Reset, Rewrite ). Непосредственно для выполнения операций используются процедуры BlockRead и BlockWrite .

procedure BlockRead(var F: File; var Buf; Count: Integer

[; var AmtTransferred: Integer]);

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

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

При использовании блочного чтения или записи размер блока необходимо выбирать таким образом, чтобы он был кратен размеру одного значения того типа, который хранится в файле. Например, если в файле хранятся значения типа Double (8 байт), то размер блока может быть равен 8, 16, 24, 32 и т. д. Фрагмент исходного кода блочного чтения при этом выглядит следующим образом:

DoubleArray: array [0..255] of Double;

if OpenDlg.Execute then AssignFile(F, OpenDlg.FileName) else Exit;

BlockRead(F, DoubleArray, 32, Transferee!);

ShowMessage(‘Считано ‘+IntToStr(Transfered)+’ блоков’);

Как видно из примера, размер блока установлен в процедуре Reset и кратен размеру элемента массива DoubleArray , в который считываются данные. В переменной Transfered возвращается число считанных блоков. Если размер файла меньше заданного в процедуре BlockRead числа блоков, ошибка не возникает, а в переменной Transfered передается число реально считанных блоков.

procedure BlockWrite(var f: File; var Buf; Count: Integer

[; var AmtTransferred: Integer]);

Оставшиеся функции ввода/вывода, работающие с файловыми переменными, подробно описаны в табл. 9.1.

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

function ChangeFileExt (const FileName, Extension: string): string;

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

procedure ChDir(S: string);

Процедура изменяет текущий каталог на другой, путь к которому описан в строке s

procedure CloseFile (var F) ;

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

Имя этой процедуры изменено из-за конфликта имен в Delphi (в Borland Pascal используется процедура Close )

function DeleteFile (const FileName: string): Boolean;

Функция производит удаление файла FileName с диска и возвращает значение False , если файл удалить не удалось или файл не существует

function ExtractFileExt (const FileName: string): string;

Функция возвращает расширение файла

function ExtractFileName (const FileName: string) : string;

Извлекает имя и расширение файла, содержащегося в параметре FileName

function ExtractFilePath( const FileName: string): string;

Функция возвращает полный путь к файлу

procedure Erase (var F);

Удаляет файл, связанный с файловой переменной F

function FileSearch (const Name, DirList: string) : string;

Данная процедура производит поиск в каталогах DirList файла Name . Если в процессе выполнения FileSearch обнаруживается искомое имя файла, то функция возвращает в строке типа string полный путь к найденному файлу. Если файл не найден, то возвращается пустая строка

function FileSetAttr (const FileName: string; Attr: Integer): Integer;

Присваивает файлу с именем FileName атрибуты Attr. Функция возвращает 0, если присвоение атрибутов прошло успешно. В противном случае возвращается код ошибки

function FilePos (var F): Longint;

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

function FileSize (var F): Integer;

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

Для текстовых файлов функция FileSize не используется

procedure Flush (var F: Text);

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

Когда текстовый файл открыт для записи с использованием функции Rewrite или Append, Flush очищает выходной буфер, связанный с файлом. После выполнения данной процедуры все символы, которые направлены для записи в файл, будут гарантированно записаны в нем

procedure GetDir(D: Byte; var S: string);

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

0 — по умолчанию (текущий);

Процедура не генерирует код ошибки. Если имя диска в D оказывается ошибочным, то в строке S возвращается значение Х:\, как если бы текущая папка была на этом ошибочно указанном диске

function lOResult: Integer;

Функция возвращает статус последней произведенной операции ввода/вывода, если контроль ошибок выключен

procedure MkDir(S: string);

Процедура создает новый каталог, который описывается в строке S

procedure Rename (var F; NewName: string) ;

Процедура изменяет имя файла, связанного с файловой переменной F. Переменная NewName является строкой типа string или PChar (если включена поддержка расширенного синтаксиса)

procedure RmDir(S: string);

Процедура удаляет пустой каталог, путь к которому задается в строке S. Если указанный каталог не существует или он не пустой, то возникает сообщение об ошибке ввода/вывода

procedure Seek (var F; N: Longint) ;

Перемещает текущую позицию курсора на N позиций. Данная процедура используется только для открытых типизированных или нетипизированных файлов для проведения чтения/записи с нужной позиции файла. Началу файла соответствует нулевой номер позиции. Для добавления новой информации в конец существующего файла необходимо установить указатель на символ, следующий за последним. Для этого можно использовать выражение Seek (F, FileSize(F))

function SeekEof[(var F: Text) ] : Boolean;

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

function SeekEoln[ (var F: Text) ] : Boolean;

Возвращает значение True , если указатель текущей позиции находится на символе конца строки.

SeekEoln может быть использован только с открытым текстовым файлом

procedure SetTextBuf (var F: Text; var Buf [; Size: Integer] );

Связывает с текстовым файлом буфер ввода/вывода. F — файловая переменная текстового типа. Каждая файловая переменная текстового типа имеет внутренний буфер емкостью 128 байт, в котором накапливаются данные при чтении и записи. Такой буфер пригоден для большинства операций. Однако при выполнении программ с интенсивным вводом/выводом буфер может переполниться, что приведет к записи операций ввода/вывода на диск и, как следствие, к существенному замедлению работы приложения. SetTextBuf позволяет помещать в текстовый файл F информацию об операциях ввода/вывода вместо ее размещения в буфере. Size указывает размер буфера в байтах. Если этот параметр опускается, то полагается размер, равный SizeOf (Buf). Новый буфер действует до тех пор, пока F не будет связана с новым файлом процедурой AssignFile

Удаляет все позиции, следующие после текущей позиции в файле. А текущая позиция становится концом файла. С переменной F может быть связан файл любого типа за исключением текстового

НОВОСТИ ФОРУМА
Рыцари теории эфира
01.10.2020 — 05:20: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ — Upbringing, Inlightening, Education ->
[center][Youtube]69vJGqDENq4[/Youtube][/center]
[center]14:36[/center]
Osievskii Global News
29 сент. Отправлено 05:20, 01.10.2020 г.’ target=_top>Просвещение от Вячеслава Осиевского — Карим_Хайдаров.
30.09.2020 — 12:51: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ — Upbringing, Inlightening, Education ->
[center][Ok]376309070[/Ok][/center]
[center]11:03[/center] Отправлено 12:51, 30.09.2020 г.’ target=_top>Просвещение от Дэйвида Дюка — Карим_Хайдаров.
30.09.2020 — 11:53: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ — Upbringing, Inlightening, Education ->
[center][Youtube]VVQv1EzDTtY[/Youtube][/center]
[center]10:43[/center]

интервью Раввина Борода https://cursorinfo.co.il/all-news/rav.
мой телеграмм https://t.me/peshekhonovandrei
мой твиттер https://twitter.com/Andrey54708595
мой инстаграм https://www.instagram.com/andreipeshekhonow/

[b]Мой комментарий:
Андрей спрашивает: Краснодарская синагога — это что, военный объект?
— Да, военный, потому что имеет разрешение от Росатома на манипуляции с радиоактивными веществами, а также иными веществами, опасными в отношении массового поражения. Именно это было выявлено группой краснодарцев во главе с Мариной Мелиховой.

[center][Youtube]CLegyQkMkyw[/Youtube][/center]
[center]10:22 [/center]

Доминико Риккарди: Россию ждёт страшное будущее (хотелки ЦРУ):
https://tainy.net/22686-predskazaniya-dominika-rikardi-o-budushhem-rossii-sdelannye-v-2000-godu.html

Завещание Алена Даллеса / Разработка ЦРУ (запрещено к ознакомлению Роскомнадзором = Жид-над-рус-надзором)
http://av-inf.blogspot.com/2013/12/dalles.html

[center][b]Сон разума народа России [/center]

[center][Youtube]CLegyQkMkyw[/Youtube][/center]
[center]10:22 [/center]

Доминико Риккарди: Россию ждёт страшное будущее (хотелки ЦРУ):
https://tainy.net/22686-predskazaniya-dominika-rikardi-o-budushhem-rossii-sdelannye-v-2000-godu.html

Завещание Алена Даллеса / Разработка ЦРУ (запрещено к ознакомлению Роскомнадзором = Жид-над-рус-надзором)
http://av-inf.blogspot.com/2013/12/dalles.html

[center][b]Сон разума народа России [/center]

ChangeFileExt — Функция Delphi

Реклама Наложенный платеж, прямые поставки от мировых производителей запчастей LAND ROVER · пн-вс 10:00-20:00

Delphi Basics : ChangeFileExt command

Description: The ChangeFileExt function changes the Extension value of a file FileName, returning the new value as a string. Related commands

delphi — (Save Dialog) How to change file …

17.08.2020 · I use this code for my XLSX/XLS/ODS exporting library to change the file extension on both XP and Vista+. One drawback: Class helpers cannot access private fields in Delphi 2007, so this code works only in Delphi 2009+. If you want Delphi 2007 compatibility, use the same hack for TOpenDialog like I used for TFileDialogWrapper in this example.

Пример кода

Delphi Basics : ExtractFileName command

Delphi Basics: ExtractFileName Function: Extracts the name . Related commands: ChangeFileExt: Change the extension part of a file name: ExtractFileDir: Extracts the dir part of a full file name: ExtractFileDrive: Extracts the drive part of a full file name: ExtractFileExt: Extracts the extension part of a full file name: ExtractFilePath .

Delphi File Extensions | Delphi Programming | …

File extension Type Delphi Version Description Keep in Source Control?.adt? Rad Studio 2010 enterprise and newer QA Audit set file, the stored settings for a QA audit xx.bdsdeploy? Deployment file.bdsgroup text/xml Delphi BDS 2006 and earlier BDS Project Group, combines several projects into a.

SysUtils.ChangeFileExt Function — Embarcadero …

ChangeFileExt takes the file name passed in FileName and changes the extension of the file name to the extension passed in Extension. Extension specifies the new extension, including the initial dot character. ChangeFileExt does not rename the actual file, it just creates a new file name string.

Remove File Extension — Delphi Pages Forums

28.02.2002 · However, if I have a file such as «temp.txt.dat», it returns «temp» — i.e: it takes all the text after the first period it finds, not the last. Could someone offer me a solution to getting just THE LAST part of the filename, and not at the first period it finds?

Delphi Associating and using file extensions — …

16.11.2020 · i need my delphi program to open a .xcr file type which will be encrypted text. i want to assign my Delphi program as the default program to open this file when the user clicks on it. I’ve done some research and from what i can tell i need to use the registry object in Delphi to change the registry but i’m not too sure how to do that.

Delphi ChangeFileExt — Функция изменяет расширение файла .

Описание: Функция ChangeFileExt изменяет расширение файла. Имя файла задаётся полностью, через запятую задаётся новоё расширение, смотри пример.

Changing File Extension — delphi

Changing file extension in windows. 2. SaveDialog doesn’t change file extension. 3. Change the file extension in TSaveDialog. 4. Changing Default file Extensions. 5. How to register file extension with my EXE file. 6. Open Files using Windows 95 File Extension Associations. 7. Open Files using Windows 95 File Extension Associations. 8.

4 Ways to Change a File Extension — wikiHow

15.10.2020 · File extensions tell your computer what kind of file it is and what types of software programs on your computer should open the file. The easiest was to change a file‘s extension is to save the file as a different file type from within a software program.

Илон Маск рекомендует:  Атрибут ismap в HTML
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL