ВВОД-ВЫВОД ДАННЫХ
ПОТОКИ ДАННЫХ
Для ввода-вывода данных в OpenEdge ABL(4GL) используются потоки данных (stream). Источниками данных могут быть как файлы, так и различные устройства, такие как, например, принтер или терминал. Перед вводом-выводом данных необходимо определить потоки, посредством которых будет осуществляться обмен данными.
Любая процедура всегда обладает одним входным и одним выходным потоками. Данные потоки создаются AVM автоматически и называются unnamed (неименованными) потоками.
Обычно для решения простых задач unnamed потоков вполне достаточно, но иногда возникает необходимость в объявлении дополнительных именованных потоков.
DEFINE {[[NEW [GLOBAL]] SHARED] | [PRIVAT]} STREAM stream.
где stream - идентификатор потока.
По умолчанию, потоки данных доступны только в той процедуре, в которой они были объявлены. Для более широкого их применения, потоки могут быть объявлены как:
NEW SHARED - поток доступен в других процедурах. При этом в процедуре, в которой необходимо получить доступ к уже объявленному в другой процедуре потоку, он должен быть объявлен как SHARED. Поток будет доступен другим процедурам лишь до момента завершения процедуры породившей этот поток, т.е. той, в которой она была определена как NEW SHARED.
NEW GLOBAL SHARED - в отличие от NEW SHARED потока NEW GLOBAL SHARED поток остается доступен другим процедурам в рамках пользовательской сессии даже после завершения породившей его процедуры.
ВЫВОД ДАННЫХ
ОПЕРАТОР OUTPUT TO
OUTPUT TO определяет направление вывода потока данных, а так же определяет правила вывода этих данных.
OUTPUT [STREAM stream | STREAM-HANDLE handle] TO
{PRINTER [printer-name]
| opsys-file
| opsys-device
| TERMINAL
| VALUE (expression)
}
[LOB-DIR {constant | VALUE (expression)}]
[APPEND]
[BINARY]
[ECHO | NO-ECHO]
[KEEP-MESSAGES]
[NO-MAP | MAP protermcap-entry]
[PAGED]
[PAGE-SIZE {constant | VALUE (expression)}]
[UNBUFFERED]
[ NO-CONVERT
| {CONVERT
[TARGET target-codepage]
[SOURCE source-codepage]
}
]
Выходной поток данных может быть определен либо его идентификатором STREAM stream, либо ссылкой STREAM-HANDLE handle. Если поток не указан ни с помощь STREAM, ни с помощью STREAM-HANDLE, то AVM использует создаваемый им автоматически неименованный поток.
stream - идентификатор потока данных для вывода. Если идентификатор потока не указан, AVM использует так называемый неименованный поток.
handle - указатель потока данных для вывода.
Направление вывода данных указывается после TO. Вывод данных возможен в следующих направлениях:
PRINTER [printer-name] - вывод данных на принтер. printer-name - имя принтера в системе. Если printer-name не указан, то вывод осуществляется на принтер установленный в системе по умолчанию.
opsys-file - вывод данных в файл. Указывается абсолютный или относительный путь к файлу, в который осуществляется вывод данных. Не может превышать более 255 символов. По умолчанию, если указанный файл существует, то он будет перезаписан.
opsys-device - на устройство ввода/вывода определенного в системе.
TERMINAL - на терминал. Является направлением используемым по умолчанию.
VALUE (expression) - выражение, значение которого определяет направление вывода данных. Примером такого выражения может служить переменная, значение которой представляет имя файла для вывода данных.
Параметры вывода данных:
LOB-DIR {constant | VALUE (expression)} - каталог вывода файлов с данными полей типов BLOB и CLOB.
APPEND - запись данных осуществляется в конец существующего файла.
BINARY - вывод данных осуществляется без какого-либо преобразования.
ECHO - выводить читаемые данные из файла в направлении OUTPUT. Данный режим используется по умолчанию.
NO-ECHO - отключает режим ECHO.
KEEP-MESSAGES - отключает вывод сообщений об ошибках и предупреждениях, а так же сообщений заданных с помощью оператора MESSAGE на терминал. При этом сообщения будут выводиться в поток.
MAP protermcap-entry - указывается применяемая секция файла PROTERMCAP.
NO-MAP - не использовать PROTERMCAP.
PAGED - осуществляет разбитие выводимых данных на страницы. Размер страницы при этом определяется с помощью PAGE-SIZE. Автоматически применяется, если вывод данных осуществляется на принтер.
PAGE-SIZE {constant | VALUE (expression)} - определяет количество строк на странице. По умолчанию принимает значение равное 56. При выводе данных на терминал (TERMINAL) значение по умолчанию принимается равным возможному числу строк для отображения в объекте вывода данных.
UNBUFFERED - осуществляет вывод данных без буферизации, т.е. по одному символу за раз.
CONVERT [TARGET target-codepage] [SOURCE source-codepage] - осуществляет перекодировку текстовых данных в процессе вывода из кодировки SOURCE в кодировку TARGET. По умолчанию, если не указана TARGET кодировка, она принимается равной кодировке указанной в параметре запуска базы -cpinternal, а SOURCE кодировка - равной кодировке указанной в параметре -cpstream.
NO-CONVERT - вывод текстовых данных осуществляется без перекодировки, тем самым отключается используемое по умолчанию преобразование текстовых данных из кодировки -cpinternal в кодировку -cpstream.
По окончанию вывода данных необходимо закрыть поток:
OUTPUT [STREAM stream | STREAM-HANDLE handle] CLOSE
DEF VAR i AS INT NO-UNDO.
REPEAT i = 1 TO 5:
DISPLAY i SKIP.
END.
_______i
1
2
3
4
5
Данный код последовательно выводит значение переменной i на терминал. Для этого AVM использует автоматически созданный неименованный поток, с направлением вывода по умолчанию - на терминал.
Изменим направление вывода неименованного потока данных в файл с помощью оператора OUTPUT. Следующий код не будет уже выводить данные на терминал, а выведет те же данные в текстовый файл myfile.txt
DEF VAR i AS INT NO-UNDO.
OUTPUT TO myfile.txt.
REPEAT i = 1 TO 5:
DISPLAY i SKIP.
END.
OUTPUT CLOSE.
ОПЕРАТОР EXPORT
EXPORT осуществляет преобразование данных таблиц и выражений к стандартному текстовому формату с последующим выводом их в поток данных в виде строки с разделителем.
EXPORT [STREAM stream | STREAM-HANDLE handle] [DELIMITER character]
{expression ...
| record [EXCEPT field ...]
}
[NO-LOBS]
либо
EXPORT [STREAM stream | STREAM-HANDLE handle]
{memptr | longchar}
DELIMITER character - определяет символ разделителя между выводимыми значениями. По умолчанию разделителем является символ пробела.
EXCEPT field - список полей таблицы исключаемых при выгрузке.
NO-LOBS - при выгрузке данных таблицы будут пропускаться поля типов BLOB и CLOB.
Поток данных, в который будут выводиться данные, может быть определен либо посредством указания его идентификатора STREAM stream, либо ссылки STREAM-HANDLE handle. Если поток не указан ни с помощь STREAM, ни с помощью STREAM-HANDLE, то AVM использует, создаваемый им автоматически, неименованный поток.
Вывод в стандартном текстовом формате игнорирует форматы полей указанные при объявлении таблицы и подразумевает следующее:
- Выводимые значения текстовых данных заключаются в двойные кавычки. Если выводимое текстовое значение содержит в себе символы двойных кавычек, то они будут заменены на две двойные кавычки;
- Вывод логических значений осуществляется в формате YES/NO;
- Неизвестное значение выводится символом ? (без кавычек);
- Формат дат определяется стартовыми параметрами -dd и -yy, а так -E;
- Формат экспортируемых значений типов DATATIME и DATATIME-TZ соответствует ISO 8601 (YYYY-MM-DDTHH:MM:SS.SSS+HH:MM).
Вывод данных, хранящихся в полях с типами данных BLOB и CLOB, осуществляется в отдельные файлы. Данные файлы создаются в отдельном каталоге, указанном в параметре LOB-DIR оператора OUTPUT. При этом, в файле выгрузки, в месте куда должны были быть записаны данные поля аналогично другим полям, в качестве значения указывается имя файла с данными из каталога LOB-DIR соответствующего поля.
Если выгрузка дынных всех полей BLOB и CLOB не требуется, то необходимо указать NO-LOBS, при этом в процессе выгрузки данных, данные поля будут пропускаться, аналогично если бы они все были указаны в EXCEPT.
Таким образом, оператор EXPORT осуществляет экспорт данных таблиц в форматы DSV (delimiter-separated values — значения разделённые разделителем), такие как: CSV (Comma-Separated Values — значения, разделённые запятыми) и TSV (Tab Separated Values — значения, разделённые табуляцией). Спецификация RFC4180.
Выгрузка всех полей временной таблицы в файл:
DEF TEMP-TABLE tt-loan
FIELD cont-code AS CHAR /* Номер договора */
FIELD open-date AS DATE /* Дата договора */
FIELD cust-name AS CHAR /* ФИО клиента */
.
/* Заполнение таблицы tt-loan */
...
/* определяем файл выгрузки */
OUTPUT tt-loan TO VALUE("loanlst.txt").
/* выгружаем записи таблицы tt-loan в файл loanlst.txt */
REPEAT:
EXPORT DELIMITER ";" tt-loan.
END.
Выгрузка всех, кроме указанных, полей временной таблицы в файл:
DEF TEMP-TABLE tt-loan
FIELD cont-code AS CHAR /* Номер договора */
FIELD open-date AS DATE /* Дата договора */
FIELD cust-name AS CHAR /* ФИО клиента */
.
/* Заполнение таблицы tt-loan */
...
/* определяем файл выгрузки */
OUTPUT tt-loan TO VALUE("loanlst.txt").
/* выгружаем записи таблицы tt-loan в файл loanlst.txt */
REPEAT:
EXPORT DELIMITER ";" tt-loan EXCEPT tt-loan.open-date.
END.
Последовательность вывода полей в приведенных выше примерах будет такой, в какой они были определены в объявлении таблицы.
Выгрузка указанных полей временной таблицы в файл:
DEF TEMP-TABLE tt-loan
FIELD cont-code AS CHAR /* Номер договора */
FIELD open-date AS DATE /* Дата договора */
FIELD cust-name AS CHAR /* ФИО клиента */
.
/* Заполнение таблицы tt-loan */
...
/* определяем файл выгрузки */
OUTPUT tt-loan TO VALUE("loanlst.txt").
/* выгружаем записи таблицы tt-loan в файл loanlst.txt */
REPEAT:
EXPORT DELIMITER ";"
tt-loan.cont-code tt-loan.cust-name tt-loan.open-date.
END.
Последовательность вывода полей будет такой, в каком мы их пропишем в операторе EXPORT.
ОПЕРАТОP PUT
PUT осуществляет вывод текстового значения в поток данных. Вывод данных на терминал PUT не осуществляет.
PUT
[STREAM stream | STREAM-HANDLE handle]
[UNFORMATTED]
[{ expression
[FORMAT string]
[{AT | TO} expression]
}
| SKIP [(n)]
| SPACE [(n)]
] ...
или
PUT [ STREAM stream | STREAM-HANDLE handle ] CONTROL expression ...
Поток данных, в который будут выводиться данные, может быть определен либо посредством указания его идентификатора STREAM stream, либо ссылки STREAM-HANDLE handle. Если поток не указан ни с помощь STREAM, ни с помощью STREAM-HANDLE, то AVM использует, создаваемый им автоматически, неименнованный поток.
UNFORMATTED - осуществляет вывод данных expression без применения какого-либо формата.
FORMAT - определяет формат, в котором будет осуществляться вывод данных expression. Если FORMAT не задан, то при выводе данных будет использован формат определенный для выражения при его определении. Так например, для значения переменной - это формат указанный при объявлении этой переменной. В противном случае, будет использован формат по умолчанию "X(8)".
AT - определяет номер начальной позиции в строке файла, выраженное в символах, с которой будет осуществлен вывод данных. Если позиция в строке уже имеет данные, т.е. не пуста, то AVM автоматически перейдет на следующую строку и повторит попытку вывода данных в ней с указанной позиции.
TO - определяет номер конечной позиции в строке файла вывода данных выраженное в символах. Если данная позиция не пуста, то произойдет автоматический переход на следующую строку.
SKIP (n) - осуществляет n переходов на новую строку.
SPACE (n) - осуществляет вывод n - пробелов.
CONTROL expression - ESC-последовательности передаваемые принтеру при выводе на него.
Пример:
OUTPUT TO "./test.txt".
PUT UNFORMATTED "Строка1" SKIP.
PUT UNFORMATTED "Строка2".
OUTPUT CLOSE.
В данном примере для вывода данных используется создаваемый AVM автоматически неименованный поток.
Пример:
DEF i AS INTEGER NO-UNDO.
DEF STREAM strOutput.
OUTPUT STREAM strOutput TO VALUE("./test.txt") APPEND.
DO i = 1 TO 10:
PUT STREAM strOutput UNFORMATTED STRING(i) SKIP.
PAUSE 1 NO-MESSAGE.
END.
OUTPUT STREAM strOutput CLOSE.
В данном примере для вывода данных используется объявленный поток strOutput. При этом запись осуществляется в конец созданного в предыдущем примере файл ./test.txt, а не создается новый файл, так как оператор OUTPUT мы указали с опцией APPEND.
ВВОД ДАННЫХ
ОПЕРАТОР INPUT FROM
INPUT FROM определяет источник чтения данных в поток.
INPUT [STREAM stream | STREAM-HANDLE handle] FROM
{ opsys-file
| opsys-device
| TERMINAL
| VALUE (expression)
| OS-DIR (directory) [NO-ATTR-LIST]
}
[LOB-DIR {constant | VALUE (expression)}]
[BINARY]
[ECHO | NO-ECHO]
[MAP protermcap-entry | NO-MAP]
[UNBUFFERED]
[ NO-CONVERT
| { CONVERT
[TARGET target-codepage]
[SOURCE source-codepage]
}
]
Поток данных, в который будет осуществляться чтение данных, может быть определен либо указанием идентификатора потока STREAM stream, либо ссылкой на поток данных STREAM-HANDLE handle.
stream - идентификатор потока данных для вывода. Если идентификатор потока не указан, AVM использует так называемый неопределенный поток.
handle - ссылка на поток данных для вывода.
Источник данных указывается после FROM и может принимать следующие значения:
opsys-file - ввод данных из файла. указывается абсолютный или относительный путь к файлу, из которого осуществляется чтение данных. Не может превышать более 255 символов.
opsys-device - с устройства ввода/вывода определенного в системе.
TERMINAL - чтение данных с терминала.
VALUE (expression) - выражение, значение которого определяет источник чтения данных. Примером такого выражения может служить переменная, значение которой представляет имя файла, из которого осуществляется чтение данных.
OS-DIR (directory) [NO-ATTR-LIST]- директория, содержимое которой необходимо получить.
Параметры чтения данных:
LOB-DIR {constant | VALUE ( expression )} - каталог с файлами данных для загрузки в поля таблицы типов BLOB и CLOB .
BINARY - чтение данных осуществляется без какого-либо преобразования.
ECHO -
NO-ECHO -
MAP protermcap-entry - указывается применяемая секция файла PROTERMCAP.
NO-MAP - не применять PROTERMCAP.
UNBUFFERED - осуществляет чтение данных без буферизации, т.е. по одному символу за раз.
CONVERT [TARGET target-codepage] [SOURCE source-codepage] - осуществляет перекодировку текстовых данных в процессе ввода из кодировки SOURCE в кодировку TARGET. По умолчанию, если не указана TARGET кодировка она принимается равной кодировке указанной в параметре запуска базы -cpinternal, а SOURCE кодировка - равной кодировке указанной в параметре -cpstream.
NO-CONVERT - ввод текстовых данных осуществляется без перекодировки, тем самым отключается используемое по умолчанию преобразование текстовых данных из кодировки -cpinternal в кодировку -cpstream.
По окончанию чтения данных необходимо закрыть поток:
INPUT [STREAM stream | STREAM-HANDLE handle] CLOSE
Оператор INPUT позволяет не только прочитать содержимое файлов, но и содержимое директории. Для этого, в OS-DIR необходимо указать директорию, содержимое которой мы хотим получить. При этом, каждому элементу в директории, будь то это файл или поддиректория, будет соответствовать строка состоящая из трех значений:
- имя элемента (файла или директории);
- полный путь элемента;
- список атрибутов отражающих тип элемента и его статус;
Возможны следующие варианты атрибутов отражающих тип элемента в директории:
- F - файл;
- D - директория;
- S - устройство;
- X - неизвестный тип.
Атрибуты отражающие статус элемента:
- H - скрытый элемент;
- L - символьная ссылка;
- P - pipe.
DEF VARIABLE search-dir AS CHARACTER NO-UNDO.
DEF VARIABLE file-name AS CHARACTER NO-UNDO
FORMAT "x(16)" LABEL "File".
DEF VARIABLE full-path AS CHARACTER NO-UNDO
FORMAT "x(50)" LABEL "Path".
DEFINE VARIABLE attr-list AS CHARACTER NO-UNDO
FORMAT "x(4)" LABEL "Attributes".
search-dir = OS-GETENV("DLC").
INPUT FROM OS-DIR(search-dir).
REPEAT:
SET file-name full-path attr-list
WITH WIDTH-CHARS 80 USE-TEXT TITLE "Contents of " + search-dir.
END.
INPUT CLOSE.
ОПЕРАТОР IMPORT
IMPORT - осуществляет чтение текстовой строки из потока данных. Основным назначением оператора IMPORT является импорт данных таблиц, выгруженных с помощью оператора EXPORT.
IMPORT [STREAM stream | STREAM-HANDLE handle]
{ [DELIMITER character] {field | ^} ...
| [DELIMITER character] record [EXCEPT field ...]
| UNFORMATTED field
}
[NO-LOBS]
[NO-ERROR]
Строка может представлять собой набор данных разделенных символом-разделителем. При чтении строки оператором IMPORT указание данного разделителя осуществляется с помощью DELIMITER character, где character - символ-разделитель. Разделителем может являться только единичный символ. В случае, если в DELIMETER указано более одного символа, то в учет будет принят только первый из указанных символов. По умолчанию, если разделитель не указан он принимается равным " " (пробел).
После указываются переменные или поля таблицы, в которые будет осуществляться запись читаемых данных. Указание данных полей осуществляется в той же последовательности, в какой записываемые в них данные указаны в строке файла.
Пример:
Создадим текстовый файл (loanlst.csv) содержащим список номеров договоров, дат их открытия и ФИО клиентов:
КД2000/001;01.02.2010;"Иванов Николай Петрович"
КД2000/289;14.10.2009;"Соломин Дмитрий Николаевич"
ДД2001/126;23.05.2011;"Митина Ольга Ивановна"
КД2002/078;05.04.2011;"Ляшко Дарья Олеговна"
КД2002/079;12.06.2010;"Киров Владимир Владимирович"
Импорт данных из файла в указанные поля таблицы
DEF TEMP-TABLE tt-loan
FIELD cont-code AS CHAR /* Номер договора */
FIELD open-date AS DATE /* Дата договора */
FIELD cust-name AS CHAR /* ФИО клиента */
.
DEF STREAM sfile.
/* Открываем входящий поток из файла loanlst.txt */
INPUT STREAM sfile FROM VALUE("loanlst.txt").
/* Читаем строки из файла в цикле и записываем их значение в таблицу */
REPEAT:
CREATE tt-loan.
IMPORT STREAM sfile DELIMITER ";"
tt-loan.cont-code tt-loan.open-date tt-loan.cust-name.
END.
/* Закрываем поток */
INPUT STREAM sfile CLOSE.
Данные из строки файла будут загружаться в соответсвии с последовательностью их в файле и последовательностью указания полей в операторе IMPORT
Импорт данных из файла в поля таблицы с пропуском значений в файле
Если в строке файла имеются значения которые необходимо пропустить, то в операторе IMPORT вместо приемника для данного значения указывается символ ^.
DEF TEMP-TABLE tt-loan
FIELD cont-code AS CHAR /* Номер договора */
FIELD cust-name AS CHAR /* ФИО клиента */
.
DEF STREAM sfile.
/* Открываем входящий поток из файла loanlst.txt */
INPUT STREAM sfile FROM VALUE("loanlst.txt").
/* Читаем строки из файла в цикле и записываем их значение в таблицу */
REPEAT:
CREATE tt-loan.
IMPORT STREAM sfile DELIMITER ";"
tt-loan.cont ^ tt-loan.cust-name.
END.
/* Закрываем поток */
INPUT STREAM sfile CLOSE.
Таким образом, при чтение строки из файла, второе и четвертое значения пропускается. Если бы мы не указали ^ для пропуска значений, то в поле таблицы tt-loan.cust-name было бы записано значение даты, а не ФИО клиента.
Импорт данных из файла в таблицу
DEF TEMP-TABLE tt-loan
FIELD cont-code AS CHAR /* Номер договора */
FIELD open-date AS DATE /* Дата договора */
FIELD cust-name AS CHAR /* ФИО клиента */
FIELD cont-summ AS DEC /* Сумма по договору */
.
DEF STREAM sfile.
/* Открываем входящий поток из файла loanlst.txt */
INPUT STREAM sfile FROM VALUE("loanlst.txt").
/* Читаем строки из файла в цикле и записываем их значение в таблицу */
REPEAT:
CREATE tt-loan.
IMPORT STREAM sfile DELIMITER ";" tt-loan.
END.
/* Закрываем поток */
INPUT STREAM sfile CLOSE.
Данные из строки файла будут загружаться в поля таблицы в соответствии с последовательностью указания их в файле и последовательностью полей в объявлении таблицы. Т.е. первое значение из строки файла будет загружено в первое объявленное поле таблицы.
Импорт данных из файла в таблицу с исключением полей
Поля таблицы, которые необходимо исключить из процесса импорта данных из файла, как если бы они не были объявлены, указываются в EXCEPT с разделителем пробел.
DEF TEMP-TABLE tt-loan
FIELD cont-code AS CHAR /* Номер договора */
FIELD open-date AS DATE /* Дата договора */
FIELD cust-name AS CHAR /* ФИО клиента */
FIELD cont-summ AS DEC /* Сумма по договору */
.
DEF STREAM sfile.
/* Открываем входящий поток из файла loanlst.txt */
INPUT STREAM sfile FROM VALUE("loanlst.txt").
/* Читаем строки из файла в цикле и записываем их значение в таблицу */
REPEAT:
CREATE tt-loan.
IMPORT STREAM sfile DELIMITER ";" tt-loan EXCEPT open-date cont-summ.
END.
/* Закрываем поток */
INPUT STREAM sfile CLOSE.
При ипорте данных будут исключаться поля open-date и cont-summ. Остальные поля таблицы будут заполнены данными из файла в соответсвии с их последовательностью.
ДИАЛОГ ВЫБОРА И СОХРАНЕНИЯ ФАЙЛА
В заключение хотелось бы рассказать о существующем готовом механизме в PROGRESS выбора и сохранения файла.
DEF VAR pFilter AS INTEGER NO-UNDO.
DEF VAR pStatus AS LOGICAL NO-UNDO.
DEF VAR pFile AS CHARACTER NO-UNDO.
RUN adeedit/_dlggetf.p (
INPUT "[ЗАГОЛОВОК]",
INPUT NO, /* NO для "открытие", YES для "сохранение" */
INPUT pFilter, /* не используется */
INPUT-OUTPUT pFile,
OUTPUT pStatus).
MESSAGE pStatus SKIP
pFile
VIEW-AS ALERT-BOX.