En este ejemplo veremos el uso de los ficheros VSAM.
Un fichero VSAM es un fichero de tipo indexado, que tiene definido un índice, y sobre el que se pueden realizar accesos por índice.
Vamos a crear un programa que dé de alta registros, borre registros y actualice registros de un fichero VSAM.
Para ello usaremos el siguiente ejemplo:
Tenemos como entrada 2 versiones del fichero oficina (será un GDG), por un lado la versión actual (0), y por otro la versión anterior (-1). En estos ficheros tendremos la información de las oficinas de un banco con formato:
COPY COFICINA:
01 CSAM-COFICINA.
05 CSAM-CLAVE.
10 CSAM-COD-CODENT PIC 9(4).
10 CSAM-COD-CODOFI PIC 9(4).
05 CSAM-DES-NOMBRE PIC X(20).
05 CSAM-COD-CODPOS PIC 9(5).
05 CSAM-COD-TELEF1 PIC 9(9).
05 CSAM-FEC-APERTU PIC X(10).
La versión (0) será la que tenga los datos más actuales (del día). La versión (-1) será la del día anterior.
Los datos de las oficinas se guardan en un fichero VSAM con clave código de entidad (CSAM-COD-CODENT) y código de oficina (CSAM-COD-CODOFI).
Vamos a comparar las dos versiones para:
1. Si una oficina existe en el fichero de hoy y en la versión del día anterior, actualizamos esa clave en el fichero VSAM.
2. Si una oficina existe en el fichero de hoy pero no en el de ayer, la damos de alta en el fichero VSAM.
3. Si una oficina existe en el fichero de ayer pero no en el de hoy, la damos de baja del fichero VSAM.
JCL:
//** *****************************************************
//** ** ACTUALIZAMOS FICHERO DE OFICINAS VSAM ** **
//** ***************************************************
//PGMVSAM EXEC PGM=PGMVSAM
//ENTRADA1 DD DSN=NOMBRE.FICHERO.OFICINA(0),DISP=SHR
//ENTRADA2 DD DSN=NOMBRE.FICHERO.OFICINA(-1),DISP=SHR
//SALIDA DD DSN=FICHERO.SALIDA.VSAM,DISP=SHR
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=4,DEST=JESTC3
//SYSDBOUT DD SYSOUT=4,DEST=JESTC3
//CEEDUMP DD SYSOUT=4,DEST=JESTC3
/*
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PGMVSAM.
AUTHOR. CONSULTORIO COBOL.
*============================================*
* PROGRAMA DE MANTENIMIENTO DE FICHERO VSAM *
*============================================*
*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT ENTRADA0 ASSIGN TO ENTRADA0
FILE STATUS IS FS-ENTRADA0.
SELECT ENTRADA1 ASSIGN TO ENTRADA1
FILE STATUS IS FS-ENTRADA1.
SELECT SALIDA ASSIGN TO SALIDA
ORGANIZATION IS INDEXED
ACCESS MODE IS RANDOM
RECORD KEY IS CLAVE-OFICVSM
FILE STATUS IS FS-SALIDA.
*
DATA DIVISION.
*
FILE SECTION.
*
**** FICHEROS DE ENTRADA ****
*
**--> OFICINAS VERSION 0 (FICHERO SECUENCIAL)
FD ENTRADA0
LABEL RECORD STANDARD
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS.
01 REG-ENTRADA0 PIC X(52).
*
*--> OFICINAS VERSION -1 (FICHERO SECUENCIAL)
FD ENTRADA1
LABEL RECORD STANDARD
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS.
01 REG-ENTRADA1 PIC X(52).
**** FICHERO DE ENTRADA - SALIDA ****
*
*--> OFICINAS (FICHERO VSAM)
FD SALIDA.
01 REG-VSAM.
03 CLAVE-OFICVSM PIC X(08).
03 RESTO-OFICVSM PIC X(44).
*
*
**********************************************
*
WORKING-STORAGE SECTION.
*
*--------------------------------------------
*--- AREA DE COPYS ---*
*---------------------------------------------
*
*--------------- COPY FICHERO OFICINAS ------------
COPY COFICINA REPLACING CSAM-COFICINA BY ==ENT-V0==.
*
COPY COFICINA REPLACING CSAM-COFICINA BY ==ENT-V1==.
*
*--------------------------------------------------
* AREA DE SWITCHES
*-------------------------------------------------
*--> Final fichero OFICINAS VERSION 0
01 WB-FIN-ENTRADA0 PIC X(1) VALUE 'N'.
88 FIN-ENTRADA0 VALUE 'S'.
*--> Final fichero OFICINAS VERSION 1
01 WB-FIN-ENTRADA1 PIC X(1) VALUE 'N'.
88 FIN-ENTRADA1 VALUE 'S'.
*
*------------------------------------------------
* CODIGOS DE ESTADO DE FICHEROS
*-------------------------------------------------
* FILE STATUS
01 FS-STATUS.
05 FS-ENTRADA0 PIC X(2).
88 FS-ENTRADA0-OK VALUE '00'.
88 FS-ENTRADA0-EOF VALUE '10'.
05 FS-ENTRADA1 PIC X(2).
88 FS-ENTRADA1-OK VALUE '00'.
88 FS-ENTRADA1-EOF VALUE '10'.
05 FS-SALIDA PIC X(2).
88 FS-SALIDA-OK VALUE '00'.
*----------------------------------------------------
* REGISTROS LEIDOS - GRABADOS - BORRADOS - MODIFICADOS
*----------------------------------------------------
01 WC-PROCESADOS.
03 REG-LEIDOS-EN0 PIC 9(09) VALUE ZEROS.
03 REG-LEIDOS-EN1 PIC 9(09) VALUE ZEROS.
03 REG-GRABADOS-VSAM PIC 9(09) VALUE ZEROS.
03 REG-BORRADOS-VSAM PIC 9(09) VALUE ZEROS.
03 REG-MODIF-VSAM PIC 9(09) VALUE ZEROS.
*
PROCEDURE DIVISION.
*
************************************************************
* | 0000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA ************************************************************
* 00000-PRINCIPAL.
*
PERFORM 1000-INICIO
PERFORM 2000-PROCESO
UNTIL FIN-ENTRADA0 AND FIN-ENTRADA1
PERFORM 3000-FINAL
.
*
*-----------
1000-INICIO.
*-----------
PERFORM 1100-ABRIR-FICHEROS
*
*--> LEEMOS PRIMERA OFICINA
PERFORM LEER-ENTRADA0
PERFORM LEER-ENTRADA1
.
*
*---------------
2000-PROCESO.
*---------------
*
EVALUATE TRUE
WHEN CSAM-CLAVE OF ENT-V0
EQUAL CSAM-CLAVE OF ENT-V1
IF ENT-V0 NOT EQUAL ENT-V1
*---------> ACTUALIZAR CLAVE EN FICHERO VSAM
MOVE ENT-V0 TO REG-VSAM
PERFORM 2100-MODIFICAR-VSAM
END-IF
PERFORM LEER-ENTRADA0
PERFORM LEER-ENTRADA1
WHEN CSAM-CLAVE OF ENT-V0 GREATER THAN
CSAM-CLAVE OF ENT-V1
*---------> DAR DE BAJA CLAVE DE V1 EN FICHERO VSAM
MOVE CSAM-CLAVE OF ENT-V1
TO CLAVE-OFICVSM
PERFORM 2200-BAJA-VSAM
PERFORM LEER-ENTRADA1
WHEN CSAM-CLAVE OF ENT-V0 LESS THAN
CSAM-CLAVE OF ENT-V1
*---------> DAR DE ALTA LA CLAVE DE V0 EN FICHERO VSAM
MOVE ENT-V0 TO REG-VSAM
PERFORM 2300-ALTA-VSAM
PERFORM LEER-ENTRADA0
END-EVALUATE
.
*
*-----------
3000-FINAL.
*-----------
PERFORM CERRAR-FICHEROS
*
PERFORM ESTADISTICAS
STOP RUN
.
*
*-----------------------
1100-ABRIR-FICHEROS.
*-----------------------
OPEN INPUT ENTRADA0
ENTRADA1
I-O SALIDA
IF NOT FS-ENTRADA0-OK
DISPLAY 'ERROR EN ABRIR-ENTRADA1'
DISPLAY 'FILE-STATUS = 'FS-ENTRADA0
END-IF
IF NOT FS-ENTRADA1-OK
DISPLAY 'ERROR EN ABRIR-ENTRADA2'
DISPLAY 'FILE-STATUS = 'FS-ENTRADA1
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN ABRIR-FVSAM'
DISPLAY 'FILE-STATUS = ' FS-SALIDA
END-IF
.
*
*----------------------
LEER-ENTRADA0.
*----------------------
READ ENTRADA0 INTO ENT-V0
EVALUATE TRUE
WHEN FS-ENTRADA0-OK
ADD 1 TO REG-LEIDOS-EN0
WHEN FS-ENTRADA0-EOF
SET FIN-ENTRADA0 TO TRUE
WHEN OTHER
DISPLAY 'ERROR EN LEER-ENTRADA0'
DISPLAY 'FILE-STATUS = ' FS-ENTRADA0
PERFORM ESTADISTICAS
END-EVALUATE
.
*
*------------------------
CERRAR-FICHEROS.
*------------------------
CLOSE ENTRADA0
ENTRADA1
SALIDA
IF NOT FS-ENTRADA0-OK
DISPLAY 'ERROR EN CERRAR-ENTRADA0'
DISPLAY 'FILE-STATUS = ' FS-ENTRADA0
PERFORM ESTADISTICAS
END-IF
IF NOT FS-ENTRADA1-OK
DISPLAY 'ERROR EN CERRAR-ENTRADA1'
DISPLAY 'FILE-STATUS = 'FS-ENTRADA1
PERFORM ESTADISTICAS
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN CERRAR-FVSAM'
DISPLAY 'FILE-STATUS = ' FS-SALIDA
PERFORM ESTADISTICAS
END-IF
.
*
*----------------------
LEER-ENTRADA1.
*----------------------
READ ENTRADA1 INTO REG-ENTRADA1
EVALUATE TRUE
WHEN FS-ENTRADA1-OK
ADD 1 TO REG-LEIDOS-EN1
WHEN FS-ENTRADA1-EOF
SET FIN-ENTRADA1 TO TRUE
WHEN OTHER
DISPLAY 'ERROR EN LEER-ENTRADA1'
DISPLAY 'FILE-STATUS = ' FS-ENTRADA1
PERFORM ESTADISTICAS
END-EVALUATE
.
*
*------------------
2100-MODIFICAR-VSAM.
*------------------
REWRITE REG-VSAM
INVALID KEY
DISPLAY 'ERROR EN MODIFICAR-VSAM'
DISPLAY 'FILE-STATUS = ' FS-SALIDA
PERFORM ESTADISTICAS
ADD 1 TO REG-MODIF-VSAM
.
*-------------
2200-BAJA-VSAM.
*-------------
DELETE REG-VSAM
INVALID KEY
DISPLAY 'ERROR EN BAJA-VSAM'
DISPLAY 'FILE-STATUS = ' FS-SALIDA
PERFORM ESTADISTICAS
ADD 1 TO REG-BORRADOS-VSAM
.
*-------------
2300-ALTA-VSAM.
*-------------
WRITE REG-VSAM
INVALID KEY
DISPLAY 'ERROR EN ALTA-VSAM'
DISPLAY 'FILE-STATUS = ' FS-SALIDA
PERFORM ESTADISTICAS
ADD 1 TO REG-GRABADOS-VSAM
.
*
*--------------------------------------------
* ESTADISTICAS
*-------------------------------------------
ESTADISTICAS.
*-------------
DISPLAY '******************************************'
DISPLAY '* E S T A D I S T I C A S *'
DISPLAY '******************************************'
DISPLAY ' PROGRAMA PGMVSAM'
DISPLAY '******************************************'
DISPLAY 'REG. LEIDOS OFI V0 ........ ' REG-LEIDOS-EN0
DISPLAY 'REG. LEIDOS OFI V-1 ....... ' REG-LEIDOS-EN1
DISPLAY 'REG. GRABADOS OFI VSAM .... ' REG-GRABADOS-VSAM
DISPLAY 'REG. BORRADOS OFI VSAM .... ' REG-BORRADOS-VSAM
DISPLAY 'REG. MODIFIC EN OFI VSAM .. ' REG-MODIF-VSAM
DISPLAY '******************************************'
.
*********************************************
Este ejemplo os lo dejo sin probar, así que puede haber algún error en el código.
Cualquier duda la vemos!
Mostrando entradas con la etiqueta ejemplo. Mostrar todas las entradas
Mostrando entradas con la etiqueta ejemplo. Mostrar todas las entradas
miércoles, 28 de enero de 2015
lunes, 12 de diciembre de 2011
Ejemplo 7: ficheros VB (longitud variable)
En este ejemplo vamos a crear un programa que lee de un fichero de entrada de longitud fija y escriba en un fichero de salida de longitud variable.
JCL:
//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.DE.SALIDA
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//P001 EXEC PGM=PRUEBA7
//SYSOUT DD SYSOUT=*
//ENTRADA DD DSN=FICHERO.DE.ENTRADA,DISP=SHR
//SALIDA DD DSN=FICHERO.DE.SALIDA,
// DISP=(NEW,CATLG,DELETE),SPACE=(TRK,(50,10)),
// DCB=(RECFM=VB,LRECL=107,BLKSIZE=0)
/*
En este caso volvemos a utilizar el IDCAMS para borrar el fichero de salida que se genera en el segundo paso. Se trata de un programa sin DB2, así que utilizamos el EXEC PGM.
Para definir el fichero de entrada "ENTRADA" indicaremos que es un fichero ya existente y compartido al indicar DISP=SHR.
En la SYSOUT veremos los mensajes de error en caso de que los haya.
El fichero de salida se definirá como variable al indicar RECFM=VB, la longitud del fichero será la máxima que pueda tener (pues cada registro medirá diferente) indicada en LRECL=107.
Si sumamos las posiciones de la variable que define el fichero de salida en el programa, REG-SALIDA, veremos que suman 103. La razón de que se indique 107 en el JOB es que para los ficheros de longitud variable, el sistema reserva las 4 primeras posiciones para guardar la longitud, de ahí los 107 (103+4). Veremos más propiedades de los ficheros de longitud variable en otro artículo.
Fichero de entrada:
----+----1-
0000155501
0000155502
0000155503
0000255504
0000255505
0000355506
0000455507
Campo1: código de cliente
Campo2: código de producto
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA7.
*=======================================================*
* PROGRAMA QUE LEE DE FICHERO FB Y
* ESCRIBE EN FICHERO VB
*=======================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
*
FILE-CONTROL.
*
SELECT ENTRADA ASSIGN TO ENTRADA
STATUS IS FS-ENTRADA.
SELECT SALIDA ASSIGN TO SALIDA
STATUS IS FS-SALIDA.
*
DATA DIVISION.
*
FILE SECTION.
*
* Fichero de entrada de longitud fija (F) igual a 11.
FD ENTRADA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 10 CHARACTERS.
01 REG-ENTRADA PIC X(10).
*
* Fichero de salida de longitud variable (V).
FD SALIDA RECORDING MODE IS V
BLOCK CONTAINS 0 RECORDS.
01 REG-SALIDA.
05 REG-CLIENTE PIC 9(5).
05 REG-LONG PIC 9(4) COMP-3.
05 REG-PRODUCTO.
10 PRODUCTO PIC X OCCURS 1 TO 95 TIMES
DEPENDING ON REG-LONG.
*
WORKING-STORAGE SECTION.
* FILE STATUS
01 FS-STATUS.
05 FS-ENTRADA PIC X(2).
88 FS-ENTRADA-OK VALUE '00'.
88 FS-FICHERO1-EOF VALUE '10'.
05 FS-SALIDA PIC X(2).
88 FS-SALIDA-OK VALUE '00'.
*
* VARIABLES
01 WB-FIN-ENTRADA PIC X(1) VALUE 'N'.
88 FIN-ENTRADA VALUE 'S'.
*
01 WI-PRODUCTO PIC 9(3) COMP-3.
01 WX-CLIENTE-ANT PIC 9(5).
*
01 WX-REGISTRO-ENTRADA.
05 WX-ENT-CLIENTE PIC 9(5).
05 WX-ENT-PRODUCTO PIC X(5).
*
01 WX-REGISTRO-SALIDA.
05 WX-SAL-PRODUCTO PIC X(5) OCCURS 19 TIMES.
*
************************************************************
PROCEDURE DIVISION.
************************************************************
* | 0000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA
************************************************************
00000-PRINCIPAL.
*
PERFORM 10000-INICIO
*
PERFORM 20000-PROCESO
UNTIL FIN-ENTRADA
*
PERFORM 30000-FINAL
.
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
* 2| Primera lectura de SYSIN
************************************************************
10000-INICIO.
*
INITIALIZE WX-REGISTRO-SALIDA
PERFORM 11000-ABRIR-FICHERO
PERFORM LEER-ENTRADA
IF FIN-ENTRADA
DISPLAY 'FICHERO DE ENTRADA VACIO'
PERFORM 30000-FINAL
END-IF
MOVE WX-ENT-CLIENTE TO WX-CLIENTE-ANT
MOVE ZEROES TO WI-PRODUCTO
.
*
************************************************************
* 11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* Abrimos los ficheros del programa
************************************************************
11000-ABRIR-FICHEROS.
*
OPEN INPUT ENTRADA
OUTPUT SALIDA
*
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN OPEN DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN OPEN DE SALIDA:'FS-SALIDA
END-IF
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro recuperado de
* | la ENTRADA
************************************************************
20000-PROCESO.
*
IF WX-ENT-CLIENTE EQUAL WX-CLIENTE-ANT
*Para un mismo cliente, guardamos sus codigos de producto
PERFORM 21000-GUARDAR-PRODUCTO
ELSE
*Al cambiar de cliente, escribimos el registro con
*los productos del cliente anterior
PERFORM 22000-INFORMAR-SALIDA
PERFORM ESCRIBIR-SALIDA
*Inicializamos las variables de trabajo
MOVE ZEROES TO WI-PRODUCTO
MOVE SPACES TO WX-REGISTRO-SALIDA
*Guardamos el siguiente cliente que vamos a tratar
MOVE WX-ENT-CLIENTE TO WX-CLIENTE-ANT
*Guardamos el codigo de producto del siguiente cliente
PERFORM 21000-GUARDAR-PRODUCTO
END-IF
PERFORM LEER-ENTRADA
.
*
************************************************************
* 21000-GUARDAR-PRODUCTO
*--|------------------+----------><----------+-------------*
* GUARDAMOS EL CODIGO DE PRODUCTO PARA UN MISMO CLIENTE
* EN LA TABLA WX-REG-SALIDA
************************************************************
21000-GUARDAR-PRODUCTO.
*
ADD 1 TO WI-PRODUCTO
MOVE WX-ENT-PRODUCTO
TO WX-SAL-PRODUCTO(WI-PRODUCTO)
.
*
************************************************************
* 22000-INFORMAR-SALIDA
*--|------------------+----------><----------+-------------*
* INFORMAMOS LOS CAMPOS DEL FICHERO DE SALIDA
* 1 * COMO HEMOS CAMBIADO DE CLIENTE, REG-CLIENTE SERA EL
* ALMACENADO EN WX-CLIENTE-ANT
* 2 * CALCULAMOS LA LONGITUD DE REG-PRODUCTO MULTIPLICANDO
* EL NÚMERO DE PRODUCTOS ALMACENADOS POR SU LONGITUD (5)
* 3 * MOVEMOS LOS CODIGOS GUARDADOS EN WX-REGISTRO-SAL
* A REG-PRODUCTO
************************************************************
22000-INFORMAR-SALIDA.
*
*1*
MOVE WX-CLIENTE-ANT TO REG-CLIENTE
*2*
COMPUTE REG-LONG = WI-PRODUCTO * 5
*3*
MOVE WX-REGISTRO-SALIDA(1:REG-LONG)
TO REG-PRODUCTO
.
*
************************************************************
* LEER ENTRADA
*--|------------------+----------><----------+-------------*
* Leemos del fichero de entrada
************************************************************
LEER-ENTRADA.
*
READ ENTRADA INTO WX-REGISTRO-ENTRADA
EVALUATE TRUE
WHEN FS-ENTRADA-OK
CONTINUE
WHEN FS-ENTRADA-EOF
SET FIN-ENTRADA TO TRUE
WHEN OTHER
DISPLAY 'ERROR EN READ DE ENTRADA:'FS-ENTRADA
END-EVALUATE
.
*
************************************************************
* - ESCRIBIR SALIDA
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS EN EL FICHERO DE SALIDA LA INFORMACION GUARDADA
* WX-REGISTRO-SALIDA
************************************************************
ESCRIBIR-SALIDA.
*
WRITE REG-SALIDA
IF FS-SALIDA-OK
INITIALIZE WX-REGISTRO-SALIDA
ELSE
DISPLAY 'ERROR EN WRITE DEL FICHERO:'FS-SALIDA
END-IF
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
*Escribimos la información del último cliente
PERFORM 22000-INFORMAR-SALIDA
PERFORM ESCRIBIR-SALIDA
*Cerramos ficheros
PERFORM 31000-CERRAR-FICHEROS
STOP RUN
.
*
************************************************************
* | 31000 - CERRAR FICHEROS
*--|------------------+----------><----------+-------------*
* | CERRAMOS LOS FICHEROS DEL PROGRAMA
************************************************************
31000-CERRAR-FICHEROS.
*
CLOSE ENTRADA
SALIDA
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN CLOSE DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN CLOSE DE SALIDA:'FS-SALIDA
END-IF
.
Fichero de salida:
----+----1----+----2----+
00001 ¬555015550255503
FFFFF005FFFFFFFFFFFFFFF
0000101F555015550255503
-------------------------
00002 5550455505
FFFFF000FFFFFFFFFF
0000201F5550455505
-------------------------
00003 ¬55506
FFFFF005FFFFF
0000300F55506
-------------------------
00004 ¬55507
FFFFF005FFFFF
0000400F55507
JCL:
//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.DE.SALIDA
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//P001 EXEC PGM=PRUEBA7
//SYSOUT DD SYSOUT=*
//ENTRADA DD DSN=FICHERO.DE.ENTRADA,DISP=SHR
//SALIDA DD DSN=FICHERO.DE.SALIDA,
// DISP=(NEW,CATLG,DELETE),SPACE=(TRK,(50,10)),
// DCB=(RECFM=VB,LRECL=107,BLKSIZE=0)
/*
En este caso volvemos a utilizar el IDCAMS para borrar el fichero de salida que se genera en el segundo paso. Se trata de un programa sin DB2, así que utilizamos el EXEC PGM.
Para definir el fichero de entrada "ENTRADA" indicaremos que es un fichero ya existente y compartido al indicar DISP=SHR.
En la SYSOUT veremos los mensajes de error en caso de que los haya.
El fichero de salida se definirá como variable al indicar RECFM=VB, la longitud del fichero será la máxima que pueda tener (pues cada registro medirá diferente) indicada en LRECL=107.
Si sumamos las posiciones de la variable que define el fichero de salida en el programa, REG-SALIDA, veremos que suman 103. La razón de que se indique 107 en el JOB es que para los ficheros de longitud variable, el sistema reserva las 4 primeras posiciones para guardar la longitud, de ahí los 107 (103+4). Veremos más propiedades de los ficheros de longitud variable en otro artículo.
Fichero de entrada:
----+----1-
0000155501
0000155502
0000155503
0000255504
0000255505
0000355506
0000455507
Campo1: código de cliente
Campo2: código de producto
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA7.
*=======================================================*
* PROGRAMA QUE LEE DE FICHERO FB Y
* ESCRIBE EN FICHERO VB
*=======================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
*
FILE-CONTROL.
*
SELECT ENTRADA ASSIGN TO ENTRADA
STATUS IS FS-ENTRADA.
SELECT SALIDA ASSIGN TO SALIDA
STATUS IS FS-SALIDA.
*
DATA DIVISION.
*
FILE SECTION.
*
* Fichero de entrada de longitud fija (F) igual a 11.
FD ENTRADA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 10 CHARACTERS.
01 REG-ENTRADA PIC X(10).
*
* Fichero de salida de longitud variable (V).
FD SALIDA RECORDING MODE IS V
BLOCK CONTAINS 0 RECORDS.
* Utilizando el depending on hacemos que el último campo
* tome diferentes longitudes dependiendo de REG-LONG 01 REG-SALIDA.
05 REG-CLIENTE PIC 9(5).
05 REG-LONG PIC 9(4) COMP-3.
05 REG-PRODUCTO.
10 PRODUCTO PIC X OCCURS 1 TO 95 TIMES
DEPENDING ON REG-LONG.
*
WORKING-STORAGE SECTION.
* FILE STATUS
01 FS-STATUS.
05 FS-ENTRADA PIC X(2).
88 FS-ENTRADA-OK VALUE '00'.
88 FS-FICHERO1-EOF VALUE '10'.
05 FS-SALIDA PIC X(2).
88 FS-SALIDA-OK VALUE '00'.
*
* VARIABLES
01 WB-FIN-ENTRADA PIC X(1) VALUE 'N'.
88 FIN-ENTRADA VALUE 'S'.
*
01 WI-PRODUCTO PIC 9(3) COMP-3.
01 WX-CLIENTE-ANT PIC 9(5).
*
01 WX-REGISTRO-ENTRADA.
05 WX-ENT-CLIENTE PIC 9(5).
05 WX-ENT-PRODUCTO PIC X(5).
*
01 WX-REGISTRO-SALIDA.
05 WX-SAL-PRODUCTO PIC X(5) OCCURS 19 TIMES.
*
************************************************************
PROCEDURE DIVISION.
************************************************************
* | 0000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA
************************************************************
00000-PRINCIPAL.
*
PERFORM 10000-INICIO
*
PERFORM 20000-PROCESO
UNTIL FIN-ENTRADA
*
PERFORM 30000-FINAL
.
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
* 2| Primera lectura de SYSIN
************************************************************
10000-INICIO.
*
INITIALIZE WX-REGISTRO-SALIDA
PERFORM 11000-ABRIR-FICHERO
PERFORM LEER-ENTRADA
IF FIN-ENTRADA
DISPLAY 'FICHERO DE ENTRADA VACIO'
PERFORM 30000-FINAL
END-IF
MOVE WX-ENT-CLIENTE TO WX-CLIENTE-ANT
MOVE ZEROES TO WI-PRODUCTO
.
*
************************************************************
* 11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* Abrimos los ficheros del programa
************************************************************
11000-ABRIR-FICHEROS.
*
OPEN INPUT ENTRADA
OUTPUT SALIDA
*
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN OPEN DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN OPEN DE SALIDA:'FS-SALIDA
END-IF
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el tratamiento de cada registro recuperado de
* | la ENTRADA
************************************************************
20000-PROCESO.
*
IF WX-ENT-CLIENTE EQUAL WX-CLIENTE-ANT
*Para un mismo cliente, guardamos sus codigos de producto
PERFORM 21000-GUARDAR-PRODUCTO
ELSE
*Al cambiar de cliente, escribimos el registro con
*los productos del cliente anterior
PERFORM 22000-INFORMAR-SALIDA
PERFORM ESCRIBIR-SALIDA
*Inicializamos las variables de trabajo
MOVE ZEROES TO WI-PRODUCTO
MOVE SPACES TO WX-REGISTRO-SALIDA
*Guardamos el siguiente cliente que vamos a tratar
MOVE WX-ENT-CLIENTE TO WX-CLIENTE-ANT
*Guardamos el codigo de producto del siguiente cliente
PERFORM 21000-GUARDAR-PRODUCTO
END-IF
PERFORM LEER-ENTRADA
.
*
************************************************************
* 21000-GUARDAR-PRODUCTO
*--|------------------+----------><----------+-------------*
* GUARDAMOS EL CODIGO DE PRODUCTO PARA UN MISMO CLIENTE
* EN LA TABLA WX-REG-SALIDA
************************************************************
21000-GUARDAR-PRODUCTO.
*
ADD 1 TO WI-PRODUCTO
MOVE WX-ENT-PRODUCTO
TO WX-SAL-PRODUCTO(WI-PRODUCTO)
.
*
************************************************************
* 22000-INFORMAR-SALIDA
*--|------------------+----------><----------+-------------*
* INFORMAMOS LOS CAMPOS DEL FICHERO DE SALIDA
* 1 * COMO HEMOS CAMBIADO DE CLIENTE, REG-CLIENTE SERA EL
* ALMACENADO EN WX-CLIENTE-ANT
* 2 * CALCULAMOS LA LONGITUD DE REG-PRODUCTO MULTIPLICANDO
* EL NÚMERO DE PRODUCTOS ALMACENADOS POR SU LONGITUD (5)
* 3 * MOVEMOS LOS CODIGOS GUARDADOS EN WX-REGISTRO-SAL
* A REG-PRODUCTO
************************************************************
22000-INFORMAR-SALIDA.
*
*1*
MOVE WX-CLIENTE-ANT TO REG-CLIENTE
*2*
COMPUTE REG-LONG = WI-PRODUCTO * 5
*3*
MOVE WX-REGISTRO-SALIDA(1:REG-LONG)
TO REG-PRODUCTO
.
*
************************************************************
* LEER ENTRADA
*--|------------------+----------><----------+-------------*
* Leemos del fichero de entrada
************************************************************
LEER-ENTRADA.
*
READ ENTRADA INTO WX-REGISTRO-ENTRADA
EVALUATE TRUE
WHEN FS-ENTRADA-OK
CONTINUE
WHEN FS-ENTRADA-EOF
SET FIN-ENTRADA TO TRUE
WHEN OTHER
DISPLAY 'ERROR EN READ DE ENTRADA:'FS-ENTRADA
END-EVALUATE
.
*
************************************************************
* - ESCRIBIR SALIDA
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS EN EL FICHERO DE SALIDA LA INFORMACION GUARDADA
* WX-REGISTRO-SALIDA
************************************************************
ESCRIBIR-SALIDA.
*
WRITE REG-SALIDA
IF FS-SALIDA-OK
INITIALIZE WX-REGISTRO-SALIDA
ELSE
DISPLAY 'ERROR EN WRITE DEL FICHERO:'FS-SALIDA
END-IF
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
*Escribimos la información del último cliente
PERFORM 22000-INFORMAR-SALIDA
PERFORM ESCRIBIR-SALIDA
*Cerramos ficheros
PERFORM 31000-CERRAR-FICHEROS
STOP RUN
.
*
************************************************************
* | 31000 - CERRAR FICHEROS
*--|------------------+----------><----------+-------------*
* | CERRAMOS LOS FICHEROS DEL PROGRAMA
************************************************************
31000-CERRAR-FICHEROS.
*
CLOSE ENTRADA
SALIDA
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN CLOSE DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN CLOSE DE SALIDA:'FS-SALIDA
END-IF
.
Fichero de salida:
----+----1----+----2----+
00001 ¬555015550255503
FFFFF005FFFFFFFFFFFFFFF
0000101F555015550255503
-------------------------
00002 5550455505
FFFFF000FFFFFFFFFF
0000201F5550455505
-------------------------
00003 ¬55506
FFFFF005FFFFF
0000300F55506
-------------------------
00004 ¬55507
FFFFF005FFFFF
0000400F55507
miércoles, 22 de junio de 2011
Ejemplo 1: Leer de SYSIN y escribir en SYSPRINT (pl/i).
En PL/I hay que diferenciar entre los programas que acceden a DB2 y los que no, pues se compilarán de maneras diferentes y se ejecutarán de forma diferente.
Enpezaremos por ver el programa sin DB2 más sencillo:
El programa más sencillo es aquel que recibe datos por SYSIN del JCL y los muestra por SYSPRINT.
JCL:
//PROG1 EXEC PGM=PRUEBA1
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ
JAVIER CARBONERO PACO GONZALEZ
JESUS IGLESIAS RICARDO MONTES
/*
donde EXEC PGM= indica el programa SIN DB2 que vamos a ejecutar
SYSPRINT DD SYSOUT=* indica que la información "displayada" se quedará en la cola del SYSPRINT (no lo vamos a guardar en un fichero)
en SYSIN DD * metemos la información que va a recibir el programa
Fijaos en las posiciones de los nombres de la SYSIN, para entender bien el programa:
----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ
JAVIER CARBONERO PACO GONZALEZ
JESUS IGLESIAS RICARDO MONTES
Como veis, hemos dividido la información en 2 trozos de 20 posiciones, cada uno con un nombre. Además hemos escrito varias líneas de SYSIN, pues como vimos en el artículo de Ficheros en PL/I, podemos recuperar varias líneas (en COBOL esto no es posible).
PROGRAMA:
PLIPRU1: PROCEDURE OPTIONS (MAIN);
/* PROGRAMA QUE LEE DE SYSIN(GET EDIT)*/
/* Y ESCRIBE EN SYSPRINT (PUT EDIT) */
/*DEFINIMOS SYSIN*/
DCL SYSIN FILE STREAM INPUT;
/*DEFINIMOS SYSPRINT*/
DCL SYSPRINT FILE PRINT;
/*DECLARACION DE VARIABLES DEL PROGRAMA*/
DCL 1 TABLA_NOMBRES,
2 NOMBRE(6) CHAR(20);
DCL 1 LINEA_SYSIN,
2 NOMBRE_SYSIN1 CHAR(20),
2 NOMBRE_SYSIN2 CHAR(20);
DCL CONT_NOMBRE DEC FIXED (2);
DCL FIN CHAR(1) INIT ('0');
/*CONTROL FIN SYSIN*/
ON ENDFILE(SYSIN) BEGIN;
FIN = '1';
END;
/*PROCESO DEL PROGRAMA*/
GET FILE(SYSIN) EDIT(LINEA_SYSIN)(A(40));
CONT_NOMBRE = 1;
DO WHILE (FIN = '0');
CALL MOVER_A_TABLA;
GET FILE(SYSIN) EDIT(LINEA_SYSIN)(A(40));
END;
CALL PINTAR_NOMBRES;
/*PARRAFO QUE GUARDA LOS NOMBRES RECUPERADOS EN TABLA_NOMBRES*/
MOVER_A_TABLA: PROC;
NOMBRE(CONT_NOMBRE) = NOMBRE_SYSIN1;
CONT_NOMBRE = CONT_NOMBRE + 1;
NOMBRE(CONT_NOMBRE) = NOMBRE_SYSIN2;
CONT_NOMBRE = CONT_NOMBRE + 1;
END MOVER_A_TABLA;
/*PARRAFO QUE ESCRIBE EN EL SYSPRINT LOS NOMBRES DE LA SYSIN*/
PINTAR_NOMBRES: PROC;
CONT_NOMBRE = 1;
DO WHILE CONT_NOMBRE < 7
PUT EDIT('NOMBRE: ', NOMBRE(CONT_NOMBRE)) (SKIP,A,A);
CONT_NOMBRE = CONT_NOMBRE + 1;
END;
END PINTAR_NOMBRES;
END PLIPRU1;
En el programa podemos ver las siguientes sentencias:
ON ENDFILE(SYSIN): controla el final de la SYSIN (como el fin fichero pero para la SYSIN).
GET FILE: lee del fichero STREAM SYSIN.
DO WHILE: es un bucle. Las instrucciones de dentro del bucle se harán MIENTRAS se cumpla la condición indicada.
CALL: llamada a párrafo
PUT EDIT: escribe en el fichero STREAM SYSPRINT.
SKIP: indica salto de línea.
END PLIPRU1: indica fin del programa.
Descripción del programa:
Al inicio, declararemos los ficheros y las variables que utilizaremos a lo largo del programa.
En el proceso principal, leemos el primer registro de la SYSIN y ponemos a 1 el contador CONT_NOMBRE y montamos un bucle para recuperar todos los registros.
En el párrafo MOVER_A_TABLA guardamos los 2 nombres recuperados de la SYSIN en diferentes filas de la tabla TABLA_NOMBRES. Para ello indicamos entre paréntesis el número de la fila donde se va a guardar. El máximo de filas de la tabla es 6 (es el número indicado entre paréntesis al lado del campo NOMBRE).
En el párrafo PINTAR_NOMBRES escribiremos en el SYSPRINT todos los nombres guardados en nuestra TABLA_NOMBRES.
Informamos el campo CONT_NOMBRE con un 1, pues vamos a utilizar los campos de la tabla interna:
Para utilizar un campo que pertenezca a una tabla interna, debemos acompañar el campo de un "índice" entre paréntesis. De tal forma que indiquemos a que fila de la tabla nos estamos refiriendo. Por ejemplo, NOMBRE(1) sería el primer nombre guardado (JOSE LOPEZ VAZQUEZ).
Como queremos displayar todas las filas de la tabla, haremos que el índice sea una variable que va aumentando.
A continuación montamos un bucle (DO WHILE) con la condición CONT_NOMBRE menor de 7, pues el máximo es 6. La primera vez CONT_NOMBRE valdrá 1, y necesitamos que el bucle se repita 6 veces:
CONT_NOMBRE = 1: NOMBRE(1) = JOSE LOPEZ VAZQUEZ
CONT_NOMBRE = 2: NOMBRE(2) = HUGO CASILLAS DIAZ
CONT_NOMBRE = 3: NOMBRE(3) = JAVIER CARBONERO
CONT_NOMBRE = 4: NOMBRE(4) = PACO GONZALEZ
CONT_NOMBRE = 5: NOMBRE(5) = JESUS IGLESIAS
CONT_NOMBRE = 6: NOMBRE(6) = RICARDO MONTES
CONT_NOMBRE = 7: salimos del bucle porque ya no se cumple CONT_NOMBRE menor que 7
NOTA: el índice de una tabla interna NUNCA puede ser cero, pues no existe la fila cero. Si no informásemos CONT_NOMBRE con 1, el PUT de NOMBRE(0) nos daría un estupendo OFFSET.
RESULTADO:
NOMBRE: JOSE LOPEZ VAZQUEZ
NOMBRE: HUGO CASILLAS DIAZ
NOMBRE: JAVIER CARBONERO
NOMBRE: PACO GONZALEZ
NOMBRE: JESUS IGLESIAS
NOMBRE: RICARDO MONTES
He de decir que no me ha dado tiempo a ejecutar este ejemplo, en cuanto tenga un rato lo pruebo y corrijo si es necesario.
Enpezaremos por ver el programa sin DB2 más sencillo:
El programa más sencillo es aquel que recibe datos por SYSIN del JCL y los muestra por SYSPRINT.
JCL:
//PROG1 EXEC PGM=PRUEBA1
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ
JAVIER CARBONERO PACO GONZALEZ
JESUS IGLESIAS RICARDO MONTES
/*
donde EXEC PGM= indica el programa SIN DB2 que vamos a ejecutar
SYSPRINT DD SYSOUT=* indica que la información "displayada" se quedará en la cola del SYSPRINT (no lo vamos a guardar en un fichero)
en SYSIN DD * metemos la información que va a recibir el programa
Fijaos en las posiciones de los nombres de la SYSIN, para entender bien el programa:
----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8
JOSE LOPEZ VAZQUEZ HUGO CASILLAS DIAZ
JAVIER CARBONERO PACO GONZALEZ
JESUS IGLESIAS RICARDO MONTES
Como veis, hemos dividido la información en 2 trozos de 20 posiciones, cada uno con un nombre. Además hemos escrito varias líneas de SYSIN, pues como vimos en el artículo de Ficheros en PL/I, podemos recuperar varias líneas (en COBOL esto no es posible).
PROGRAMA:
PLIPRU1: PROCEDURE OPTIONS (MAIN);
/* PROGRAMA QUE LEE DE SYSIN(GET EDIT)*/
/* Y ESCRIBE EN SYSPRINT (PUT EDIT) */
/*DEFINIMOS SYSIN*/
DCL SYSIN FILE STREAM INPUT;
/*DEFINIMOS SYSPRINT*/
DCL SYSPRINT FILE PRINT;
/*DECLARACION DE VARIABLES DEL PROGRAMA*/
DCL 1 TABLA_NOMBRES,
2 NOMBRE(6) CHAR(20);
DCL 1 LINEA_SYSIN,
2 NOMBRE_SYSIN1 CHAR(20),
2 NOMBRE_SYSIN2 CHAR(20);
DCL CONT_NOMBRE DEC FIXED (2);
DCL FIN CHAR(1) INIT ('0');
/*CONTROL FIN SYSIN*/
ON ENDFILE(SYSIN) BEGIN;
FIN = '1';
END;
/*PROCESO DEL PROGRAMA*/
GET FILE(SYSIN) EDIT(LINEA_SYSIN)(A(40));
CONT_NOMBRE = 1;
DO WHILE (FIN = '0');
CALL MOVER_A_TABLA;
GET FILE(SYSIN) EDIT(LINEA_SYSIN)(A(40));
END;
CALL PINTAR_NOMBRES;
/*PARRAFO QUE GUARDA LOS NOMBRES RECUPERADOS EN TABLA_NOMBRES*/
MOVER_A_TABLA: PROC;
NOMBRE(CONT_NOMBRE) = NOMBRE_SYSIN1;
CONT_NOMBRE = CONT_NOMBRE + 1;
NOMBRE(CONT_NOMBRE) = NOMBRE_SYSIN2;
CONT_NOMBRE = CONT_NOMBRE + 1;
END MOVER_A_TABLA;
/*PARRAFO QUE ESCRIBE EN EL SYSPRINT LOS NOMBRES DE LA SYSIN*/
PINTAR_NOMBRES: PROC;
CONT_NOMBRE = 1;
DO WHILE CONT_NOMBRE < 7
PUT EDIT('NOMBRE: ', NOMBRE(CONT_NOMBRE)) (SKIP,A,A);
CONT_NOMBRE = CONT_NOMBRE + 1;
END;
END PINTAR_NOMBRES;
END PLIPRU1;
En el programa podemos ver las siguientes sentencias:
ON ENDFILE(SYSIN): controla el final de la SYSIN (como el fin fichero pero para la SYSIN).
GET FILE: lee del fichero STREAM SYSIN.
DO WHILE: es un bucle. Las instrucciones de dentro del bucle se harán MIENTRAS se cumpla la condición indicada.
CALL: llamada a párrafo
PUT EDIT: escribe en el fichero STREAM SYSPRINT.
SKIP: indica salto de línea.
END PLIPRU1: indica fin del programa.
Descripción del programa:
Al inicio, declararemos los ficheros y las variables que utilizaremos a lo largo del programa.
En el proceso principal, leemos el primer registro de la SYSIN y ponemos a 1 el contador CONT_NOMBRE y montamos un bucle para recuperar todos los registros.
En el párrafo MOVER_A_TABLA guardamos los 2 nombres recuperados de la SYSIN en diferentes filas de la tabla TABLA_NOMBRES. Para ello indicamos entre paréntesis el número de la fila donde se va a guardar. El máximo de filas de la tabla es 6 (es el número indicado entre paréntesis al lado del campo NOMBRE).
En el párrafo PINTAR_NOMBRES escribiremos en el SYSPRINT todos los nombres guardados en nuestra TABLA_NOMBRES.
Informamos el campo CONT_NOMBRE con un 1, pues vamos a utilizar los campos de la tabla interna:
Para utilizar un campo que pertenezca a una tabla interna, debemos acompañar el campo de un "índice" entre paréntesis. De tal forma que indiquemos a que fila de la tabla nos estamos refiriendo. Por ejemplo, NOMBRE(1) sería el primer nombre guardado (JOSE LOPEZ VAZQUEZ).
Como queremos displayar todas las filas de la tabla, haremos que el índice sea una variable que va aumentando.
A continuación montamos un bucle (DO WHILE) con la condición CONT_NOMBRE menor de 7, pues el máximo es 6. La primera vez CONT_NOMBRE valdrá 1, y necesitamos que el bucle se repita 6 veces:
CONT_NOMBRE = 1: NOMBRE(1) = JOSE LOPEZ VAZQUEZ
CONT_NOMBRE = 2: NOMBRE(2) = HUGO CASILLAS DIAZ
CONT_NOMBRE = 3: NOMBRE(3) = JAVIER CARBONERO
CONT_NOMBRE = 4: NOMBRE(4) = PACO GONZALEZ
CONT_NOMBRE = 5: NOMBRE(5) = JESUS IGLESIAS
CONT_NOMBRE = 6: NOMBRE(6) = RICARDO MONTES
CONT_NOMBRE = 7: salimos del bucle porque ya no se cumple CONT_NOMBRE menor que 7
NOTA: el índice de una tabla interna NUNCA puede ser cero, pues no existe la fila cero. Si no informásemos CONT_NOMBRE con 1, el PUT de NOMBRE(0) nos daría un estupendo OFFSET.
RESULTADO:
NOMBRE: JOSE LOPEZ VAZQUEZ
NOMBRE: HUGO CASILLAS DIAZ
NOMBRE: JAVIER CARBONERO
NOMBRE: PACO GONZALEZ
NOMBRE: JESUS IGLESIAS
NOMBRE: RICARDO MONTES
He de decir que no me ha dado tiempo a ejecutar este ejemplo, en cuanto tenga un rato lo pruebo y corrijo si es necesario.
miércoles, 23 de marzo de 2011
Ejemplo 4: generando un listado.
Un programa típico que nos encontraremos en cualquier aplicación es aquel que genera un listado. ¿Que a qué nos referimos con listado? Mejor verlo para hacernos una idea:
----+----1----+----2----+----3----+----4----+
LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 23-03-11 PAGINA: 1
------------------------------------------
NOMBRE APELLIDO
--------- ---------------
ANA LOPEZ
ANA MARTINEZ
ANA PEREZ
ANA RODRIGUEZ
TOTAL ANA : 04
LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 23-03-11 PAGINA: 2
------------------------------------------
NOMBRE APELLIDO
--------- ---------------
BEATRIZ GARCIA
BEATRIZ MOREDA
BEATRIZ OTERO
TOTAL BEATRIZ : 03
TOTAL NOMBRES: 07
Aquí vemos un listado de 2 páginas. Ambas páginas tienen una parte común que se denomina cabecera y que, por lo general, será la misma en todas las páginas. Suele contener un título que describa al listado y la fecha en que ha sido generado. Lo único que cambia es el número de página en el que estamos^^
Después tenemos una "subcabecera" también común en cada página, en este caso la subcabecera incluye a "NOMBRE" y "APELLIDO".
En nuestro listado de ejemplo hemos querido que cada nombre salga en una página distinta. Al final de cada página de nombre escribimos un "subtotal" con el número de registros que hemos escrito para ese nombre.
Al final del listado escribiremos una linea de "totales" con el total de registros escritos.
El fichero de partida para este programa será el siguiente:
----+----1----+----2
ANA LOPEZ
ANA MARTINEZ
ANA PEREZ
ANA RODRIGUEZ
BEATRIZ GARCIA
BEATRIZ MOREDA
BEATRIZ OTERO
Los ficheros que se usan en listados siempre están ordenados por algún campo. En nuestro caso por "nombre" y "apellido". En el JCL incluiremos el paso de SORT para ordenarlo.
Vamos allá!
Fichero de entrada desordenado:
----+----1----+----2
ANA LOPEZ
BEATRIZ MOREDA
ANA PEREZ
BEATRIZ OTERO
ANA RODRIGUEZ
BEATRIZ GARCIA
ANA MARTINEZ
JCL:
//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.NOMBRES.APELLIDO.ORDENADO
DEL FICHERO.CON.LISTADO
SET MAXCC = 0
//******************************************************
//* ORDENAMOS EL FICHERO POR NOMBRE Y APELLIDO *********
//SORT01 EXEC PGM=SORT
//SORTIN DD DSN=FICHERO.NOMBRES.APELLIDO,DISP=SHR
//SORTOUT DD DSN=FICHERO.NOMBRES.APELLIDO.ORDENADO,
// DISP=(,CATLG),SPACE=(TRK,(50,10))
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
SORT FIELDS=(1,9,CH,A,10,10,CH,A)
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//PROG4 EXEC PGM=PRUEBA4
//SYSOUT DD SYSOUT=*
//ENTRADA DD DSN=FICHERO.NOMBRES.APELLIDO.ORDENADO,DISP=SHR
//SALIDA DD DSN=FICHERO.CON.LISTADO,
// DISP=(NEW, CATLG, DELETE),SPACE=(TRK,(50,10)),
// DCB=(RECFM=FBA,LRECL=133)
/*
En este JCL tenemos 3 pasos:
Paso 1: Borrado de ficheros que se generan durante la ejecución, visto ya en otros ejemplos.
Paso 2: Ordenación del fichero de entrada usando el SORT. Toda la información sobre el SORT la tenéis en SORT vol.1: SORT, INCLUDE.
Paso 3: Ejecución del programa que genera el listado. Tenemos como fichero de entrada el fichero de salida del SORT, y como fichero de salida ojito: indicaremos RECFM=FBA siempre para listados. Esto significa que el fichero contiene caracteres ASA, que son los que le indican a la impresora los saltos de línea que tiene que hacer al imprimir. Lo iremos viendo con el programa de ejemplo. La longitud del fichero(LRECL) suele ser 133, debido a que se imprimen en hojas A4 en formato apaisado.
Programa:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA3.
*==========================================================*
* PROGRAMA QUE LEE DE FICHERO Y ESCRIBE EN FICHERO
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
*
FILE-CONTROL.
*
SELECT ENTRADA ASSIGN TO ENTRADA
STATUS IS FS-ENTRADA.
SELECT LISTADO ASSIGN TO LISTADO
STATUS IS FS-LISTADO.
*
DATA DIVISION.
*
FILE SECTION.
*
* FICHERO DE ENTRADA DE LONGITUD FIJA (F) IGUAL A 20.
FD ENTRADA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 20 CHARACTERS.
01 REG-ENTRADA PIC X(20).
*
* FICHERO DE LISTADO DE LONGITUD FIJA (F) IGUAL A 132.
FD LISTADO RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 132 CHARACTERS.
01 REG-LISTADO PIC X(132).
*
WORKING-STORAGE SECTION.
*
* FILE STATUS
*
01 FS-STATUS.
05 FS-ENTRADA PIC X(2).
88 FS-ENTRADA-OK VALUE '00'.
88 FS-ENTRADA-EOF VALUE '10'.
05 FS-LISTADO PIC X(2).
88 FS-LISTADO-OK VALUE '00'.
*
* SWITCHES
*
01 WB-FIN-ENTRADA PIC X(1) VALUE 'N'.
88 FIN-ENTRADA VALUE 'S'.
*
* CONTADORES
*
01 WC-LINEAS PIC 9(2).
01 WC-NOMBRES PIC 9(2).
01 WC-TOTALES PIC 9(2).
*
* VARIABLES
*
01 WX-REGISTRO-ENTRADA.
05 WX-NOMBRE PIC X(9).
05 WX-APELLIDO PIC X(10).
01 WX-NOMBRE-ANT PIC X(9).
01 WX-FEC-DDMMAA.
05 WX-FEC-DD PIC 9(2).
05 FILLER PIC X VALUE '-'.
05 WX-FEC-MM PIC 9(2).
05 FILLER PIC X VALUE '-'.
05 WX-FEC-AA PIC 9(2).
01 WX-FECHA PIC 9(6).
*
* REGISTRO LISTADO
*
01 CABECERA1.
05 FILLER PIC X(40)
VALUE 'LISTADO DE EJEMPLO DEL CONSULTORIO
- 'COBOL'.
01 CABECERA2.
05 FILLER PIC X(5) VALUE 'DIA: '.
05 LT-FECHA PIC X(8).
05 FILLER PIC X(16) VALUE ALL SPACES.
05 FILLER PIC X(8) VALUE 'PAGINA: '.
05 LT-NUMPAG PIC 9.
*
01 CABECERA3.
05 FILLER PIC X(42) VALUE ALL '-'.
*
01 SUBCABECERA1.
05 FILLER PIC X(6) VALUE 'NOMBRE'.
05 FILLER PIC X(9) VALUE ALL SPACES.
05 FILLER PIC X(8) VALUE 'APELLIDO'.
*
01 SUBCABECERA2.
05 FILLER PIC X(9) VALUE ALL '-'.
05 FILLER PIC X(3) VALUE ALL SPACES.
05 FILLER PIC X(15) VALUE ALL '-'.
*
01 DETALLE.
05 LT-NOMBRE PIC X(9).
05 FILLER PIC X(3) VALUE ALL SPACES.
05 LT-APELLIDO PIC X(15).
*
01 SUBTOTAL.
05 FILLER PIC X(6) VALUE 'TOTAL '.
05 LT-NOMTOT PIC X(9).
05 FILLER PIC X(2) VALUE ': '.
05 LT-NUMNOM PIC 9(2).
01 TOTALES.
05 FILLER PIC X(15) VALUE 'TOTAL NOMBRES: '.
05 LT-TOTALES PIC 9(2).
*
************************************************************
PROCEDURE DIVISION.
************************************************************
* | 0000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA ************************************************************
00000-PRINCIPAL.
*
PERFORM 10000-INICIO
*
PERFORM 20000-PROCESO
UNTIL FIN-ENTRADA
*
PERFORM 30000-FINAL
.
*
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| INICIALIZACIóN DE ÁREAS DE TRABAJO
* 2| PRIMERA LECTURA DEL FICHERO DE ENTRADA
* 3| INFORMAMOS CABECERA Y ESCRIBIMOS CABECERA
************************************************************
10000-INICIO.
*
INITIALIZE DETALLE
WX-REGISTRO-ENTRADA
PERFORM 11000-ABRIR-FICHEROS
PERFORM LEER-ENTRADA
IF FIN-ENTRADA
DISPLAY 'FICHERO DE ENTRADA VACIO'
PERFORM 30000-FINAL
END-IF
MOVE WX-NOMBRE TO WX-NOMBRE-ANT
PERFORM INFORMAR-CABECERA
PERFORM ESCRIBIR-CABECERAS
.
*
************************************************************
* 11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* ABRIMOS LOS FICHEROS DEL PROGRAMA ************************************************************
11000-ABRIR-FICHEROS.
*
OPEN INPUT ENTRADA
OUTPUT LISTADO
*
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN OPEN DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-LISTADO-OK
DISPLAY 'ERROR EN OPEN DE LISTADO:'FS-LISTADO
END-IF
.
*
************************************************************
* 12000 - INFORMAR CABECERA
*--|------------------+----------><----------+-------------*
* INFORMAMOS EL CAMPO FECHA DE LA CABECERA ************************************************************
INFORMAR-CABECERA.
*
ACCEPT WX-FECHA FROM DATE
MOVE WX-FECHA(1:2) TO WX-FEC-AA
MOVE WX-FECHA(3:2) TO WX-FEC-MM
MOVE WX-FECHA(5:2) TO WX-FEC-DD
MOVE WX-FEC-DDMMAA TO LT-FECHA
* Inicializamos el contador de páginas
MOVE 1 TO LT-NUMPAG
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| ESCRIBIMOS LAS LINEAS DE DETALLE Y CONTROLAMOS LOS
* | SALTOS DE PAGINA
************************************************************
20000-PROCESO.
*
INITIALIZE DETALLE
IF WC-LINEAS GREATER 64
ADD 1 TO LT-NUMPAG
PERFORM ESCRIBIR-CABECERAS
END-IF
IF WX-NOMBRE NOT EQUAL WX-NOMBRE-ANT
PERFORM ESCRIBIR-SUBTOTAL
ADD 1 TO LT-NUMPAG
PERFORM ESCRIBIR-CABECERAS
MOVE ZEROES TO WC-NOMBRES
END-IF
PERFORM 21000-INFORMAR-DETALLE
PERFORM ESCRIBIR-DETALLE
MOVE WX-NOMBRE TO WX-NOMBRE-ANT
PERFORM LEER-ENTRADA
.
*
************************************************************
* ESCRIBIR CABECERAS
*--|------------------+----------><----------+-------------*
* ESCRIBE LA CABECERA Y SUBCABECERA DEL LISTADO ************************************************************
ESCRIBIR-CABECERAS.
*
WRITE REG-LISTADO FROM CABECERA1 AFTER ADVANCING PAGE
WRITE REG-LISTADO FROM CABECERA2
WRITE REG-LISTADO FROM CABECERA3
WRITE REG-LISTADO FROM SUBCABECERA1 AFTER ADVANCING 2 LINES
WRITE REG-LISTADO FROM SUBCABECERA2
MOVE 6 TO WC-LINEAS
.
*
************************************************************
* ESCRIBIR SUBTOTAL
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS LINEA DE SUBTOTAL ************************************************************
ESCRIBIR-SUBTOTAL.
*
MOVE WX-NOMBRE-ANT TO LT-NOMTOT
MOVE WC-NOMBRES TO LT-NUMNOM
WRITE REG-LISTADO FROM SUBTOTAL AFTER ADVANCING 2 LINES
.
*
************************************************************
* 21000 INFORMAR DETALLE
*--|------------------+----------><----------+-------------*
* INFORMAMOS LOS CAMPOS DE LA LINEA DE DETALLE CON LA
* INFORMACION DEL FICHERO DE ENTRADA ************************************************************
21000-INFORMAR-DETALLE.
*
MOVE WX-NOMBRE TO LT-NOMBRE
MOVE WX-APELLIDO TO LT-APELLIDO
.
*
************************************************************
* ESCRIBIR DETALLE
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS LA LINEA DE DETALLE ************************************************************
ESCRIBIR-DETALLE.
*
WRITE REG-LISTADO FROM DETALLE
IF NOT FS-LISTADO-OK
DISPLAY 'ERROR AL ESCRIBIR DETALLE:'FS-LISTADO
END-IF
ADD 1 TO WC-LINEAS
ADD 1 TO WC-NOMBRES
ADD 1 TO WC-TOTALES
.
*
************************************************************
* LEER ENTRADA
*--|------------------+----------><----------+-------------*
* LEEMOS DEL FICHERO DE ENTRADA ************************************************************
LEER-ENTRADA.
*
READ ENTRADA INTO WX-REGISTRO-ENTRADA
EVALUATE TRUE
WHEN FS-ENTRADA-OK
CONTINUE
WHEN FS-ENTRADA-EOF
SET FIN-ENTRADA TO TRUE
WHEN OTHER
DISPLAY 'ERROR EN READ DE ENTRADA:'FS-ENTRADA
END-EVALUATE
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA ************************************************************
30000-FINAL.
*
IF WC-LINEAS GREATER 60
ADD 1 TO LT-NUMPAG
PERFORM ESCRIBIR-CABECERAS
PERFORM ESCRIBIR-SUBTOTAL
PERFORM ESCRIBIR-TOTALES
ELSE
PERFORM ESCRIBIR-SUBTOTAL
PERFORM ESCRIBIR-TOTALES
END-IF
PERFORM 31000-CERRAR-FICHEROS
STOP RUN
.
*
************************************************************
* | ESCRIBIR TOTALES
*--|------------------+----------><----------+-------------*
* | ESCRIBIMOS LA LINEA DE TOTALES DEL LISTADO ************************************************************
ESCRIBIR-TOTALES.
*
MOVE WC-TOTALES TO LT-TOTALES
WRITE REG-LISTADO FROM TOTALES
.
*
************************************************************
* | 31000 - CERRAR FICHEROS
*--|------------------+----------><----------+-------------*
* | CERRAMOS LOS FICHEROS DEL PROGRAMA
************************************************************
31000-CERRAR-FICHEROS.
*
CLOSE ENTRADA
LISTADO
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN CLOSE DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-LISTADO-OK
DISPLAY 'ERROR EN CLOSE DE LISTADO:'FS-LISTADO
END-IF
.
*
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
INPUT-OUTPUT SECTION: en este ejemplo existirá porque utilizamos un fichero de entrada y uno de salida.
DATA DIVISION: existirá siempre.
FILE SECTION: en este ejemplo existirá pues utilizamos un fichero de entrada y uno de salida.
WORKING-STORAGE SECTION: exisitirá siempre.
En este caso no exisistirá la LINKAGE SECTION pues el programa no se comunica con otros programas.
PROCEDURE DIVISION: exisitirá siempre.
En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
OPEN: "Abre" los ficheros del programa. Lo acompañaremos de "INPUT" para los ficheros de entrada y "OUTPUT" para los ficheros de salida.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
MOVE/TO: movemos la información de un campo a otro.
PERFORM UNTIL: bucle
SET:Activa los niveles 88 de un campo tipo "switch".
READ: Lee cada registro del fichero de entrada. En el "INTO" le indicamos donde debe guardar la información.
EVALUATE TRUE: "Evalúa" si los niveles 88 por los que preguntamos en el "WHEN" están activados a "TRUE".
WRITE: Escribe la información indicada en el "FROM" en el fichero indicado.
STOP RUN: sentencia de finalización de ejecución.
CLOSE: "Cierra" los ficheros del programa.
Descripción del programa:
En el párrafo de inicio, inicializamos el registro de salida: WX-REGISTRO-SALIDA
Abriremos los ficheros del programa (OPEN INPUT para la entrada, y OUTPUT para la salida) y controlaremos el file-status. Si todo va bien el código del file-status valdrá '00'. Podéis ver la lista de los file-status más comunes.
Leemos el primer registro del fichero de entrada y controlaremos el file-status.
Además comprobamos que el fichero de entrada no venga vacío (en caso de que así sea, finalizamos la ejecución). Si todo ha ido OK guardaremos el nombre leído del fichero de entrada en WX-NOMBRE-ANT para controlar posteriormente el momento en que cambiemos de nombre.
Informaremos la parte genérica de la cabecera e inicializamos el contador de páginas. Escribimos la cabecera por primera vez.
INFORMAR-CABECERA:
Recoge la fecha del sistema mediante un ACCEPT. La variable "DATE" es la fecha del sistema en formato 9(6) AAMMDD.
Para informar la fecha del listado formateamos la recibida del sistema a formato DD-MM-AA.
ESCRIBIR-CABECERAS:
Escribe las lineas de cabecera CABECERA1, CABECERA2, CABECERA3, SUBCABECERA1 y SUBCABECERA2.
En el WRITE de CABECERA1 vemos que utilizamos el "AFTER ADVANCING PAGE", esto significa que esta línea se escribirá en una página nueva. Lo podemos ver en el caracter ASA que aparece a la izquierda de esta linea en el fichero de salida que será un '1'.
En el WRITE de SUBCABECERA1 vemos que utilizamos "AFTER ADVANCING 2 LINES", esto significa que se escribirá una línea en blanco y después la línea de SUBCABECERA1. El caracter ASA que aparecerá será un '0'.
Informamos el contador de líneas a '6', pues la cabecera ocupa 6 líneas.
En el párrafo de proceso, que se repetirá hasta que se termine el fichero de entrada (FIN-ENTRADA), controlaremos:
Por un lado el número de líneas escritas, y en caso de superar un máximo (64 en nusetro caso) escribiremos otra vez las cabeceras en una página nueva (ver párrado ESCRIBIR-CABECERAS).
Por otro lado la variable WX-NOMBRE, pues queremos escribir cada nombre en una página distinta. Cuando cambie el nombre escribiremos la línea de subtotales y volveremos a escribir cabeceras (que nos hará el salto de página).
Informaremos la linea de detalle y escribiremos en el fichero del listado.
Guardamos el último nombre escrito en WX-NOMBRE-ANT para controlar el cambio de nombre.
Leemos el siguiente registro del fichero de entrada.
Llamadas a párrafos:
ESCRIBIR-SUBTOTAL:
Informará el campo LT-NOMTOT con el nombre que hemos estado escribiendo, y LT-NUMNOM con el contador de registros escritos para ese nombre.
Escribirá SUBTOTALES dejando antes una línea en blanco (AFTER ADVANCING 2 LINES).
21000-INFORMAR-DETALLE:
Informamos los campos de la línea de detalle LT-NOMBRE y LT-APELLIDO con los campos de lfichero de entrada WX-NOMBRE y WX-APELLIDO.
ESCRIBIR-DETALLE:
Escribe la linea de detalle en el fichero del listado. Controlamos file-status y añadimos uno a los contadores.
En el párrado de FINAL, finalizaremos el programa escribiendo la línea de subtotales que falta, y la línea de totales generales.
Controlaremos el número de líneas que llevamos escritas, pues para escribir la línea SUBTOTALES y TOTALES necesitamos 3 líneas.
En el párrafo de ESCRIBIR-TOTALES informaremos el campo LT-TOTALES con el contador de registros escritos en total.
Al inicio del artículo veiamos como quedaría el listado una vez "impreso", ya sea por pantalla o en papel. Vamos a ver como quedaría el fichero con los caracteres ASA que indican los saltos de linea.
Fichero de salida:
----+----1----+----2----+----3----+----4----+
1LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 22-03-11 PAGINA: 1
------------------------------------------
0NOMBRE APELLIDO
--------- ---------------
ANA LOPEZ
ANA MARTINEZ
ANA PEREZ
ANA RODRIGUEZ
0TOTAL ANA : 04
1LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 22-03-11 PAGINA: 2
------------------------------------------
0NOMBRE APELLIDO
--------- ---------------
BEATRIZ GARCIA
BEATRIZ MOREDA
BEATRIZ OTERO
0TOTAL BEATRIZ : 03
TOTAL NOMBRES: 07
Donde:
1 = salto de página.
0 = deja una línea en blanco justo antes.
- = deja dos líneas en blanco.
espacio = escribe sin dejar lineas en blanco.
Y listo! Si veis que no me he parado mucho en alguna cosa y queréis que explique más en detalle, dejad un comentario y lo vemos : )
----+----1----+----2----+----3----+----4----+
LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 23-03-11 PAGINA: 1
------------------------------------------
NOMBRE APELLIDO
--------- ---------------
ANA LOPEZ
ANA MARTINEZ
ANA PEREZ
ANA RODRIGUEZ
TOTAL ANA : 04
LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 23-03-11 PAGINA: 2
------------------------------------------
NOMBRE APELLIDO
--------- ---------------
BEATRIZ GARCIA
BEATRIZ MOREDA
BEATRIZ OTERO
TOTAL BEATRIZ : 03
TOTAL NOMBRES: 07
Aquí vemos un listado de 2 páginas. Ambas páginas tienen una parte común que se denomina cabecera y que, por lo general, será la misma en todas las páginas. Suele contener un título que describa al listado y la fecha en que ha sido generado. Lo único que cambia es el número de página en el que estamos^^
Después tenemos una "subcabecera" también común en cada página, en este caso la subcabecera incluye a "NOMBRE" y "APELLIDO".
En nuestro listado de ejemplo hemos querido que cada nombre salga en una página distinta. Al final de cada página de nombre escribimos un "subtotal" con el número de registros que hemos escrito para ese nombre.
Al final del listado escribiremos una linea de "totales" con el total de registros escritos.
El fichero de partida para este programa será el siguiente:
----+----1----+----2
ANA LOPEZ
ANA MARTINEZ
ANA PEREZ
ANA RODRIGUEZ
BEATRIZ GARCIA
BEATRIZ MOREDA
BEATRIZ OTERO
Los ficheros que se usan en listados siempre están ordenados por algún campo. En nuestro caso por "nombre" y "apellido". En el JCL incluiremos el paso de SORT para ordenarlo.
Vamos allá!
Fichero de entrada desordenado:
----+----1----+----2
ANA LOPEZ
BEATRIZ MOREDA
ANA PEREZ
BEATRIZ OTERO
ANA RODRIGUEZ
BEATRIZ GARCIA
ANA MARTINEZ
JCL:
//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.NOMBRES.APELLIDO.ORDENADO
DEL FICHERO.CON.LISTADO
SET MAXCC = 0
//******************************************************
//* ORDENAMOS EL FICHERO POR NOMBRE Y APELLIDO *********
//SORT01 EXEC PGM=SORT
//SORTIN DD DSN=FICHERO.NOMBRES.APELLIDO,DISP=SHR
//SORTOUT DD DSN=FICHERO.NOMBRES.APELLIDO.ORDENADO,
// DISP=(,CATLG),SPACE=(TRK,(50,10))
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
SORT FIELDS=(1,9,CH,A,10,10,CH,A)
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//PROG4 EXEC PGM=PRUEBA4
//SYSOUT DD SYSOUT=*
//ENTRADA DD DSN=FICHERO.NOMBRES.APELLIDO.ORDENADO,DISP=SHR
//SALIDA DD DSN=FICHERO.CON.LISTADO,
// DISP=(NEW, CATLG, DELETE),SPACE=(TRK,(50,10)),
// DCB=(RECFM=FBA,LRECL=133)
/*
En este JCL tenemos 3 pasos:
Paso 1: Borrado de ficheros que se generan durante la ejecución, visto ya en otros ejemplos.
Paso 2: Ordenación del fichero de entrada usando el SORT. Toda la información sobre el SORT la tenéis en SORT vol.1: SORT, INCLUDE.
Paso 3: Ejecución del programa que genera el listado. Tenemos como fichero de entrada el fichero de salida del SORT, y como fichero de salida ojito: indicaremos RECFM=FBA siempre para listados. Esto significa que el fichero contiene caracteres ASA, que son los que le indican a la impresora los saltos de línea que tiene que hacer al imprimir. Lo iremos viendo con el programa de ejemplo. La longitud del fichero(LRECL) suele ser 133, debido a que se imprimen en hojas A4 en formato apaisado.
Programa:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA3.
*==========================================================*
* PROGRAMA QUE LEE DE FICHERO Y ESCRIBE EN FICHERO
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
INPUT-OUTPUT SECTION.
*
FILE-CONTROL.
*
SELECT ENTRADA ASSIGN TO ENTRADA
STATUS IS FS-ENTRADA.
SELECT LISTADO ASSIGN TO LISTADO
STATUS IS FS-LISTADO.
*
DATA DIVISION.
*
FILE SECTION.
*
* FICHERO DE ENTRADA DE LONGITUD FIJA (F) IGUAL A 20.
FD ENTRADA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 20 CHARACTERS.
01 REG-ENTRADA PIC X(20).
*
* FICHERO DE LISTADO DE LONGITUD FIJA (F) IGUAL A 132.
FD LISTADO RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 132 CHARACTERS.
01 REG-LISTADO PIC X(132).
*
WORKING-STORAGE SECTION.
*
* FILE STATUS
*
01 FS-STATUS.
05 FS-ENTRADA PIC X(2).
88 FS-ENTRADA-OK VALUE '00'.
88 FS-ENTRADA-EOF VALUE '10'.
05 FS-LISTADO PIC X(2).
88 FS-LISTADO-OK VALUE '00'.
*
* SWITCHES
*
01 WB-FIN-ENTRADA PIC X(1) VALUE 'N'.
88 FIN-ENTRADA VALUE 'S'.
*
* CONTADORES
*
01 WC-LINEAS PIC 9(2).
01 WC-NOMBRES PIC 9(2).
01 WC-TOTALES PIC 9(2).
*
* VARIABLES
*
01 WX-REGISTRO-ENTRADA.
05 WX-NOMBRE PIC X(9).
05 WX-APELLIDO PIC X(10).
01 WX-NOMBRE-ANT PIC X(9).
01 WX-FEC-DDMMAA.
05 WX-FEC-DD PIC 9(2).
05 FILLER PIC X VALUE '-'.
05 WX-FEC-MM PIC 9(2).
05 FILLER PIC X VALUE '-'.
05 WX-FEC-AA PIC 9(2).
01 WX-FECHA PIC 9(6).
*
* REGISTRO LISTADO
*
01 CABECERA1.
05 FILLER PIC X(40)
VALUE 'LISTADO DE EJEMPLO DEL CONSULTORIO
- 'COBOL'.
01 CABECERA2.
05 FILLER PIC X(5) VALUE 'DIA: '.
05 LT-FECHA PIC X(8).
05 FILLER PIC X(16) VALUE ALL SPACES.
05 FILLER PIC X(8) VALUE 'PAGINA: '.
05 LT-NUMPAG PIC 9.
*
01 CABECERA3.
05 FILLER PIC X(42) VALUE ALL '-'.
*
01 SUBCABECERA1.
05 FILLER PIC X(6) VALUE 'NOMBRE'.
05 FILLER PIC X(9) VALUE ALL SPACES.
05 FILLER PIC X(8) VALUE 'APELLIDO'.
*
01 SUBCABECERA2.
05 FILLER PIC X(9) VALUE ALL '-'.
05 FILLER PIC X(3) VALUE ALL SPACES.
05 FILLER PIC X(15) VALUE ALL '-'.
*
01 DETALLE.
05 LT-NOMBRE PIC X(9).
05 FILLER PIC X(3) VALUE ALL SPACES.
05 LT-APELLIDO PIC X(15).
*
01 SUBTOTAL.
05 FILLER PIC X(6) VALUE 'TOTAL '.
05 LT-NOMTOT PIC X(9).
05 FILLER PIC X(2) VALUE ': '.
05 LT-NUMNOM PIC 9(2).
01 TOTALES.
05 FILLER PIC X(15) VALUE 'TOTAL NOMBRES: '.
05 LT-TOTALES PIC 9(2).
*
************************************************************
PROCEDURE DIVISION.
************************************************************
* | 0000 - PRINCIPAL
*--|------------------+----------><----------+-------------*
* 1| EJECUTA EL INICIO DEL PROGRAMA
* 2| EJECUTA EL PROCESO DEL PROGRAMA
* 3| EJECUTA EL FINAL DEL PROGRAMA ************************************************************
00000-PRINCIPAL.
*
PERFORM 10000-INICIO
*
PERFORM 20000-PROCESO
UNTIL FIN-ENTRADA
*
PERFORM 30000-FINAL
.
*
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| INICIALIZACIóN DE ÁREAS DE TRABAJO
* 2| PRIMERA LECTURA DEL FICHERO DE ENTRADA
* 3| INFORMAMOS CABECERA Y ESCRIBIMOS CABECERA
************************************************************
10000-INICIO.
*
INITIALIZE DETALLE
WX-REGISTRO-ENTRADA
PERFORM 11000-ABRIR-FICHEROS
PERFORM LEER-ENTRADA
IF FIN-ENTRADA
DISPLAY 'FICHERO DE ENTRADA VACIO'
PERFORM 30000-FINAL
END-IF
MOVE WX-NOMBRE TO WX-NOMBRE-ANT
PERFORM INFORMAR-CABECERA
PERFORM ESCRIBIR-CABECERAS
.
*
************************************************************
* 11000 - ABRIR FICHEROS
*--|------------------+----------><----------+-------------*
* ABRIMOS LOS FICHEROS DEL PROGRAMA ************************************************************
11000-ABRIR-FICHEROS.
*
OPEN INPUT ENTRADA
OUTPUT LISTADO
*
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN OPEN DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-LISTADO-OK
DISPLAY 'ERROR EN OPEN DE LISTADO:'FS-LISTADO
END-IF
.
*
************************************************************
* 12000 - INFORMAR CABECERA
*--|------------------+----------><----------+-------------*
* INFORMAMOS EL CAMPO FECHA DE LA CABECERA ************************************************************
INFORMAR-CABECERA.
*
ACCEPT WX-FECHA FROM DATE
MOVE WX-FECHA(1:2) TO WX-FEC-AA
MOVE WX-FECHA(3:2) TO WX-FEC-MM
MOVE WX-FECHA(5:2) TO WX-FEC-DD
MOVE WX-FEC-DDMMAA TO LT-FECHA
* Inicializamos el contador de páginas
MOVE 1 TO LT-NUMPAG
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| ESCRIBIMOS LAS LINEAS DE DETALLE Y CONTROLAMOS LOS
* | SALTOS DE PAGINA
************************************************************
20000-PROCESO.
*
INITIALIZE DETALLE
IF WC-LINEAS GREATER 64
ADD 1 TO LT-NUMPAG
PERFORM ESCRIBIR-CABECERAS
END-IF
IF WX-NOMBRE NOT EQUAL WX-NOMBRE-ANT
PERFORM ESCRIBIR-SUBTOTAL
ADD 1 TO LT-NUMPAG
PERFORM ESCRIBIR-CABECERAS
MOVE ZEROES TO WC-NOMBRES
END-IF
PERFORM 21000-INFORMAR-DETALLE
PERFORM ESCRIBIR-DETALLE
MOVE WX-NOMBRE TO WX-NOMBRE-ANT
PERFORM LEER-ENTRADA
.
*
************************************************************
* ESCRIBIR CABECERAS
*--|------------------+----------><----------+-------------*
* ESCRIBE LA CABECERA Y SUBCABECERA DEL LISTADO ************************************************************
ESCRIBIR-CABECERAS.
*
WRITE REG-LISTADO FROM CABECERA1 AFTER ADVANCING PAGE
WRITE REG-LISTADO FROM CABECERA2
WRITE REG-LISTADO FROM CABECERA3
WRITE REG-LISTADO FROM SUBCABECERA1 AFTER ADVANCING 2 LINES
WRITE REG-LISTADO FROM SUBCABECERA2
MOVE 6 TO WC-LINEAS
.
*
************************************************************
* ESCRIBIR SUBTOTAL
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS LINEA DE SUBTOTAL ************************************************************
ESCRIBIR-SUBTOTAL.
*
MOVE WX-NOMBRE-ANT TO LT-NOMTOT
MOVE WC-NOMBRES TO LT-NUMNOM
WRITE REG-LISTADO FROM SUBTOTAL AFTER ADVANCING 2 LINES
.
*
************************************************************
* 21000 INFORMAR DETALLE
*--|------------------+----------><----------+-------------*
* INFORMAMOS LOS CAMPOS DE LA LINEA DE DETALLE CON LA
* INFORMACION DEL FICHERO DE ENTRADA ************************************************************
21000-INFORMAR-DETALLE.
*
MOVE WX-NOMBRE TO LT-NOMBRE
MOVE WX-APELLIDO TO LT-APELLIDO
.
*
************************************************************
* ESCRIBIR DETALLE
*--|------------------+----------><----------+-------------*
* ESCRIBIMOS LA LINEA DE DETALLE ************************************************************
ESCRIBIR-DETALLE.
*
WRITE REG-LISTADO FROM DETALLE
IF NOT FS-LISTADO-OK
DISPLAY 'ERROR AL ESCRIBIR DETALLE:'FS-LISTADO
END-IF
ADD 1 TO WC-LINEAS
ADD 1 TO WC-NOMBRES
ADD 1 TO WC-TOTALES
.
*
************************************************************
* LEER ENTRADA
*--|------------------+----------><----------+-------------*
* LEEMOS DEL FICHERO DE ENTRADA ************************************************************
LEER-ENTRADA.
*
READ ENTRADA INTO WX-REGISTRO-ENTRADA
EVALUATE TRUE
WHEN FS-ENTRADA-OK
CONTINUE
WHEN FS-ENTRADA-EOF
SET FIN-ENTRADA TO TRUE
WHEN OTHER
DISPLAY 'ERROR EN READ DE ENTRADA:'FS-ENTRADA
END-EVALUATE
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA ************************************************************
30000-FINAL.
*
IF WC-LINEAS GREATER 60
ADD 1 TO LT-NUMPAG
PERFORM ESCRIBIR-CABECERAS
PERFORM ESCRIBIR-SUBTOTAL
PERFORM ESCRIBIR-TOTALES
ELSE
PERFORM ESCRIBIR-SUBTOTAL
PERFORM ESCRIBIR-TOTALES
END-IF
PERFORM 31000-CERRAR-FICHEROS
STOP RUN
.
*
************************************************************
* | ESCRIBIR TOTALES
*--|------------------+----------><----------+-------------*
* | ESCRIBIMOS LA LINEA DE TOTALES DEL LISTADO ************************************************************
ESCRIBIR-TOTALES.
*
MOVE WC-TOTALES TO LT-TOTALES
WRITE REG-LISTADO FROM TOTALES
.
*
************************************************************
* | 31000 - CERRAR FICHEROS
*--|------------------+----------><----------+-------------*
* | CERRAMOS LOS FICHEROS DEL PROGRAMA
************************************************************
31000-CERRAR-FICHEROS.
*
CLOSE ENTRADA
LISTADO
IF NOT FS-ENTRADA-OK
DISPLAY 'ERROR EN CLOSE DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-LISTADO-OK
DISPLAY 'ERROR EN CLOSE DE LISTADO:'FS-LISTADO
END-IF
.
*
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
INPUT-OUTPUT SECTION: en este ejemplo existirá porque utilizamos un fichero de entrada y uno de salida.
DATA DIVISION: existirá siempre.
FILE SECTION: en este ejemplo existirá pues utilizamos un fichero de entrada y uno de salida.
WORKING-STORAGE SECTION: exisitirá siempre.
En este caso no exisistirá la LINKAGE SECTION pues el programa no se comunica con otros programas.
PROCEDURE DIVISION: exisitirá siempre.
En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
OPEN: "Abre" los ficheros del programa. Lo acompañaremos de "INPUT" para los ficheros de entrada y "OUTPUT" para los ficheros de salida.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
MOVE/TO: movemos la información de un campo a otro.
PERFORM UNTIL: bucle
SET:Activa los niveles 88 de un campo tipo "switch".
READ: Lee cada registro del fichero de entrada. En el "INTO" le indicamos donde debe guardar la información.
EVALUATE TRUE: "Evalúa" si los niveles 88 por los que preguntamos en el "WHEN" están activados a "TRUE".
WRITE: Escribe la información indicada en el "FROM" en el fichero indicado.
STOP RUN: sentencia de finalización de ejecución.
CLOSE: "Cierra" los ficheros del programa.
Descripción del programa:
En el párrafo de inicio, inicializamos el registro de salida: WX-REGISTRO-SALIDA
Abriremos los ficheros del programa (OPEN INPUT para la entrada, y OUTPUT para la salida) y controlaremos el file-status. Si todo va bien el código del file-status valdrá '00'. Podéis ver la lista de los file-status más comunes.
Leemos el primer registro del fichero de entrada y controlaremos el file-status.
Además comprobamos que el fichero de entrada no venga vacío (en caso de que así sea, finalizamos la ejecución). Si todo ha ido OK guardaremos el nombre leído del fichero de entrada en WX-NOMBRE-ANT para controlar posteriormente el momento en que cambiemos de nombre.
Informaremos la parte genérica de la cabecera e inicializamos el contador de páginas. Escribimos la cabecera por primera vez.
INFORMAR-CABECERA:
Recoge la fecha del sistema mediante un ACCEPT. La variable "DATE" es la fecha del sistema en formato 9(6) AAMMDD.
Para informar la fecha del listado formateamos la recibida del sistema a formato DD-MM-AA.
ESCRIBIR-CABECERAS:
Escribe las lineas de cabecera CABECERA1, CABECERA2, CABECERA3, SUBCABECERA1 y SUBCABECERA2.
En el WRITE de CABECERA1 vemos que utilizamos el "AFTER ADVANCING PAGE", esto significa que esta línea se escribirá en una página nueva. Lo podemos ver en el caracter ASA que aparece a la izquierda de esta linea en el fichero de salida que será un '1'.
En el WRITE de SUBCABECERA1 vemos que utilizamos "AFTER ADVANCING 2 LINES", esto significa que se escribirá una línea en blanco y después la línea de SUBCABECERA1. El caracter ASA que aparecerá será un '0'.
Informamos el contador de líneas a '6', pues la cabecera ocupa 6 líneas.
En el párrafo de proceso, que se repetirá hasta que se termine el fichero de entrada (FIN-ENTRADA), controlaremos:
Por un lado el número de líneas escritas, y en caso de superar un máximo (64 en nusetro caso) escribiremos otra vez las cabeceras en una página nueva (ver párrado ESCRIBIR-CABECERAS).
Por otro lado la variable WX-NOMBRE, pues queremos escribir cada nombre en una página distinta. Cuando cambie el nombre escribiremos la línea de subtotales y volveremos a escribir cabeceras (que nos hará el salto de página).
Informaremos la linea de detalle y escribiremos en el fichero del listado.
Guardamos el último nombre escrito en WX-NOMBRE-ANT para controlar el cambio de nombre.
Leemos el siguiente registro del fichero de entrada.
Llamadas a párrafos:
ESCRIBIR-SUBTOTAL:
Informará el campo LT-NOMTOT con el nombre que hemos estado escribiendo, y LT-NUMNOM con el contador de registros escritos para ese nombre.
Escribirá SUBTOTALES dejando antes una línea en blanco (AFTER ADVANCING 2 LINES).
21000-INFORMAR-DETALLE:
Informamos los campos de la línea de detalle LT-NOMBRE y LT-APELLIDO con los campos de lfichero de entrada WX-NOMBRE y WX-APELLIDO.
ESCRIBIR-DETALLE:
Escribe la linea de detalle en el fichero del listado. Controlamos file-status y añadimos uno a los contadores.
En el párrado de FINAL, finalizaremos el programa escribiendo la línea de subtotales que falta, y la línea de totales generales.
Controlaremos el número de líneas que llevamos escritas, pues para escribir la línea SUBTOTALES y TOTALES necesitamos 3 líneas.
En el párrafo de ESCRIBIR-TOTALES informaremos el campo LT-TOTALES con el contador de registros escritos en total.
Al inicio del artículo veiamos como quedaría el listado una vez "impreso", ya sea por pantalla o en papel. Vamos a ver como quedaría el fichero con los caracteres ASA que indican los saltos de linea.
Fichero de salida:
----+----1----+----2----+----3----+----4----+
1LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 22-03-11 PAGINA: 1
------------------------------------------
0NOMBRE APELLIDO
--------- ---------------
ANA LOPEZ
ANA MARTINEZ
ANA PEREZ
ANA RODRIGUEZ
0TOTAL ANA : 04
1LISTADO DE EJEMPLO DEL CONSULTORIO COBOL
DIA: 22-03-11 PAGINA: 2
------------------------------------------
0NOMBRE APELLIDO
--------- ---------------
BEATRIZ GARCIA
BEATRIZ MOREDA
BEATRIZ OTERO
0TOTAL BEATRIZ : 03
TOTAL NOMBRES: 07
Donde:
1 = salto de página.
0 = deja una línea en blanco justo antes.
- = deja dos líneas en blanco.
espacio = escribe sin dejar lineas en blanco.
Y listo! Si veis que no me he parado mucho en alguna cosa y queréis que explique más en detalle, dejad un comentario y lo vemos : )
Suscribirse a:
Entradas (Atom)