ВВОД-ВЫВОД ДАННЫХ


ПОТОКИ ДАННЫХ


Для ввода-вывода данных в OpenEdge ABL(4GL) используются потоки данных (stream). Источниками данных могут быть как файлы, так и различные устройства, такие как, например, принтер или терминал. Перед вводом-выводом данных необходимо определить потоки, посредством которых будет осуществляться обмен данными. 

stream.jpg

Любая процедура всегда обладает одним входным и одним выходным потоками. Данные потоки создаются 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

Для указания в качестве разделителя символа табуляции: DELIMITER "\t"

Выгрузка всех полей временной таблицы в файл:

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.

imp-exp01.jpg

Таким образом, при чтение строки из файла, второе и четвертое значения пропускается. Если бы мы не указали ^ для пропуска значений, то в поле таблицы 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.

imp-exp03.jpg

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

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

Поля таблицы, которые необходимо исключить из процесса импорта данных из файла, как если бы они не были объявлены, указываются в 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.

imp-exp02.jpg

При ипорте данных будут исключаться поля 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.
Вы здесь: Главная Основы ABL ВВОД-ВЫВОД ДАННЫХ