Пакет
UTL_FILE
позволяет программам PL/SQL
как читать, так и записывать данные в любой файл операционной системы, доступный для сервера, на котором работает экземпляр базы данных. Данные из файла можно загрузить непосредственно в таблицу базы данных, в полной мере используя всю мощь и гибкость программирования PL/ SQL.
Также разработчик может генерировать отчеты прямо из PL/SQL
, не беспокоясь об ограничениях на максимальный размер буфера DBMS_OUTPUT
, существовавших до выхода Oracle Database 10g Release 2
.
Идея записи произвольных файлов выглядит довольно рискованно — злонамеренный или беспечный программист теоретически может воспользоваться UTL_FILE
для записи файлов баз данных табличного пространства, управляющих файлов и т. д. Oracle
позволяет администратору установить ограничения на чтение и запись файлов одним из двух способов:
- Чтение и запись файлов средствами
UTL_FILE
производится в каталогах, заданных параметромUTL_FILE_DIR
в инициализационном файле базы данных. UTL_FILE
также читает и записывает файлы в каталогах, определяемых объектами каталогов базы данных.
Сначала вы узнаете, как использовать эти два механизма, а затем мы рассмотрим специфические возможности пакета UTL_FILE
. Многие программы UTL_FILE
инкапсулируются в удобном пакете, доступном в файле fileIO.pkg
на сайте github.
Параметр UTL_FILE_DIR
Хотя использование UTL_FILE_DIR
официально не признано устаревшим, этот подход редко используется в новейших версиях базы данных Oracle
. Объекты каталогов гораздо удобнее и обладают большей гибкостью. Если у вас есть выбор, не используйте UTL_FILE_DIR
; просто пропустите этот раздел и переходите к разделу «Работа с каталогами в Oracle
».
Вызывая FOPEN
для открытия файла, необходимо указать как местонахождение, так и имя файла. Местонахождение файла проверяется по списку доступных каталогов, которые задаются в файле инициализации базы данных строками вида:
UTL_FILE_DIR = каталог
Включите параметр UTL_FILE_DIR
для каждого каталога, который вы хотите сделать доступным для операций UTL_FILE
. Например, следующие записи разрешают доступ к четырем разным каталогам в файловых системах семейства Unix/Linux
:
UTL_FILE_DIR = /tmp
UTL_FILE_DIR = /ora_apps/hr/time_reporting
UTL_FILE_DIR = /ora_apps/hr/time_reporting/log
UTL_FILE_DIR = /users/test_area
Чтобы обойти средства безопасности сервера и разрешить чтение/запись во все каталоги, можно воспользоваться специальным синтаксисом:
UTL_FILE_DIR = *
Никогда не используйте этот механизм в среде реальной эксплуатации. Конечно, в среде разработки этот синтаксис упрощает использование UTL_FILE
и тестирование кода. Однако при переводе приложения в рабочую среду доступ следует ограничить несколькими конкретными каталогами.
Настройка каталогов
Несколько замечаний по поводу настройки доступных каталогов для UTL_FILE
и работы с ними:
- Доступ не распространяется на подкаталоги. Допустим, файл инициализации содержит следующие строки:
UTL_FILE_DIR = c:\group\dev1
UTL_FILE_DIR = c:\group\prod\oe
UTL_FILE_DIR = c:\group\prod\ar
Вы не сможете открыть файл в подкаталоге c :\group\prod\oe\reports.
- Не используйте следующую запись в системах Unix и Linux:
UTL_FILE_DIR = .
Она выполняет чтение/запись в текущем каталоге операционной системы.
- Не заключайте имена каталогов в одиночные или двойные кавычки.
- В среде
Unix/Linux
владельцем файла, созданногоFOPEN
, является теневой процесс, запустивший экземпляр Oracle (как правило, «oracle»). При попытке обратиться к этим файлам или изменить их за пределамиUTL_FILE
необходимо обладать соответствующими привилегиями (или войти в систему как «oracle
»). - Не завершайте имя каталога ограничителем (например, косой чертой в
Unix/Linux
). Следующая спецификация создаст проблемы при попытке чтения или записи в каталог:
UTL_FILE_DIR = /tmp/orafiles/
Определение местонахождения файла при открытии
Местонахождение файла определяется строкой, специфической для операционной системы, которая задает каталог или область для открытия файла. При передаче местонахождения в вызове UTL_FILE. FOPEN
спецификация местонахождения указывается в том виде, в каком она задана в файле инициализации базы данных. И помните, что в операционных системах, учитывающих регистр символов, регистр спецификации в файле инициализации должен совпадать с регистром, использованным в вызове UTL_FILE.F0PEN
. Несколько примеров:
- Windows:
file_id := UTL_FILE.FOPEN ('k:\common\debug', 'trace.lis', 'R');
- Unix/Linux:
file_id := UTL_FILE.FOPEN ('/usr/od2000/admin', 'trace.lis', 'W');
Местонахождение должно задаваться явно, как полный путь к файлу. При этом не допускается использование параметров, относящихся к специфике операционной системы (например, переменных окружения в Unix/Linux
).
Работа с каталогами в Oracle
До выхода версии Oracle9i Release 2
при открытии файла приходилось указывать его местонахождение. Однако жесткое кодирование данных всегда нежелательно — а если данные будут перемещены в другое место? Сколько программ придется изменять, чтобы они искали свои данные в правильном месте? И сколько раз придется вносить такие изменения?
Лучше объявить переменную или константу, которой будет присваиваться значение, определяющее местонахождение данных. Если сделать это в пакете, к константе можно будет обращаться из любой программы в схеме, обладающей привилегией EXECUTE
для этого пакета. Пример:
PACKAGE accts_pkg
IS
c_data_location
CONSTANT VARCHAR2(30) := '/accts/data';
...
END accts_pkg;
DECLARE
file_id UTL_FILE.file_type;
BEGIN
file_id := UTL_FILE.fopen (accts_pkg.c_data_location, 'trans.dat', 'R');
END;
Неплохо, но еще лучше — использовать объект уровня схемы, который может определяться в базе данных. Для создания каталога администратор базы данных должен предоставить вам привилегию CREATE ANY DIRECTORY
, после чего вы получаете возможность создания новых каталогов инструкциями следующего вида:
CREATE OR REPLACE DIRECTORY development_dir AS '/dev/source';
CREATE OR REPLACE DIRECTORY test_dir AS '/test/source';
Несколько важных обстоятельств, относящихся к созданию каталогов в UTL_FILE
:
- Oracle не проверяет заданное вами имя каталога. Строка просто связывается с именованным объектом базы данных.
- Имя каталога, указанное, допустим, при вызове
UTL_FILE.FOPEN
, интерпретируется не как имя объектаOracle
, а как строка с учетом регистра символов. Иначе говоря, если имя не задается как строка с символами в верхнем регистре, операция завершится неудачей. Например, такое решение работает:
handle := UTL_FILE.FOPEN(
location => 'TEST_DIR', filename => 'myfile.txt', open_mode =>
'r');
... а такое — нет:
handle := UTL_FILE.FOPEN(
location => test_dir, filename => 'myfile.txt', open_mode => 'r');
- После того как каталог будет создан, вы можете предоставить конкретным пользователям разрешения на работу с ним:
GRANT READ ON DIRECTORY development_dir TO senior_developer;
- Наконец, информацию о каталогах, доступных для текущей схемы, можно получить из представления
ALL_DIRECT0RIES
. Данные представления также могут использоваться для создания полезных вспомогательных программ. В следующем примере выводится список всех каталогов, определенных в базе данных:
/* Файл в Сети: fileIO.pkg */
PROCEDURE fileIO.gen_utl_file_dir_entries
IS
BEGIN
FOR rec IN (SELECT * FROM all_directories)
LOOP
DBMS_OUTPUT.PUT_LINE ('UTL_FILE_DIR = ' || rec.directory_path);
END LOOP;
END gen_utl_file_dir_entries;
Одно из преимуществ подобных вспомогательных программ заключается в том, что они позволяют легко и нетривиально обрабатывать «ошибки форматирования» (например, если имя каталога не задается в верхнем регистре).
Открытие файлов
Прежде чем читать или записывать данные, файл необходимо предварительно открыть. Функция UTL_FILE.F0PEN
открывает заданный файл и возвращает дескриптор для выполнения дальнейших операций с файлом. Заголовок функции выглядит так:
FUNCTION UTL_FILE.FOPEN (
Location IN VARCHAR2
, filename IN VARCHAR2
, open_mode IN VARCHAR2
, max_Linesize IN BINARY_INTEGER DEFAULT NULL)
RETURN UTL_FILE.file_type;
Здесь location
— путь к файлу (каталог из UTL_FILE_DIR
или объект каталога в базе данных); filename
— имя файла; openmode
— режим открытия (см. ниже); max_linesize
— максимальное количество символов в строке с учетом символа новой строки (от 1 до 32 767); UTL_FILE.file_type
— запись со всей информацией, необходимой UTL_FILE
для работы с файлом.
Файл может открываться в одном из трех режимов:
- R— файл открывается только для чтения. В этом режиме для чтения данных из файла используется процедура
GET_LINE
из пакетаUTL_FILE
. - W — файл открывается для чтения и записи в режиме замены. Все существующие строки удаляются из файла. В этом режиме для модификации файла могут использоваться любые из следующих программ
UTL_FILE
:PUT, PUT_LINE
,NEW_LINE
,PUTF
иFFLUSH
. - A — файл открывается для чтения и записи в режиме присоединения. Все существующие строки остаются в файле, а новые строки присоединяются за последней строкой. Для модификации файла могут использоваться любые из следующих программ
utl_file
:put
,put_line
,new_line
,PUTF
иFFLUSH
.
При открытии файлов необходимо учитывать следующие обстоятельства:
- Путь, объединенный с именем файла, должен представлять действительное имя файла в вашей операционной системе.
- Путь к файлу должен быть доступным и уже существующим; функция
FOPEN
не создает каталог или подкаталог для записи нового файла. - Файл, открываемый для чтения, должен уже существовать. Если файл открывается для записи, он либо создается (если файл не существует), либо из него удаляется все содержимое (если файл существует).
- Файл, открываемый для присоединения, уже должен существовать. В противном случае
UTL_FILE
инициирует исключениеINVALID_OPERATION
.
Следующий пример демонстрирует объявление файлового дескриптора с последующим открытием файла только для чтения:
DECLARE
config_file UTL_FILE.FILE_TYPE;
BEGIN
config_file := UTL_FILE.FOPEN ('/maint/admin', 'config.txt', 'R');
Обратите внимание: при открытии файла максимальная длина строки не задается. Этот параметр не является обязательным. Если он не указан, то максимальная длина строки, которая читается или записывается в файл, равна приблизительно 1024. С учетом этого ограничения можно передать аргумент max_linesize
, как это сделано в следующем примере:
DECLARE
config_file UTL_FILE.FILE_TYPE;
BEGIN
config_file := UTL_FILE.FOPEN (
'/maint/admin', 'config.txt', 'R', max_linesize => 32767);
Файлы в многобайтовых кодировках следует открывать функцией FOPEN_ NCHAR
. В этом случае Oracle
рекомендует ограничить max_linesize
значением 6400.
Проверка открытия файла
Функция IS_0PEN
возвращает TRUE
, если заданный дескриптор указывает на уже открытый файл. В противном случае возвращается FALSE
. Заголовок функции выглядит так:
FUNCTION UTL_FILE.IS_OPEN (file IN UTL_FILE.FILE_TYPE) RETURN BOOLEAN;
Здесь file
— проверяемый файл.
Важно понимать, что означает такая проверка в контексте UTL_FILE
. Функция IS_0PEN
не выполняет никаких проверок на уровне операционной системы, она всего лишь убеждается в том, что поле id записи файлового дескриптора отлично от NULL
. Если вы не модифицировали эти записи и их содержимое, поле id имеет отличное от NULL
значение только при вызове F0PEN
. При вызове FCL0SE
в него снова записывается значение NULL
.
Закрытие файла
Для закрытия файла или всех открытых файлов в сеансе следует использовать процедуры UTL_FILE.FCL0SE
и UTL_FILE.FCL0SE_ALL
соответственно. Заголовок процедуры выглядит так:
PROCEDURE UTL_FILE.FCLOSE (file IN OUT UTL_FILE.FILE_TYPE);
Здесь file
— имя закрываемого файла. Обратите внимание на то, что аргумент UTL_FILE. FCL0SE
передается в режиме IN 0UT
, потому что процедура после закрытия файла записывает в поле id записи значение NULL
.
Если при закрытии файла в буфере остались незаписанные данные, UTL_FILE
инициирует исключение WRITE_ERR0R
.
Процедура FCL0SE_ALL
закрывает все открытые файлы. Ее заголовок выглядит так:
PROCEDURE UTL_FILE.FCLOSE_ALL;
Эта процедура удобна тогда, когда программа открывает множество разных файлов, и вы хотите удостовериться в том, что ни один файл не останется открытым при ее завершении.
В программах, открывающих файлы, процедура FCL0SE_ALL
может вызываться в обработчиках исключений; этот вызов гарантирует, что файлы будут закрыты и в случае аварийного завершения программы.
EXCEPTI0N
WHEN 0THERS
THEN
UTL_FILE.FCLOSE_ALL;
... другие завершающие действия ...
END;
При закрытии файлов процедурой FCL0SE_ALL
их файловые дескрипторы не помечаются как закрытые (иначе говоря, их поле id остается отличным от NULL
). В результате любые вызовы IS_0PEN
для таких дескрипторов будут возвращать TRUE
, хотя вы и не сможете выполнять с этими файлами операции чтения/записи (без повторного открытия).
Чтение из файла
Процедура UTL_FILE.GET_LINE
читает из открытого файла строку данных в заданный буфер. Заголовок процедуры выглядит так:
PROCEDURE UTL_FILE.GET_LINE
(file IN UTL_FILE.FILE_TYPE,
buffer OUT VARCHAR2);
Здесь file
— дескриптор, полученный при вызове FOPEN
, а buffer
— буфер, в который читаются данные. Переменная, заданная для параметра buffer
, должна быть достаточно большой для хранения всех данных вплоть до следующего символа новой строки или маркера конца файла. В противном случае PL/SQL
инициирует исключение VALUE_ERROR
. Символ-завершитель строки не включается в строку, записываемую в буфер.
Oracle
предоставляет дополнительные версии GET
для чтения данных NVARCHAR2
(GET_LINE_NCHAR
) и данных RAW
(GET_RAW
).
Пример использования GET_LINE
:
DECLARE
l_file UTL_FILE.FILE_TYPE;
l_line VARCHAR2(32767);
BEGIN
l_file := UTL_FILE.FOPEN ('TEMP_DIR', 'numlist.txt', 'R', max_linesize =>
32767);
UTL_FILE.GET_LINE (l_file, l_line);
DBMS_OUTPUT.PUT_LINE (l_line);
END;
GET_LINE
читает данные только в строковую переменную, поэтому если в файле хранятся числа или даты, вам придется самостоятельно выполнить преобразование к локальной переменной соответствующего типа данных.
Исключения GET_LINE
Если GET_LINE
пытается прочитать данные после конца файла, инициируется исключение NO_DATA_FOUND
. Это же исключение инициируется в следующих ситуациях:
- при выполнении неявного (
SELECT INTO
) курсора, не возвращающего строк; - при ссылке на неопределенную строку в коллекции
PL/SQL
; - при чтении после конца двоичного файла (
BFILE
) с использованиемDBMS_LOB
.
Если вы выполняете несколько таких операций в одном блоке PL/SQL
, вероятно, вам понадобится дополнительная логика для определения источника ошибки. Пример такого решения приведен в файле who_did_that.sql
на сайте github.
Удобная инкапсуляция для GET_LINE
Процедура GET_LINE
проста и прямолинейна: она получает следующую строку из файла. Если файловый указатель уже находится на последней строке файла, UTL_FILE.GET_LINE
не вернет никакие флаги, а инициирует исключение NO_DATA_FOUND
. Это ухудшает структуру кода; возможно, вам стоит рассмотреть возможность инкапсуляции GET_LINE
для решения этой проблемы, как объясняется в этом разделе.
Следующая программа читает каждую строку из файла и обрабатывает ее:
DECLARE
l_file UTL_FILE.file_type;
l_line VARCHAR2 (32767);
BEGIN
l_file := UTL_FILE.FOPEN ('TEMP', 'names.txt', 'R');
LOOP
UTL_FILE.get_line (l_file, l_line);
process_line (l_line);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
UTL_FILE.fclose (l_file);
END;
Обратите внимание: этот простой цикл не содержит явных команд EXIT
; он завершается неявно и с инициированием исключения сразу же после того, как UTL_FILE
прочитает данные за концом файла. В подобных небольших блоках логика выглядит понятно, но представьте, что программа насчитывает сотни строк гораздо более сложного кода. Также допустим, что чтение содержимого файла — всего лишь один шаг в общем алгоритме. Если исключение завершает блок, остаток бизнес-логики необходимо разместить в разделе исключений (нежелательно) или же упаковать логику чтения файла в анонимном блоке BEGIN-END
.
Меня такое решение не устраивает. Я не хочу программировать бесконечные циклы без команды EXIT
; условие завершения не структурировано в самом цикле. Более того, ситуация достижения конца файла не является исключением; в конце концов, каждый файл когда-то да кончится. Почему я должен передавать управление в раздел исключений только из-за того, что я хочу полностью прочитать файл?
Полагаю, более удачный способ обработки конца файла заключается в построении программной «обертки» для GET_LINE
, которая немедленно проверяет конец файла и возвращает логический признак (TRUE
или FALSE
). Этот подход продемонстрирован в следующей процедуре get_nextline
:
/* Файл в Сети: getnext.sp */
PROCEDURE get_nextline (
file_in IN UTL_FILE.FILE_TYPE
, line_out OUT VARCHAR2
, eof_out OUT BOOLEAN)
IS
BEGIN
UTL_FILE.GET_LINE (file_in, line_out);
eof_out := FALSE;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
line_out := NULL;
eof_out := TRUE;
END;
Процедура get_nextline
получает уже назначенный файловый дескриптор и возвращает два значения: строку текста (если она имеется) и логический признак (TRUE
, если достигнут конец файла; FALSE
в противном случае). Используя get_nextline
, я могу прочитать содержимое файла в цикле с командой EXIT
:
DECLARE
l_file UTL_FILE.file_type;
l_line VARCHAR2 (32767);
l_eof BOOLEAN;
BEGIN
l_file := UTL_FILE.FOPEN ('TEMP', 'names.txt', 'R');
LOOP
get_nextline (l_file, l_line, l_eof);
EXIT WHEN l_eof;
process_line (l_line);
END LOOP;
UTL_FILE.fclose (l_file);
END;
С процедурой get_nextline
достижение конца файла уже не рассматривается как исключительная ситуация. Я читаю строки из файла, пока он не закончится, после чего закрываю файл и выхожу из цикла. На мой взгляд, такая программа получается более понятной и прямолинейной.
Запись в файл
В отличие от относительно простой процедуры чтения, пакет UTL_FILE
содержит несколько разных процедур, предназначенных для записи в файл:
- UTL_FILE.PUT — вывод данных в текущую строку открытого файла без присоединения завершителя. Либо используйте процедуру
NEW_LINE
для завершения текущей строки, либо выводите строку вместе с завершителем при помощи процедурыPUT_LINE
. - UTL_FILE.NEW_LINE — вставка одного (по умолчанию) или нескольких символов новой строки в текущей позиции.
- UTL_FILE.PUT_LINE — вывод в файл строки, за которой следует символ-завершитель, зависящий от платформы. Именно эта процедура чаще всего используется при работе с пакетом
UTL_FILE
. - UTL_FILE.PUTF — вывод в файл до пяти строк в формате, определяемом форматной строкой (по аналогии с функцией
printf
языка C). - UTL_FILE.FFLUSH — операции записи
UTL_FILE
обычно буферизуются;FFLUSH
немедленно записывает содержимое буфера в файловую систему.
Все эти процедуры могут использоваться только в том случае, если файл был открыт в режиме W или A. Если файл был открыт только для чтения, исполняющее ядро инициирует исключение UTL_FILE.INVALID_OPERATION.
Oracle
предоставляет дополнительные версии PUT
для записи данных NVARCHAR2
(PUT_LINE_NCHAR
, PUT_NCHAR
, PUTF_NCHAR
) и данных RAW
(PUT_ RAW
).
Рассмотрим пример использования UTL_FILE.PUT_LINE
. Эта процедура записывает данные в файл, присоединяя за текстом символ новой строки. Заголовок PUT_LINE
выглядит так:
PROCEDURE UTL_FILE.PUT_LINE (
file IN UTL_FILE.FILE_TYPE
,buffer IN VARCHAR2
,autofLush IN BOOLEAN DEFAULT FALSE)
Здесь file
— дескриптор, полученный при вызове FOPEN
, а buffer
— текст, записываемый в файл (длина не более 32 767); autoflush
— TRUE
, если вы хотите, чтобы эта строка немедленно была передана операционной системе.
Файл должен быть предварительно открыт перед вызовом UTL_FILE.PUT_LINE
.
Пример использования PUT_LINE
для вывода всех имен из таблицы в файл:
PROCEDURE names_to_file
IS
fileid UTL_FILE.file_type;
BEGIN
fileid := UTL_FILE.FOPEN ('TEMP', 'names.dat', 'W');
FOR emprec IN (SELECT * FROM employee)
LOOP
UTL_FILE.put_line (fileid, emprec.first_name || ' ' || emprec.last_name);
END LOOP;
UTL_FILE.fclose (fileid);
END names_to_file;
Вызов PUT_LINE
эквивалентен вызову PUT
, за которым следует вызов NEW_LINE
. Также он эквивалентен вызову PUTF
с форматной строкой «%s\n
» (см. описание PUTF
в следующем разделе).
Запись отформатированного текста в файл
Как и PUT
, процедура PUTF
помещает данные в файл, но использует форматные данные (отсюда и суффикс F) для интерпретации разных элементов, помещаемых в файл. При вызове PUTF
можно передавать от одного до пяти элементов данных. Заголовок процедуры выглядит так:
PROCEDURE UTL_FILE.putf
(file IN FILE_TYPE ,format IN VARCHAR2
,arg1 IN VARCHAR2 DEFAULT NULL
,arg2 IN VARCHAR2 DEFAULT NULL
,arg3 IN VARCHAR2 DEFAULT NULL
,arg4 IN VARCHAR2 DEFAULT NULL
,arg5 IN VARCHAR2 DEFAULT NULL);
Здесь file
— дескриптор, полученный при вызове FOPEN
, а format
— строка, определяющая формат элементов в файле (см. ниже); параметры argN
определяют необязательные строковые аргументы (от одного до пяти).
Форматная строка позволяет подставить значения argN
прямо в текст, записываемый в файл. Кроме «шаблонного» (то есть литерального) текста форматная строка может содержать следующие служебные комбинации:
%s
— приказываетPUTF
поместить соответствующий элемент в файл. Форматная строка может содержать до пяти комбинаций%s
, потому чтоPUTF
получает до пяти элементов.\n
— приказываетPUTF
поместить в файл символ новой строки. Количество вхождений \n в форматную строку не ограничено.
Комбинации %s
заменяются строками аргументов в указанном порядке. Если при вызове передано недостаточно значений для подстановки на место всех форматных комбинаций, %s
просто удаляется из строки перед записью в файл.
Следующий пример демонстрирует использование форматных строк. Допустим, содержимое файла должно выглядеть так:
Employee: Steven Feuerstein
Soc Sec #: 123-45-5678
Salary: $1000
Задача решается одним вызовом PUTF:
UTL_FILE.PUTF
(file_handle, 'Employee: %s\nSoc Sec #: %s\nSalary: %s\n',
'Steven Feuerstein',
'123-45-5678',
TO_CHAR (:employee.salary, '$9999'));
Если вам потребуется вывести более пяти элементов данных, просто вызовите PUTF
дважды для завершения работы.
Копирование файлов
Процедура UTL_FILE.FCOPY
позволяет легко скопировать содержимое одного файла в другой. Например, в следующем фрагменте в архивном каталоге создается
резервная копия файла из каталога разработки:
DECLARE
file_suffix VARCHAR2 (100)
:= TO_CHAR (SYSDATE, 'YYYYMMDDHH24MISS');
BEGIN
-- Копирование всего файла ...
UTL_FILE.FCOPY (
src_location => 'DEVELOPMENT_DIR',
src_filename => 'archive.zip',
dest_location => 'ARCHIVE_DIR',
dest_filename => 'archive'
|| file_suffix
|| '.zip'
);
END;
Процедура FCOPY
также может использоваться для копирования части файла. Два дополнительных параметра задают номера начальной и конечной строки блока, копируемого из файла. Допустим, в текстовом файле хранятся имена победителей ежемесячного конкурса знатоков PL/SQL
, начавшегося в январе 2008 года. Мы хотим скопировать все имена, относящиеся к 2009 году, в другой файл. В этом нам помогут пятый и шестой аргументы FCOPY
:
DECLARE
c_start_year CONSTANT PLS_INTEGER := 2008;
c_year_of_interest CONSTANT PLS_INTEGER := 2009;
l_start PLS_INTEGER;
l_end PLS_INTEGER;
BEGIN
l_start := (c_year_of_interest - c_start_year)*12 + 1;
l_end := l_start + 11;
UTL_FILE.FCOPY (
src_location => 'WINNERS_DIR',
src_filename => 'names.txt',
dest_location => 'WINNERS_DIR',
dest_filename => 'names2008.txt',
start_line => l_start,
end line => l_end
);
END;
Удобная инкапсуляция UTL_FILE.FCOPY
позволит задавать начальную и конечную строки вместо номеров строк. При желании вы можете реализовать такую программу самостоятельно.
Удаление файлов
В Oracle9i Release 2
и последующих версиях для удаления файлов используется процедура UTL_FILE.FREMOVE
. Заголовок процедуры выглядит так:
PROCEDURE UTL_FILE.FREMOVE (
Location IN VARCHAR2,
filename IN VARCHAR2);
Например, в следующем фрагменте UTL_FILE.FREMOVE
удаляет архивный файл из предыдущего примера:
BEGIN
UTL_FILE.FREMOVE ('DEVELOPMENT_DIR', 'archive.zip');
END;
Вы задаете путь и имя файла, а UTL_FILE
пытается его удалить. А если при удалении возникнут проблемы? Будет инициировано одно из следующих исключений.
Исключение | Описание |
UTL_FILE.invalid_path | Недействительный дескриптор |
UTL_FILE.invalid_filename | Файл не найден или имя файла равно NULL |
UTL_FILE.file_open | Файл уже открыт для записи/присоединения |
UTL_FILE.access_denied | Отказано в доступе к объекту каталога |
UTL_FILE.remove_failed | Сбой при удалении файла |
Иначе говоря, UTL_FILE
инициирует исключение при попытке удаления несуществующего файла или при отсутствии привилегий, необходимых для удаления файла. Многие программы удаления файлов в других языках (например, File.delete
в Java) возвращают код состояния, который информирует вас о результате попытки удаления. Если вы предпочитаете этот способ, используйте (или скопируйте) программу fileIO.FREMOVE
из файла fileIO.pkg
на сайте githib.
Переименование и перемещение файлов
Операции копирования и удаления объединяются в вызове процедуры UTL_FILE.RENAME
. Эта удобная утилита позволяет либо переименовать файл в том же каталоге, либо изменить как имя, так и местонахождение (то есть фактически переместить файл). Заголовок FRENAME
выглядит так:
PROCEDURE UTL_FILE.frename (
src_location IN VARCHAR2,
src_filename IN VARCHAR2,
dest_location IN VARCHAR2,
dest_filename IN VARCHAR2,
overwrite IN BOOLEAN DEFAULT FALSE);
Программа может инициировать одно из следующих исключений.
Исключение | Описание |
UTL_FILE.invalid_path | Недействительный дескриптор |
UTL_FILE.invalid_filename | Файл не найден или имя файла равно NULL |
UTL_FILE.access_denied | Отказано в доступе к объекту каталога |
UTL_FILE.remove_failed | Сбой при удалении файла |
Получение атрибутов файла
Иногда требуется получить информацию о конкретном файле. Сколько места занимает файл? Существует ли он? Какой размер блока используется для хранения файла? Ответы уже не являются секретами, которые могут быть разрешены только с помощью команды операционной системы (или в случае длины файла — пакета DBMS_LOB
), как в ранних версиях Oracle. Всю эту информацию можно получить за один вызов процедуры UTL_FILE.FGETATTR
.
Заголовок FGETATTR
выглядит так:
PROCEDURE UTL_FILE.FGETATTR (
Location IN VARCHAR2,
filename IN VARCHAR2,
fexists OUT BOOLEAN,
fiLe_Length OUT NUMBER,
bLock_size OUT BINARY_INTEGER);
Таким образом, чтобы использовать эту программу, вы должны объявить три переменные для хранения логического признака (существует ли файл?), длины файла и размера блока. Пример использования:
DECLARE
L_fexists BOOLEAN;
L_fiLe_Length PLS_INTEGER;
L_bLock_size PLS_INTEGER;
BEGIN
UTL_FILE.FGETATTR (
Location => 'DEVELOPMENT_DIR',
fiLename => 'bigpkg.pkg',
fexists => l_fexists,
fiLe_Length => l_file_length,
bLock_size => l_block_size
);
...
END;
Такой интерфейс весьма неудобен. Даже если вас интересует только длина файла, все равно придется объявлять все переменные, получать длину, а затем работать с полученным значением. Возможно, для работы с FGETATTR
стоит создать собственные функции- «обертки», которые позволяют получить отдельное значение:
FUNCTION fileIO.flength (
location_in IN VARCHAR2,
file_in IN VARCHAR2
)
RETURN PLS_INTEGER;
или :
FUNCTION fileIO.fexists (
location_in IN VARCHAR2,
file_in IN VARCHAR2
)
RETURN BOOLEAN;
В этом случае вам не приходится объявлять ненужные переменные, а программный код становится проще и лаконичнее.