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 manual. Mostrar todas las entradas
Mostrando entradas con la etiqueta manual. Mostrar todas las entradas
miércoles, 28 de enero de 2015
lunes, 10 de octubre de 2011
Ejemplo 6: programa que llama a otro programa
En este ejemplo vamos a ver un programa que llama a otro programa, denominado rutina, para recuperar información.
Se trata de un programa sin DB2 que recibirá un número de DNI por SYSIN y llamará a una rutina para calcular la letra de dicho NIF. La información recuperada la mostrará por SYSOUT.
JCL:
//PROG6 EXEC PGM=PRUEBA6
//SYSOUT DD SYSOUT=*
//SYSIN DD *
32684930
/*
donde EXEC PGM= indica el programa SIN DB2 que vamos a ejecutar
SYSOUT DD SYSOUT=* indica que la información "displayada" se quedará en la cola del SYSOUT (no lo vamos a guardar en un fichero)
en SYSIN DD * metemos la información que va a recibir el programa
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA6.
*==========================================================*
* PROGRAMA QUE LLAMA A OTRO PROGRAMA (RUTINA)
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
DATA DIVISION.
*
WORKING-STORAGE SECTION.
*
01 WX-SYSIN.
05 WX-NUMERO-NIF PIC X(8).
05 FILLER PIC X(72).
01 WX-RUTINA.
05 WX-NIF-COMPLETO.
10 WX-NUMERO-NIF PIC 9(8).
10 WX-LETRA-NIF PIC X.
05 WX-RETORNO PIC X(2).
01 RUTINA1 PIC X(7) VALUE 'RUTINA1'.
*
************************************************************
PROCEDURE DIVISION.
************************************************************
* | 00000 - 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
*
PERFORM 30000-FINAL
.
*
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
* 2| Lectura de SYSIN
************************************************************
10000-INICIO.
*
INITIALIZE WX-SYSIN
WX-RUTINA
ACCEPT WX-SYSIN FROM SYSIN
.
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el acceso a base de datos
************************************************************
20000-PROCESO.
*
MOVE WX-NUMERO-NIF OF WX-SYSIN
TO WX-NUMERO-NIF OF WX-RUTINA
CALL RUTINA1 USING WX-RUTINA
IF WX-RETORNO EQUAL 'OK'
DISPLAY 'LA LLAMADA HA IDO BIEN'
PERFORM 21000-GRABAR-SALIDA
ELSE
DISPLAY 'LA LLAMADA HA IDO MAL'
PERFORM 30000-FINAL
END-IF
.
*************************************************************
* | 21000 - GRABAR - SALIDA
*--|------------------+----------><----------+--------------*
* | ESCRIBE EN SYSOUT LA INFORMACIÓN RECUPERADA DE LA TABLA
*************************************************************
21000-GRABAR-SALIDA.
*
DISPLAY 'NIF COMPLETO:'WX-NIF-COMPLETO
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
STOP RUN
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
En este caso no existirá la INPUT-OUTPUT SECTION, pues nuestro programa no utiliza ficheros.
DATA DIVISION: existirá siempre.
En este caso no existirá la FILE SECTION, pues nuestro programa no utiliza ficheros.
WORKING-STORAGE SECTION: exisitirá siempre.
En este caso no exisistirá la LINKAGE SECTION pues el programa no es llamado desde otros programas.
PROCEDURE DIVISION: exisitirá siempre.
En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
ACCEPT: esta sentencia recoge la información del campo indicado en el "FROM". En este caso recoge la información almacenada en "SYSIN"; la que nosotros hemos introducido en el JCL.
MOVE/TO: movemos la información de un campo a otro
CALL/USING:es la sentencia que usamos para llamar a una rutina. Después del CALL indicamos el nombre de la rutina que vamos a invocar, y después del USING indicamos las variables de comunicación entre ambos programas.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
IF/ELSE: comprueba si se cumple una condición.
STOP RUN: sentencia de finalización de ejecución.
Descripción del programa:
En el párrafo de inicio, inicializamos las variables que vamos a utilizar a lo largo del programa. Luego mediante un ACCEPT recogemos la información que hemos escrito en la SYSIN de nuestro JOB.
En el párrafo de proceso, informamos el campo WX-NUMERO-NIF del área WX-RUTINA con la información recogida de SYSIN.
Como veis existen dos variables con el mismo nombre. Esto no dará problemas al compilar, mientras las variables pertenezcan a niveles superiores diferentes.
En nuestro caso tenemos un WX-NUMERO-NIF que pertenece a WX-SYSIN, y otro que pertenece a WX-RUTINA. Para utilizar estas variables a lo largo del programa tendremos que indicar a cual de ellas nos referimos, por eso les hemos añadido el "OF WX-XXXXX".
Una vez informada el área de comunicación entre dos programas, procedemos a hacer la llamada en sí con la sentencia CALL/USING.
Se trata de una llamada dinámica, pues el nombre de la rutina está contenido dentro de una variable, así que después de la llamada la rutina será descargada de la memoria.
En las llamadas estáticas el nombre de la rutina se indica entre comillas simpes 'RUTINA1'. En este caso, después de la llamada el módulo queda residente en memoria, porque se integra en el programa objeto.
Para que no haya errores comprobamos que la llamada ha ido bien validando el retorno (informado dentro de la rutina).
Si todo ha ido bien grabamos la información recuperada (NIF con letra) en la SYSOUT mediante un DISPLAY.
RUTINA
IDENTIFICATION DIVISION.
PROGRAM-ID. RUTINA1.
*==========================================================*
* RUTINA QUE CALCULA LA LETRA DE UN NIF
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
DATA DIVISION.
*
WORKING-STORAGE SECTION.
*
01 WI-INDICES.
05 WI-IND PIC 9(2).
*
01 WX-VARIABLES.
05 WX-NIF-DIVID PIC 9(8).
05 WX-NIF-MULTI PIC 9(8).
*
01 WT-TABLAS.
05 WT-NIF-TABLA PIC X(24)
VALUE "TRWAGMYFPDXBNJZSQVHLCKET".
05 WT-NIF-TABLA-R REDEFINES WT-NIF-TABLA.
10 WT-LETRA-TABLA OCCURS 24 PIC X.
*
LINKAGE SECTION.
*
01 WX-RUTINA.
05 WX-NIF-COMPLETO.
10 WX-NUMERO-NIF PIC 9(8).
10 WX-LETRA-NIF PIC X.
05 WX-RETORNO PIC X(2).
*
************************************************************
PROCEDURE DIVISION USING WX-RUTINA.
************************************************************
* | 00000 - 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
*
PERFORM 30000-FINAL
.
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
************************************************************
10000-INICIO.
*
INITIALIZE WX-VARIABLES
WI-INDICES
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el cálculo de la letra del NIF
************************************************************
20000-PROCESO.
*
COMPUTE WX-NIF-DIVID = WX-NUMERO-NIF / 23
COMPUTE WX-NIF-MULTI = WX-NIF-DIVID * 23
COMPUTE WI-IND = WX-NUMERO-NIF - WX-NIF-MULTI
ADD 1 TO WI-IND
MOVE WT-LETRA-TABLA(WI-IND) TO WX-LETRA-NIF
MOVE 'OK' TO WX-RETORNO
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
GOBACK
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
En este caso no existirá la INPUT-OUTPUT SECTION, pues nuestro programa no utiliza ficheros.
DATA DIVISION: existirá siempre.
En este caso no existirá la FILE SECTION, pues nuestro programa no utiliza ficheros.
WORKING-STORAGE SECTION: exisitirá siempre.
LINKAGE SECTION: en este caso sí existirá puesto que se trata de una rutina que es llamada por un programa principal.
PROCEDURE DIVISION: exisitirá siempre.
En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
COMPUTE: realiza cálculos numéricos
ADD: operador de adición (suma)
MOVE/TO: movemos la información de un campo a otro
GOBACK: sentencia de finalización de ejecución para rutinas. Devuelve el control al programa llamante.
Descripción del programa:
En la LINKAGE SECTION definimos el área de comunicación con el programa llamante (PRUEBA6), en este caso WX-RUTINA.
En el párrafo de inicio inicializamos las variables que vamos a utilizar a lo largo del programa.
En el párrafo de proceso hacemos los cálculos necesarios para saber qué letra se corresponde al número de NIF que hemos introducido e informamos con un 'OK' el código de retorno.
En caso de que se produzca un error antes de terminar el proceso, el código de retorno irá vacío, y podremos controlar esta diferencia en el programa llamante.
Una vez calculada la letra del NIF devolvemos el control al programa PRUEBA6 haciendo GOBACK.
RESULTADO:
NIF-COMPLETO: 32684930K
Diferencias entre ambos programas:
LINKAGE SECTION: sólo la rutina (programa que es llamado por otro) tiene variables definidas en esta sección.
PROCEDURE DIVISION: sólo la rutina lleva asociada el área de comunicación entre programas en la procedure, añadiéndole la sentencia USING.
30000-FINAL: el programa principal lleva un STOP RUN de finalización de ejecución, mientras que la rutina lleva un GOBACK para devolver el control al programa llamante (programa que ha hecho el CALL).
Si tenéis cualquier duda sobre el uso de rutinas, ya sabéis, preguntad lo que queráis!
Se trata de un programa sin DB2 que recibirá un número de DNI por SYSIN y llamará a una rutina para calcular la letra de dicho NIF. La información recuperada la mostrará por SYSOUT.
JCL:
//PROG6 EXEC PGM=PRUEBA6
//SYSOUT DD SYSOUT=*
//SYSIN DD *
32684930
/*
donde EXEC PGM= indica el programa SIN DB2 que vamos a ejecutar
SYSOUT DD SYSOUT=* indica que la información "displayada" se quedará en la cola del SYSOUT (no lo vamos a guardar en un fichero)
en SYSIN DD * metemos la información que va a recibir el programa
PROGRAMA:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRUEBA6.
*==========================================================*
* PROGRAMA QUE LLAMA A OTRO PROGRAMA (RUTINA)
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
DATA DIVISION.
*
WORKING-STORAGE SECTION.
*
01 WX-SYSIN.
05 WX-NUMERO-NIF PIC X(8).
05 FILLER PIC X(72).
01 WX-RUTINA.
05 WX-NIF-COMPLETO.
10 WX-NUMERO-NIF PIC 9(8).
10 WX-LETRA-NIF PIC X.
05 WX-RETORNO PIC X(2).
01 RUTINA1 PIC X(7) VALUE 'RUTINA1'.
*
************************************************************
PROCEDURE DIVISION.
************************************************************
* | 00000 - 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
*
PERFORM 30000-FINAL
.
*
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
* 2| Lectura de SYSIN
************************************************************
10000-INICIO.
*
INITIALIZE WX-SYSIN
WX-RUTINA
ACCEPT WX-SYSIN FROM SYSIN
.
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el acceso a base de datos
************************************************************
20000-PROCESO.
*
MOVE WX-NUMERO-NIF OF WX-SYSIN
TO WX-NUMERO-NIF OF WX-RUTINA
CALL RUTINA1 USING WX-RUTINA
IF WX-RETORNO EQUAL 'OK'
DISPLAY 'LA LLAMADA HA IDO BIEN'
PERFORM 21000-GRABAR-SALIDA
ELSE
DISPLAY 'LA LLAMADA HA IDO MAL'
PERFORM 30000-FINAL
END-IF
.
*************************************************************
* | 21000 - GRABAR - SALIDA
*--|------------------+----------><----------+--------------*
* | ESCRIBE EN SYSOUT LA INFORMACIÓN RECUPERADA DE LA TABLA
*************************************************************
21000-GRABAR-SALIDA.
*
DISPLAY 'NIF COMPLETO:'WX-NIF-COMPLETO
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
STOP RUN
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
En este caso no existirá la INPUT-OUTPUT SECTION, pues nuestro programa no utiliza ficheros.
DATA DIVISION: existirá siempre.
En este caso no existirá la FILE SECTION, pues nuestro programa no utiliza ficheros.
WORKING-STORAGE SECTION: exisitirá siempre.
En este caso no exisistirá la LINKAGE SECTION pues el programa no es llamado desde otros programas.
PROCEDURE DIVISION: exisitirá siempre.
En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
ACCEPT: esta sentencia recoge la información del campo indicado en el "FROM". En este caso recoge la información almacenada en "SYSIN"; la que nosotros hemos introducido en el JCL.
MOVE/TO: movemos la información de un campo a otro
CALL/USING:es la sentencia que usamos para llamar a una rutina. Después del CALL indicamos el nombre de la rutina que vamos a invocar, y después del USING indicamos las variables de comunicación entre ambos programas.
DISPLAY: escribe el contenido del campo indicado en la SYSOUT del JCL.
IF/ELSE: comprueba si se cumple una condición.
STOP RUN: sentencia de finalización de ejecución.
Descripción del programa:
En el párrafo de inicio, inicializamos las variables que vamos a utilizar a lo largo del programa. Luego mediante un ACCEPT recogemos la información que hemos escrito en la SYSIN de nuestro JOB.
En el párrafo de proceso, informamos el campo WX-NUMERO-NIF del área WX-RUTINA con la información recogida de SYSIN.
Como veis existen dos variables con el mismo nombre. Esto no dará problemas al compilar, mientras las variables pertenezcan a niveles superiores diferentes.
En nuestro caso tenemos un WX-NUMERO-NIF que pertenece a WX-SYSIN, y otro que pertenece a WX-RUTINA. Para utilizar estas variables a lo largo del programa tendremos que indicar a cual de ellas nos referimos, por eso les hemos añadido el "OF WX-XXXXX".
Una vez informada el área de comunicación entre dos programas, procedemos a hacer la llamada en sí con la sentencia CALL/USING.
Se trata de una llamada dinámica, pues el nombre de la rutina está contenido dentro de una variable, así que después de la llamada la rutina será descargada de la memoria.
En las llamadas estáticas el nombre de la rutina se indica entre comillas simpes 'RUTINA1'. En este caso, después de la llamada el módulo queda residente en memoria, porque se integra en el programa objeto.
Para que no haya errores comprobamos que la llamada ha ido bien validando el retorno (informado dentro de la rutina).
Si todo ha ido bien grabamos la información recuperada (NIF con letra) en la SYSOUT mediante un DISPLAY.
RUTINA
IDENTIFICATION DIVISION.
PROGRAM-ID. RUTINA1.
*==========================================================*
* RUTINA QUE CALCULA LA LETRA DE UN NIF
*==========================================================*
*
ENVIRONMENT DIVISION.
*
CONFIGURATION SECTION.
*
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
*
DATA DIVISION.
*
WORKING-STORAGE SECTION.
*
01 WI-INDICES.
05 WI-IND PIC 9(2).
*
01 WX-VARIABLES.
05 WX-NIF-DIVID PIC 9(8).
05 WX-NIF-MULTI PIC 9(8).
*
01 WT-TABLAS.
05 WT-NIF-TABLA PIC X(24)
VALUE "TRWAGMYFPDXBNJZSQVHLCKET".
05 WT-NIF-TABLA-R REDEFINES WT-NIF-TABLA.
10 WT-LETRA-TABLA OCCURS 24 PIC X.
*
LINKAGE SECTION.
*
01 WX-RUTINA.
05 WX-NIF-COMPLETO.
10 WX-NUMERO-NIF PIC 9(8).
10 WX-LETRA-NIF PIC X.
05 WX-RETORNO PIC X(2).
*
************************************************************
PROCEDURE DIVISION USING WX-RUTINA.
************************************************************
* | 00000 - 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
*
PERFORM 30000-FINAL
.
************************************************************
* | 10000 - INICIO
*--|------------+----------><----------+-------------------*
* | SE REALIZA EL TRATAMIENTO DE INICIO:
* 1| Inicialización de Áreas de Trabajo
************************************************************
10000-INICIO.
*
INITIALIZE WX-VARIABLES
WI-INDICES
.
*
************************************************************
* | 20000 - PROCESO
*--|------------------+----------><------------------------*
* | SE REALIZA EL TRATAMIENTO DE LOS DATOS:
* 1| Realiza el cálculo de la letra del NIF
************************************************************
20000-PROCESO.
*
COMPUTE WX-NIF-DIVID = WX-NUMERO-NIF / 23
COMPUTE WX-NIF-MULTI = WX-NIF-DIVID * 23
COMPUTE WI-IND = WX-NUMERO-NIF - WX-NIF-MULTI
ADD 1 TO WI-IND
MOVE WT-LETRA-TABLA(WI-IND) TO WX-LETRA-NIF
MOVE 'OK' TO WX-RETORNO
.
*
************************************************************
* | 30000 - FINAL
*--|------------------+----------><----------+-------------*
* | FINALIZA LA EJECUCION DEL PROGRAMA
************************************************************
30000-FINAL.
*
GOBACK
.
En el programa podemos ver las siguientes divisiones/secciones:
IDENTIFICATION DIVISION: existirá siempre.
ENVIRONMENT DIVISION: existirá siempre.
CONFIGURATION SECTION: existirá siempre.
En este caso no existirá la INPUT-OUTPUT SECTION, pues nuestro programa no utiliza ficheros.
DATA DIVISION: existirá siempre.
En este caso no existirá la FILE SECTION, pues nuestro programa no utiliza ficheros.
WORKING-STORAGE SECTION: exisitirá siempre.
LINKAGE SECTION: en este caso sí existirá puesto que se trata de una rutina que es llamada por un programa principal.
PROCEDURE DIVISION: exisitirá siempre.
En el programa podemos ver las siguientes sentencias:
PERFORM: llamada a párrafo
INITIALIZE: para inicializar variable
COMPUTE: realiza cálculos numéricos
ADD: operador de adición (suma)
MOVE/TO: movemos la información de un campo a otro
GOBACK: sentencia de finalización de ejecución para rutinas. Devuelve el control al programa llamante.
Descripción del programa:
En la LINKAGE SECTION definimos el área de comunicación con el programa llamante (PRUEBA6), en este caso WX-RUTINA.
En el párrafo de inicio inicializamos las variables que vamos a utilizar a lo largo del programa.
En el párrafo de proceso hacemos los cálculos necesarios para saber qué letra se corresponde al número de NIF que hemos introducido e informamos con un 'OK' el código de retorno.
En caso de que se produzca un error antes de terminar el proceso, el código de retorno irá vacío, y podremos controlar esta diferencia en el programa llamante.
Una vez calculada la letra del NIF devolvemos el control al programa PRUEBA6 haciendo GOBACK.
RESULTADO:
NIF-COMPLETO: 32684930K
Diferencias entre ambos programas:
LINKAGE SECTION: sólo la rutina (programa que es llamado por otro) tiene variables definidas en esta sección.
PROCEDURE DIVISION: sólo la rutina lleva asociada el área de comunicación entre programas en la procedure, añadiéndole la sentencia USING.
30000-FINAL: el programa principal lleva un STOP RUN de finalización de ejecución, mientras que la rutina lleva un GOBACK para devolver el control al programa llamante (programa que ha hecho el CALL).
Si tenéis cualquier duda sobre el uso de rutinas, ya sabéis, preguntad lo que queráis!
martes, 27 de septiembre de 2011
Publicado "PGM=SORT: utilidades."
Buenos días a todos.
Como se indica en el título, ya está publicado el segundo libro del Consultorio Cobol, "PGM=SORT: utilidades", donde se recogen todos los artículos relacionados con el uso del programa SORT en JCL.
Está a la venta en la web de Lulu.com con el título "PGM=SORT: utilidades", y podéis adquirirlo por tan sólo 1,50€.
Una vez más se trata de un formato pdf para facilitar la búsqueda dentro de un mismo tema como es el SORT.
Esperamos que os guste y, sobre todo, que os sea útil.
Como se indica en el título, ya está publicado el segundo libro del Consultorio Cobol, "PGM=SORT: utilidades", donde se recogen todos los artículos relacionados con el uso del programa SORT en JCL.
Está a la venta en la web de Lulu.com con el título "PGM=SORT: utilidades", y podéis adquirirlo por tan sólo 1,50€.
Una vez más se trata de un formato pdf para facilitar la búsqueda dentro de un mismo tema como es el SORT.
Esperamos que os guste y, sobre todo, que os sea útil.
miércoles, 30 de marzo de 2011
Esquema de un programa pl/i
Un programa pl/i no tiene una estructura "fija", es decir, el programa compilará aunque no sigas un esquema determinado. Pero para facilitar la comprensión del código, siempre es mejor seguir algunas pautas : )
INICIO DEL PROGRAMA
Todo programa principal empezará con la sentencia:
PRUEBA1: PROCEDURE OPTIONS(MAIN);
Donde:
PRUEBA1 es el identificador del programa. No es necesario que coincida con el nombre externo del programa. Tendrá una longitud máxima de 7 caracteres.
PROCEDURE o PROC es la palabra clave que le indica al compilador que el programa es un procedimiento.
OPTIONS(MAIN): son las palabras clave que indican al compilador que se trata de un programa principal.
Para rutinas y programas online ya lo iremos viendo.
Los comentarios empezarán con /* y terminarán con */ y podrán codificarse en cualquier parte del código. No hay que colocar el inicio en ninguna columna en concreto.
PROCESO DEL PROGRAMA
En el proceso del programa incluiremos tanto la declaración de variables, como llamadas a otros procedimientos, como la funcionalidad del programa en sí.
Para las declaraciones utilizaremos la sentencia DECLARE o DCL para abreviar.
Declaración de funciones del sistema:
DCL (DATE,TIME) BUILTIN;
Donde DATE y TIME serían la fecha y hora del sistema respectivamente.
Declaración de ficheros:
DCL SYSPRINT FILE PRINT;
DCL SYSIN FILE STREAM INPUT;
DCL SALIDA FILE STREAM OUTPUT;
DCL SALIDA FILE RECORD OUTPUT;
DCL REG_SALIDA CHAR(100);
Donde:
FILE indica que se trata de un fichero.
STREAM indica que los datos se guardarán como una cadena de caracteres. El acceso siempre será secuencial.
INPUT/OUTPUT indica que es un fichero de entrada/salida respectivamente.
RECORD indica que los datos se guardarán en un formato especifico (en nuestro caso REG_SALIDA). Existen diferentes tipos de acceso que iremos viendo en futuros ejemplos.
Declaración de variables:
Para entenderlo mejor, veamos un cuadro con la equivalencia entre declaraciones COBOL y PL/I:
Donde:
CHAR indica campo alfanumérico.
INIT indica el valor que toma la variable (equivale a nuestro VALUE).
PIC (N)9 indica campo numérico.
DEC FIXED indica campo comprimido (equivale a COMP-3). En los campos con decimales hay que tener en cuenta que si en cobol indicábamos 9(N)V9(M), en pl/i se indica (N+M,M).
BIN FIXED indica campo binario (hexadecimal).
BIT indica variable booleana que tomará los valores de verdadera '1'B (SET campo a TRUE) y falsa '0'B (SET campo a FALSE).
Para definir tablas se indicará el número de filas entre paréntesis; como el ejemplo TABLA(3). Si fuese una tabla de dos dimensiones escribiríamos primero el número de filas y después el número de comumnas:
DCL TABLA_DOBLE(2,3) CHAR(3);
En PL/I no estamos obligados a declarar las variables, aunque es recomendable. Veamos que pasa cuando no declaramos variables, o no las declaramos "del todo":
Si sólo declaramos la base:
DEC = DEC FLOAT (6)
BIN = BIN FLOAT (21)
Si sólo declaramos la precisión:
FIXED = DEC FIXED (5,0)
FLOAT = DEC FLOAT (6)
Si declaramos base + precisión:
BIN FIXED = BIN FIXED (15,0)
DEC FIXED = DEC FIXED (5,0)
BIN FLOAT = BIN FLOAT (21)
DEC FLOAT = DEC FLOAT (6)
Sin declarar nada de nada:
COMIENZA POR: I-N = BIN FIXED (15,0)
COMIENZA POR: A-H O-Z Y CARACT. ESPECIALES = DEC FLOAT(6)
CALCULO DE LONGITUDES
Numéricos y alfanuméricos
Lo que venga indicado en el PIC (N)9 o en el CHAR(N).
DCL ALFANUM CHAR(5);
DCL NUMERICO PIC (5)9;
longitud = 5
Comprimidos
Si tenemos un campo DEC FIXED (N,M), la longitud será N+1/2. Si el resultado tiene decimales redondeamos hacia arriba. El máximo permitido es N = 15.
DCL COMPRIMIDO DEC FIXED (7,2);
longitud = 7+1/2 = 4
Binarios
Si tenemos un campo BIN FIXED (N,M), el número de bytes que se reservan en memoria para cada campo es:
2 posiciones para N entre 1 y 15.
4 posiciones para N ente 16 y 31(máximo permitido).
FINAL DEL PROGRAMA
Para finalizar el programa escribiremos la sentencia:
END PRUEBA1;
Donde PRUEBA1 es el nombre del proceso principal que habíamos definido al inicio del programa.
La parte de informar variables, condiciones, bucles, etc. lo dejaremos para el siguiente artículo : )
INICIO DEL PROGRAMA
Todo programa principal empezará con la sentencia:
PRUEBA1: PROCEDURE OPTIONS(MAIN);
Donde:
PRUEBA1 es el identificador del programa. No es necesario que coincida con el nombre externo del programa. Tendrá una longitud máxima de 7 caracteres.
PROCEDURE o PROC es la palabra clave que le indica al compilador que el programa es un procedimiento.
OPTIONS(MAIN): son las palabras clave que indican al compilador que se trata de un programa principal.
Para rutinas y programas online ya lo iremos viendo.
Los comentarios empezarán con /* y terminarán con */ y podrán codificarse en cualquier parte del código. No hay que colocar el inicio en ninguna columna en concreto.
PROCESO DEL PROGRAMA
En el proceso del programa incluiremos tanto la declaración de variables, como llamadas a otros procedimientos, como la funcionalidad del programa en sí.
Para las declaraciones utilizaremos la sentencia DECLARE o DCL para abreviar.
Declaración de funciones del sistema:
DCL (DATE,TIME) BUILTIN;
Donde DATE y TIME serían la fecha y hora del sistema respectivamente.
Declaración de ficheros:
DCL SYSPRINT FILE PRINT;
DCL SYSIN FILE STREAM INPUT;
DCL SALIDA FILE STREAM OUTPUT;
DCL SALIDA FILE RECORD OUTPUT;
DCL REG_SALIDA CHAR(100);
Donde:
FILE indica que se trata de un fichero.
STREAM indica que los datos se guardarán como una cadena de caracteres. El acceso siempre será secuencial.
INPUT/OUTPUT indica que es un fichero de entrada/salida respectivamente.
RECORD indica que los datos se guardarán en un formato especifico (en nuestro caso REG_SALIDA). Existen diferentes tipos de acceso que iremos viendo en futuros ejemplos.
Declaración de variables:
Para entenderlo mejor, veamos un cuadro con la equivalencia entre declaraciones COBOL y PL/I:
| 01 ALFAN PIC X(N) VALUE SPACES. | DCL ALFAN CHAR (N) INIT ' '; |
| 01 NUMER PIC 9(N) VALUE ZEROS. | DCL NUMER PIC '(N)9' INIT (0); |
| 01 COMPR PIC S9(N)V9(M) COMP-3. | DCL COMPR DEC FIXED (N+M,M); |
| 01 HEXA1 PIC S9(4) COMP. | DCL HEXA1 BIN FIXED (15) INIT(0); |
| 01 HEXA2 PIC S9(9) COMP. | DCL HEXA2 BIN FIXED (31) INIT (0); |
| 01 ESTRUCTURA. | DCL 1 ESTRUCTURA, |
| 05 ALFANUM PIC X. | 5 ALFANUM CHAR, |
| 05 NUMERICO PIC 9(2). | 5 NUMERICO PIC '(2)9', |
| 05 COM PIC 9(7)V9(2) COMP-3. | 5 COM DEC FIXED (9,2), |
| 05 BINHEXA PIC S9(4) COMP. | 5 BINHEXA BIN FIXED (15), |
| 05 BINHEXA2 PIC S9(9) COMP. | 5 BINHEXA2 BIN FIXED (31); |
| 01 EDITADO PIC ZZZ9,99. | DCL EDITADO PIC 'ZZZ9V,99'; |
| 01 SWITCH PIC X VALUE 'S'. | DCL BOOLEAN BIT; |
| 88 VERDAD VALUE 'S'. | DCL VERDAD BIT INIT('1'B); |
| 88 FALSO VALUE 'N'. | DCL FALSO BIT INIT('0'B); |
| 01 TABLA PIC X OCCURS 3 TIMES. | DCL TABLA(3) CHAR(1); |
Donde:
CHAR indica campo alfanumérico.
INIT indica el valor que toma la variable (equivale a nuestro VALUE).
PIC (N)9 indica campo numérico.
DEC FIXED indica campo comprimido (equivale a COMP-3). En los campos con decimales hay que tener en cuenta que si en cobol indicábamos 9(N)V9(M), en pl/i se indica (N+M,M).
BIN FIXED indica campo binario (hexadecimal).
BIT indica variable booleana que tomará los valores de verdadera '1'B (SET campo a TRUE) y falsa '0'B (SET campo a FALSE).
Para definir tablas se indicará el número de filas entre paréntesis; como el ejemplo TABLA(3). Si fuese una tabla de dos dimensiones escribiríamos primero el número de filas y después el número de comumnas:
DCL TABLA_DOBLE(2,3) CHAR(3);
En PL/I no estamos obligados a declarar las variables, aunque es recomendable. Veamos que pasa cuando no declaramos variables, o no las declaramos "del todo":
Si sólo declaramos la base:
DEC = DEC FLOAT (6)
BIN = BIN FLOAT (21)
Si sólo declaramos la precisión:
FIXED = DEC FIXED (5,0)
FLOAT = DEC FLOAT (6)
Si declaramos base + precisión:
BIN FIXED = BIN FIXED (15,0)
DEC FIXED = DEC FIXED (5,0)
BIN FLOAT = BIN FLOAT (21)
DEC FLOAT = DEC FLOAT (6)
Sin declarar nada de nada:
COMIENZA POR: I-N = BIN FIXED (15,0)
COMIENZA POR: A-H O-Z Y CARACT. ESPECIALES = DEC FLOAT(6)
CALCULO DE LONGITUDES
Numéricos y alfanuméricos
Lo que venga indicado en el PIC (N)9 o en el CHAR(N).
DCL ALFANUM CHAR(5);
DCL NUMERICO PIC (5)9;
longitud = 5
Comprimidos
Si tenemos un campo DEC FIXED (N,M), la longitud será N+1/2. Si el resultado tiene decimales redondeamos hacia arriba. El máximo permitido es N = 15.
DCL COMPRIMIDO DEC FIXED (7,2);
longitud = 7+1/2 = 4
Binarios
Si tenemos un campo BIN FIXED (N,M), el número de bytes que se reservan en memoria para cada campo es:
2 posiciones para N entre 1 y 15.
4 posiciones para N ente 16 y 31(máximo permitido).
FINAL DEL PROGRAMA
Para finalizar el programa escribiremos la sentencia:
END PRUEBA1;
Donde PRUEBA1 es el nombre del proceso principal que habíamos definido al inicio del programa.
La parte de informar variables, condiciones, bucles, etc. lo dejaremos para el siguiente artículo : )
¿Qué es PL/I?
PL/I (Programming Language 1) es un lenguaje de programación de IBM (al igual que COBOL) que os podréis encontrar en muchos entornos HOST. Incluso si trabajáis en COBOL, es probable que alguna aplicación tenga código en PL/I y seguro que tenéis acceso al compilador.
Veamos las principales diferencias con COBOL:
Estas son a grandes rasgos las principales diferencias entre los dos lenguajes. En próximos artículos iremos viendo como codificar un programa PL/I, su estructura, como informar variables, como hacer bucles, etc.
Veamos las principales diferencias con COBOL:
- No existe área A y área B. Existe un único área desde la columna 2 a la 72.
- No hay divisiones ni secciones.
- Acepta caracteres alfabéticos de la A a la Z (solo en mayúsuculas). Acepta los caracteres '@', '$', '#' ó 'Ñ' (según la instalación). Los números del 0 al 9. No acepta el guión '-' pero sí el guión bajo '_'.
- Operadores lógicos: negación (¬), igualdad (=), conjunción (&), disyunción (|), mayor que (>), menor que (<).
- No es necesario declarar las variables (aunque recomendable).
- En pl/i no existen las palabras reservadas, sólo palabras CLAVE pero que pueden utilizarse como variables.
- El final de una sentencia se termina con punto y coma (;) en lugar de con un punto (.), y además debe finalizarse cada sentencia, no vale con poner un ; al final de un procedimiento (como en COBOL que poníamos un . al final de párrafo).
Estas son a grandes rasgos las principales diferencias entre los dos lenguajes. En próximos artículos iremos viendo como codificar un programa PL/I, su estructura, como informar variables, como hacer bucles, etc.
Principiantes PL/I
Ahora que ya te habías hecho un experto en COBOL gracias al manual del Consultorio, resulta que a tu empresa se le ocurre cambiarte a un proyecto en PL/I.
¿¿¿Y ahora qué hago???
Que no cunda el pánico!! En el Consultorio Cobol estamos preparados para todo tipo de contratiempos. Con todos vosotros, el nuevo "manual de PL/I para principiantes"!
Manual de PL/I para principiantes
1. ¿Qué es PL/I?
2. Esquema de un programa PL/I.
3. Proceso de un programa PL/I.
4. Ficheros en PL/I.
5. Ejemplo 1: leer de SYSIN y escrbir en SYSPRINT (pl/i).
Continuará...
¿¿¿Y ahora qué hago???
Que no cunda el pánico!! En el Consultorio Cobol estamos preparados para todo tipo de contratiempos. Con todos vosotros, el nuevo "manual de PL/I para principiantes"!
Manual de PL/I para principiantes
1. ¿Qué es PL/I?
- Descripción del lenguaje de programación PL/I.
- Diferencias principales con COBOL.
2. Esquema de un programa PL/I.
- Esquema básico de un programa PL/I.
- Declaración de variables en PL/I.
3. Proceso de un programa PL/I.
- Informando variables: asignación de valores.
- Inicializando variables: sentencia INIT.
- Condiciones: IF/ELSE.
- Bucles: DO WHILE, DO UNTIL, DO/TO.
4. Ficheros en PL/I.
- Ficheros RECORD.
- Ficheros STREAM.
5. Ejemplo 1: leer de SYSIN y escrbir en SYSPRINT (pl/i).
- JCL que ejecuta un programa sin DB2.
- Programa que realiza un GET de la SYSIN y un PUT de la información recibida para mostrarla por SYSPRINT.
Continuará...
lunes, 28 de febrero de 2011
Ejemplo 3: leer de fichero y escribir en fichero.
Para este ejemplo crearemos un programa que lea un fichero de entrada, formatee la información y escriba en un fichero de salida.
JCL:
//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.DE.SALIDA
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//PROG3 EXEC PGM=PRUEBA3
//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))
/*
En este caso volvemos a utilizar el IDCAMS para borrar el fichero de salida que se genera en el segundo paso. Sigue siendo 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 compartida al indicar DISP=SHR.
En la SYSOUT veremos los mensajes de error en caso de que los haya.
Fichero de entrada:
----+----1----+
11111AA100A
22222BB100B
33333CC100K
44444DD100M
campo1: número de cliente
campo2: código de empresa
campo3: saldo
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 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 11 CHARACTERS.
01 REG-ENTRADA PIC X(11).
*
* Fichero de salida de longitud fija (F) igual a 28.
FD SALIDA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 28 CHARACTERS.
01 REG-SALIDA PIC X(28).
*
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 WB-SIGNOS PIC X.
88 SIGNO-MAS VALUE 'A', 'B',
'C', 'D',
'E', 'F',
'G', 'H',
'I', '{'.
88 SIGNO-MENOS VALUE 'J', 'K',
'L', 'M',
'N', 'O',
'P', 'Q',
'R', '}'.
01 WX-TABLA-EMPRESAS.
05 PIC X(11) VALUE 'AAEMPRESA 1'.
05 PIC X(11) VALUE 'BBEMPRESA 2'.
05 PIC X(11) VALUE 'CCEMPRESA 3'.
05 PIC X(11) VALUE 'DDEMPRESA 4'.
01 REDEFINES WX-TABLA-EMPRESAS.
05 WX-ELEMENTOS OCCURS 4 TIMES
INDEXED BY WI-ELEM.
10 WX-CODIGO-EMPRESA PIC X(2).
10 WX-NOMBRE-EMPRESA PIC X(9).
*
01 WX-REGISTRO-ENTRADA.
05 WX-ENT-CLIENTE PIC 9(5).
05 WX-ENT-COD-EMPRESA PIC X(2).
05 WX-ENT-SALDO PIC S9(4).
*
01 WX-REGISTRO-SALIDA.
05 WX-SAL-CLIENTE PIC 9(5).
05 WX-SAL-COD-EMPRESA PIC X(2).
05 WX-SAL-NOMBRE-EMPRESA PIC X(9).
05 WX-SAL-SALDO PIC 9(4).
05 WX-SAL-SIGNO PIC X(8).
* CONSTANTES
01 WK-POSITIVO PIC X(8) VALUE 'positivo'.
01 WK-NEGATIVO PIC X(8) VALUE 'negativo'.
*
************************************************************
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
.
*
************************************************************
* 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 DEL FICHERO DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN OPEN DEL FICHERO 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.
*
PERFORM 21000-BUSCAR-NOMBRE-EMPRESA
PERFORM 22000-BUSCAR-SIGNO-SALDO
PERFORM 23000-INFORMAR-SALIDA
PERFORM ESCRIBIR-SALIDA
PERFORM LEER-ENTRADA
.
*
************************************************************
* 21000 - BUSCAR NOMBRE EMPRESA
*--|------------------+----------><----------+-------------*
* BUSCAMOS EL CODIGO DE EMPRESA DEL FICHERO DE ENTRADA EN
* NUESTRA TABLA INTERNA PARA RECUPERAR EL NOMBRE
************************************************************
21000-BUSCAR-NOMBRE-EMPRESA.
*
*Ponemos el índice WI-ELEM a 1 y se irá incrementando de 1 en 1
SET WI-ELEM TO 1
*Buscamos en WX-TABLA-EMPRESAS el nombre de empresa que tenga
*el mismo código que el del fichero de entrada.
*Si no lo encuentra, movemos espacios al nombre de la empresa
SEARCH WX-ELEMENTOS
AT END
MOVE SPACES TO WX-SAL-NOMBRE-EMPRESA
WHEN WX-CODIGO-EMPRESA(WI-ELEM) EQUAL WX-ENT-COD-EMPRESA
MOVE WX-NOMBRE-EMPRESA(WI-ELEM)
TO WX-SAL-NOMBRE-EMPRESA
END-SEARCH
.
*
************************************************************
* 22000-BUSCAR-SIGNO-SALDO
*--|------------------+----------><----------+-------------*
* COMPROBAMOS EL SIGNO DEL SALDO E INFORMAMOS EL CAMPO:
* WX-SAL-SIGNO
************************************************************
22000-BUSCAR-SIGNO-SALDO.
*
*El signo viene dado por la última posición. La movemos al
*switch WB-SIGNOS. Según su valor informará positivo o negativo
MOVE WX-ENT-SALDO(4:1) TO WB-SIGNOS
EVALUATE TRUE
WHEN SIGNO-MAS
MOVE WK-POSITIVO TO WX-SAL-SIGNO
WHEN SIGNO-MENOS
MOVE WK-NEGATIVO TO WX-SAL-SIGNO
WHEN OTHER
MOVE SPACES TO WX-SAL-SIGNO
END-EVALUATE
.
*
************************************************************
* 23000-INFORMAR-SALIDA
*--|------------------+----------><----------+-------------*
* INFORMAMOS EL RESTO DE CAMPOS DEL FICHERO DE SALIDA
************************************************************
23000-INFORMAR-SALIDA.
*
MOVE WX-ENT-CLIENTE TO WX-SAL-CLIENTE
MOVE WX-ENT-COD-EMPRESA TO WX-SAL-COD-EMPRESA
MOVE WX-ENT-SALDO TO WX-SAL-SALDO
.
*
************************************************************
* 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 FROM WX-REGISTRO-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.
*
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
.
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.
SEARCH: esta sententencia se utiliza para buscar un dato dentro de una tabla interna, recorriéndola usándo un índice y comparando alguno de sus campos con el campo que buscamos.
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.
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 enrtada, 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.
Además comprobamos que el fichero de entrada no venga vacío (en caso de que así sea, finalizamos la ejecución).
En el párrafo de proceso, que se repetirá hasta que se termine el fichero de entrada (FIN-ENTRADA), tenemos varias llamadas a párrafos:
21000-BUSCAR-NOMBRE-EMPRESA:
Busca en la taba interna WX-TABLA-EMPRESAS utilizando la sentencia SEARCH.
22000-BUSCAR-SIGNO-SALDO:
La última posición del campo saldo (S9(4)) nos indica el signo. Podéis ver la referencia a los signos en campos numéricos.
Guardaremos esa última posición en el nivel superior de un campo tipo switch (WB-SIGNOS). Si el valor se corresponde con alguno de los indicados en los niveles 88, ese nivel se activará a "TRUE".
En el "EVALUATE TRUE", el programa entrará por la sentencia "WHEN" que esté activa (que sea "TRUE").
23000-INFORMAR-SALIDA:
Informamos el resto de campos.
ESCRIBIR-SALIDA:
Escribimos nuestro registro ya informado en el fichero de salida.
LEER-ENTRADA:
Leemos el siguiente registro del fichero de entrada.
Fichero de salida:
----+----1----+----2----+----3
11111AAEMPRESA 11001positivo
22222BBEMPRESA 21002positivo
33333CCEMPRESA 31002negativo
44444DDEMPRESA 41004negativo
campo1: número de cliente
campo2: código de empresa
campo3: nombre de empresa
campo4: saldo
campo5: signo del saldo
En este programa además de ver como crear un PROCESO que trate todos los registros de un fichero de entrada, hemos visto varias sentencias como el EVALUATE y el SEARCH. Así aprovechamos para ir introduciendo más sentencias útiles del COBOL en forma de ejemplos.
Y si os queda cualquier duda, estamos aquí para resolverlas : )
JCL:
//******************************************************
//******************** BORRADO *************************
//BORRADO EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEL FICHERO.DE.SALIDA
SET MAXCC = 0
//******************************************************
//*********** EJECUCION DEL PROGRAMA PRUEBA3 ***********
//PROG3 EXEC PGM=PRUEBA3
//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))
/*
En este caso volvemos a utilizar el IDCAMS para borrar el fichero de salida que se genera en el segundo paso. Sigue siendo 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 compartida al indicar DISP=SHR.
En la SYSOUT veremos los mensajes de error en caso de que los haya.
Fichero de entrada:
----+----1----+
11111AA100A
22222BB100B
33333CC100K
44444DD100M
campo1: número de cliente
campo2: código de empresa
campo3: saldo
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 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 11 CHARACTERS.
01 REG-ENTRADA PIC X(11).
*
* Fichero de salida de longitud fija (F) igual a 28.
FD SALIDA RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 28 CHARACTERS.
01 REG-SALIDA PIC X(28).
*
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 WB-SIGNOS PIC X.
88 SIGNO-MAS VALUE 'A', 'B',
'C', 'D',
'E', 'F',
'G', 'H',
'I', '{'.
88 SIGNO-MENOS VALUE 'J', 'K',
'L', 'M',
'N', 'O',
'P', 'Q',
'R', '}'.
01 WX-TABLA-EMPRESAS.
05 PIC X(11) VALUE 'AAEMPRESA 1'.
05 PIC X(11) VALUE 'BBEMPRESA 2'.
05 PIC X(11) VALUE 'CCEMPRESA 3'.
05 PIC X(11) VALUE 'DDEMPRESA 4'.
01 REDEFINES WX-TABLA-EMPRESAS.
05 WX-ELEMENTOS OCCURS 4 TIMES
INDEXED BY WI-ELEM.
10 WX-CODIGO-EMPRESA PIC X(2).
10 WX-NOMBRE-EMPRESA PIC X(9).
*
01 WX-REGISTRO-ENTRADA.
05 WX-ENT-CLIENTE PIC 9(5).
05 WX-ENT-COD-EMPRESA PIC X(2).
05 WX-ENT-SALDO PIC S9(4).
*
01 WX-REGISTRO-SALIDA.
05 WX-SAL-CLIENTE PIC 9(5).
05 WX-SAL-COD-EMPRESA PIC X(2).
05 WX-SAL-NOMBRE-EMPRESA PIC X(9).
05 WX-SAL-SALDO PIC 9(4).
05 WX-SAL-SIGNO PIC X(8).
* CONSTANTES
01 WK-POSITIVO PIC X(8) VALUE 'positivo'.
01 WK-NEGATIVO PIC X(8) VALUE 'negativo'.
*
************************************************************
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
.
*
************************************************************
* 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 DEL FICHERO DE ENTRADA:'FS-ENTRADA
END-IF
IF NOT FS-SALIDA-OK
DISPLAY 'ERROR EN OPEN DEL FICHERO 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.
*
PERFORM 21000-BUSCAR-NOMBRE-EMPRESA
PERFORM 22000-BUSCAR-SIGNO-SALDO
PERFORM 23000-INFORMAR-SALIDA
PERFORM ESCRIBIR-SALIDA
PERFORM LEER-ENTRADA
.
*
************************************************************
* 21000 - BUSCAR NOMBRE EMPRESA
*--|------------------+----------><----------+-------------*
* BUSCAMOS EL CODIGO DE EMPRESA DEL FICHERO DE ENTRADA EN
* NUESTRA TABLA INTERNA PARA RECUPERAR EL NOMBRE
************************************************************
21000-BUSCAR-NOMBRE-EMPRESA.
*
*Ponemos el índice WI-ELEM a 1 y se irá incrementando de 1 en 1
SET WI-ELEM TO 1
*Buscamos en WX-TABLA-EMPRESAS el nombre de empresa que tenga
*el mismo código que el del fichero de entrada.
*Si no lo encuentra, movemos espacios al nombre de la empresa
SEARCH WX-ELEMENTOS
AT END
MOVE SPACES TO WX-SAL-NOMBRE-EMPRESA
WHEN WX-CODIGO-EMPRESA(WI-ELEM) EQUAL WX-ENT-COD-EMPRESA
MOVE WX-NOMBRE-EMPRESA(WI-ELEM)
TO WX-SAL-NOMBRE-EMPRESA
END-SEARCH
.
*
************************************************************
* 22000-BUSCAR-SIGNO-SALDO
*--|------------------+----------><----------+-------------*
* COMPROBAMOS EL SIGNO DEL SALDO E INFORMAMOS EL CAMPO:
* WX-SAL-SIGNO
************************************************************
22000-BUSCAR-SIGNO-SALDO.
*
*El signo viene dado por la última posición. La movemos al
*switch WB-SIGNOS. Según su valor informará positivo o negativo
MOVE WX-ENT-SALDO(4:1) TO WB-SIGNOS
EVALUATE TRUE
WHEN SIGNO-MAS
MOVE WK-POSITIVO TO WX-SAL-SIGNO
WHEN SIGNO-MENOS
MOVE WK-NEGATIVO TO WX-SAL-SIGNO
WHEN OTHER
MOVE SPACES TO WX-SAL-SIGNO
END-EVALUATE
.
*
************************************************************
* 23000-INFORMAR-SALIDA
*--|------------------+----------><----------+-------------*
* INFORMAMOS EL RESTO DE CAMPOS DEL FICHERO DE SALIDA
************************************************************
23000-INFORMAR-SALIDA.
*
MOVE WX-ENT-CLIENTE TO WX-SAL-CLIENTE
MOVE WX-ENT-COD-EMPRESA TO WX-SAL-COD-EMPRESA
MOVE WX-ENT-SALDO TO WX-SAL-SALDO
.
*
************************************************************
* 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 FROM WX-REGISTRO-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.
*
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
.
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.
SEARCH: esta sententencia se utiliza para buscar un dato dentro de una tabla interna, recorriéndola usándo un índice y comparando alguno de sus campos con el campo que buscamos.
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.
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 enrtada, 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.
Además comprobamos que el fichero de entrada no venga vacío (en caso de que así sea, finalizamos la ejecución).
En el párrafo de proceso, que se repetirá hasta que se termine el fichero de entrada (FIN-ENTRADA), tenemos varias llamadas a párrafos:
21000-BUSCAR-NOMBRE-EMPRESA:
Busca en la taba interna WX-TABLA-EMPRESAS utilizando la sentencia SEARCH.
22000-BUSCAR-SIGNO-SALDO:
La última posición del campo saldo (S9(4)) nos indica el signo. Podéis ver la referencia a los signos en campos numéricos.
Guardaremos esa última posición en el nivel superior de un campo tipo switch (WB-SIGNOS). Si el valor se corresponde con alguno de los indicados en los niveles 88, ese nivel se activará a "TRUE".
En el "EVALUATE TRUE", el programa entrará por la sentencia "WHEN" que esté activa (que sea "TRUE").
23000-INFORMAR-SALIDA:
Informamos el resto de campos.
ESCRIBIR-SALIDA:
Escribimos nuestro registro ya informado en el fichero de salida.
LEER-ENTRADA:
Leemos el siguiente registro del fichero de entrada.
Fichero de salida:
----+----1----+----2----+----3
11111AAEMPRESA 11001positivo
22222BBEMPRESA 21002positivo
33333CCEMPRESA 31002negativo
44444DDEMPRESA 41004negativo
campo1: número de cliente
campo2: código de empresa
campo3: nombre de empresa
campo4: saldo
campo5: signo del saldo
En este programa además de ver como crear un PROCESO que trate todos los registros de un fichero de entrada, hemos visto varias sentencias como el EVALUATE y el SEARCH. Así aprovechamos para ir introduciendo más sentencias útiles del COBOL en forma de ejemplos.
Y si os queda cualquier duda, estamos aquí para resolverlas : )
Suscribirse a:
Comentarios (Atom)

