15. Tipo de dados adicionais

Todos os programas que vimos até agora utilizavam dados do tipo numérico ou alfanumérico. Mas o COBOL possui alguns outros tipos de dados que são muito utilizados em sistemas legados.

Esse capítulo trata de um assunto que normalmente os livros de COBOL abordam em seus primeiros capítulos. Nosso entendimento, porém, é que, agora que você está familiarizado com os recursos da linguagem, ficará mais fácil entender onde e quando esses tipos de dados adicionais podem ser empregados e onde e quando devem ser evitados.

Entender como cada tipo de dado é armazenado é importante em algumas situações. É muito comum, por exemplo, precisar conferir o conteúdo de um campo decimal compactado ou binário. Por esse motivo, para cada um dos tipos de dados a seguir vamos demonstrar como eles são armazenados e como podemos transformar aquilo que vemos no editor em seu conteúdo real.

A cláusula USAGE

A cláusula USAGE pode ser declarada após o nome ou a picture de uma variável. Ela informa ao compilador a finalidade dessa variável, que consequentemente influencia a forma como o programa vai tratá-la. Em outras palavras, o uso correto da cláusula USAGE pode tornar o sistema mais eficiente; mais seu uso incorreto, por falta de entendimento ou atenção, pode afetar negativamente seu desempenho.

Normalmente a cláusula USAGE está associada a um item elementar, como no exemplo abaixo:

03 WT-AC-VENDAS PIC S9(013)V9(002) VALUE ZEROS USAGE IS PACKED-DECIMAL.

Mas podemos também associá-la a um item de grupo. Nesse caso, todos os seus itens elementares passam a ter o mesmo formato:

01 WT-CONTADORES USAGE IS BINARY.
   03 WT-CT-VENDAS     PIC S9(007) VALUE ZEROS.
   03 WT-CT-COMPRAS    PIC S9(007) VALUE ZEROS.

As palavras USAGE e IS são opcionais e por isso frequentemente são omitidas:

03 WT-AC-VENDAS PIC S9(013)V9(002) VALUE ZEROS PACKED-DECIMAL.

Campos Decimais Zonados

Todas as variáveis que declaramos até aqui (campos de arquivos, variáveis de trabalho, parâmetros externos…) foram do tipo decimal zonado. Eles também são chamados genericamente de USAGE DISPLAY. Esse é o valor default da cláusula USAGE e por isso nunca precisamos declará-la. Por exemplo, quando criamos contadores na WORKING poderíamos ter escrito assim…

01 WT-CONTADORES.
    03 WT-CT-LIDOS    PIC 9(005) VALUE ZEROS DISPLAY.
    03 WT-CT-GRAVADOS PIC 9(005) VALUE ZEROS DISPLAY.

Quando uma variável adota o formato USAGE DISPLAY cada caracter ocupa exatamente um byte de memória, que é representado pelo seu próprio código ASCII ou EBCDIC, dependendo do equipamento.

Assim, um contador com picture 9(005) ocupa exatamente 5 bytes de memória.

Cobol: Campo USAGE DISPLAY
Figura 68. Posicionamento em memória de um campo USAGE DISPLAY

Esse formato é ideal quando precisamos exibir o valor do campo em telas ou relatórios; ou quando precisamos enviar a informação para outros sistemas (em outras plataformas) que podem não ler corretamente outros tipos de dados que são específicos do COBOL.

Campos decimais compactados

Existem outros tipos que são mais eficientes tanto sob o ponto de vista de performance quanto de ocupação de memória. Um desses tipos é o decimal compactado, ou USAGE PACKED-DECIMAL:

01 WT-CONTADORES.
    03 WT-CT-LIDOS     PIC S9(005) VALUE ZEROS USAGE PACKED-DECIMAL.
    03 WT-CT-GRAVADOS  PIC S9(006) VALUE ZEROS USAGE PACKED-DECIMAL.

Quando você declara uma variável numérica com USAGE PACKED-DECIMAL, dois dígitos são armazenados num mesmo byte de memória, com exceção do byte mais à direita, que terá um dígito e um sinal positivo ou negativo:

Cobol: Campo decimal compactado com sinal positivo
Figura 69. Posicionamento em memória de um campo decimal compactado com sinal positivo

Para colocar dois caracteres num único byte o COBOL utiliza os quatro primeiros bits para um caracter e os quatro bits seguintes para o outro caracter. O primeiro efeito, óbvio, é que conseguimos economizar praticamente metade do espaço em memória.

Os números binários agora correspondem a hexadecimais diferentes, que podem ou não ter representação gráfica. Na figura anterior, o símbolo “?” indica que o hexadecimal resultante não tem representação gráfica na tabela ASCII. É por isso que quando usamos um editor de texto puro para abrir um arquivo com campos decimais compactados não conseguimos ver seu conteúdo. Alguns editores possuem recursos para mostrar o hexadecimal correspondente a cada byte, e só usando esses recursos podemos ver e/ou editar o conteúdo desse tipo de campo.

Na maioria das plataformas, o desempenho de operações aritméticas e comparações entre campos numéricos decimais compactados é superior quando comparado aos campos numéricos USAGE DISPLAY. A contrapartida é que poucas linguagens de programação sabem interpretar os campos PACKED-DECIMAL criados pelo COBOL. Essa é uma das razões pelas quais evitamos usar esse tipo de formato de campo em arquivos que serão exportados para sistemas de outras plataformas.

Outro ponto que vale a pena destacar é que um campo decimal compactado sempre ocupará uma quantidade ímpar de bytes de memória: um campo 9(005) ou S9(005) ocupará 3 bytes, porque metade do byte mais à direita será reservado para o sinal mesmo que esse sinal não tenha sido declarado explicitamente na picture.

Consequentemente, campos decimais compactados de tamanhos pares – S9(008), S9(010) , S9(008)V9(002) – sempre desperdiçam metade do byte mais à esquerda. Observe na figura abaixo o posicionamento em memória de uma variável PIC 9(006) PACKED-DECIMAL preenchida com o valor 001932:

Cobol: Campo decimal compactado com tamanho par
Figura 70. Posicionamento em memória de campo decimal compactado com tamanho par

Repare que o programa teve que completar metade do byte mais à esquerda com zero binário. O espaço de memória é o mesmo que seria ocupado por uma variável S9(007). Esse é um dos motivos pelos quais recomenda-se que toda variável decimal compactada tenha tamanho ímpar e uma declaração explícita de sinal. Mas além disso, essa prática favorece o desempenho do programa, ainda que hoje em dia qualquer diferença só seja percebida em programas que processam grandes volumes de dados, na ordem de dezenas de milhões de registros ou mais.

O tamanho máximo de um campo decimal compactado é de 31 dígitos, contando a parte inteira e a decimal, se houver. Assim, PIC S9(029)V9(002) PACKED-DECIMAL é um tamanho válido, pois o sinal e a vírgula não ocupam espaços.

A notação “PACKED DECIMAL” é relativamente recente. É bastante provável que você encontre programas usando a opção mais tradicional COMPUTATIONAL-3 ou COMP-3 na cláusula USAGE, como nos exemplos abaixo. O significado e o funcionamento é exatamente o mesmo que vimos aqui.

03 WT-VALOR PIC S9(013)V9(002) USAGE COMP-3 VALUE ZEROS.
03 WT-CONTADOR PIC S9(007) COMPUTATIONAL-3 VALUE ZEROS.
03 WT-ACUMULADOR PIC S9(015)V9(006) COMP-3 VALUE ZEROS.

Campos binários

O uso da cláusula USAGE BINARY faz com que o COBOL armazene o dado numérico na forma binária. O maior benefício desse formato é o desempenho obtido em comparações e operações aritméticas. Abaixo temos dois exemplos de declaração de campos numéricos binários:

03 WT-CONTADOR PIC S9(004) USAGE IS BINARY VALUE ZEROS.
03 WT-NUMERO   PIC S9(008) BINARY VALUE ZEROS.

Existem algumas diferenças significativas no tratamento de campos binários de plataforma para plataforma, e de compilador para compilador, a começar pela quantidade de bytes ocupados. Em geral, o espaço ocupado por um campo binário depende diretamente do tamanho de sua picture. A regra abaixo se aplica para a maioria dos compiladores:

Cobol: Espaço ocupado por campos binários
Figura 71. Espaço ocupado por campos binários

A forma como o conteúdo é armazenado num campo binário também pode variar. Na maioria das plataformas, porém, os compiladores COBOL usam o formato big-endian. Para ilustrar como esse formato funciona, vamos analisar um campo binário definido como S9(004) e preenchido com o valor 1932:

03 WT-CONTADOR PIC S9(004) BINARY VALUE 1932.

Ao criar esse campo em memória o programa vai transformá-lo em um número binário de 16 bits igual a 00000111 10001100.

No formato big-endian o bit mais à esquerda representa o sinal: 0 para positivo e 1 para negativo. Esse formato também estabelece que os dígitos mais significativos (os dígitos mais à esquerda) devem ser armazenados antes dos menos significativos. Como o campo do nosso exemplo é sinalizado, o valor máximo que esse campo pode receber é 32767.

Cobol: Campo binário big-endian
Figura 72. Campo binário no formato big-endian

Se esse campo estiver num arquivo e você pedir para exibi-lo em hexadecimal, verá dois bytes preenchidos com x’07’ e x’8C’, que não têm representação gráfica na tabela ASCII.

Cobol: Campo binário com valor positivo
Figura 73. Posicionamento de campo binário em memória com valor positivo

No formato big-endian a representação de valores negativos usa a operação complemento de dois. Essa operação inverte (flip) todos os bits do número positivo e soma 1 ao conteúdo flipado. Se armazenássemos o valor -1932 no nosso exemplo o conteúdo armazenado seria:

          0000 0111 1000 1100
flip      1111 1000 0111 0011
+1        1111 1000 0111 0100

O valor binário que seria armazenado seria 11111000 01110100, com o bit mais à esquerda indicando que trata-se de um número negativo:

Cobol: Campo binário em memória

Se esse campo estiver num arquivo e você pedir para exibi-lo em hexadecimal, verá dois bytes preenchidos com x’F8’ e x’74’. O primeiro não tem representação gráfica na tabela ASCII; o segundo representa um T minúsculo.

A notação “BINARY” também é relativamente recente. É mais provável que você encontre programas usando a opção mais tradicional COMPUTATIONAL ou COMP na cláusula USAGE, como nos exemplos abaixo. O significado e o funcionamento é exatamente o mesmo que vimos aqui.

03 WT-VALOR PIC S9(004) USAGE COMP VALUE ZEROS.
03 WT-CONTADOR PIC S9(002) COMPUTATIONAL VALUE ZEROS.
03 WT-ACUMULADOR PIC S9(008) COMP VALUE ZEROS.

Campos (quase) booleanos

O COBOL possui um recurso chamado nome condicional que se assemelha (mas não é igual) aos campos do tipo booleano que encontramos em outras linguagens de programação.

Os nomes condicionais permitem que o programador dê nomes às condições. Por exemplo, você pode codificar um IF com operandos e operadores…

IF WT-ST-CRA0201 NOT = “00”

…ou simplesmente dar um nome à essa condição:

IF CRA0201-FIM-DE-ARQUIVO

Os nomes condicionais são criados na DATA DIVISION com um nível especial, 88:

03 WT-ST-CRA0201 PIC X(002) VALUE SPACES.
    88 CRA0201-LEITURA-OK     VALUE “00”.
    88 CRA0201-FIM-DE-ARQUIVO VALUE “10”.
    88 CRA0201-DUPLICADO      VALUE “22”.
    88 CRA0201-NAO-ENCONTRADO VALUE “23”.

O nível 88 não cria um item elementar sob um item de grupo, como acontece com os níveis 02 a 49. Ele simplesmente estabelece um nome para um possível valor da variável. Depois de definidos na DATA DIVISION, os nomes condicionais podem ser usados em IFs, UNTILs, EVALUATEs e/ou qualquer outro comando que espere uma condição.

Para o exemplo acima, codificar…

PERFORM 2-PROCESSA UNTIL CRA0201-FIM-DE-ARQUIVO

PERFORM 21-LE-REGISTROS UNTIL NOT CRA0201-LEITURA-OK

IF NOT CRA0201-FIM-DE-ARQUIVO AND NOT CRA0201-NAO-ENCONTRADO

…é a mesma coisa que codificar…

PERFORM 2-PROCESSA UNTIL WT-ST-CRA0201 = “10”

PERFORM 21-LE-REGISTROS UNTIL WT-ST-CRA0201 NOT = “00”

IF WT-ST-CRA0201 NOT = “10” AND WT-ST-CRA0201 NOT = “23”

Você pode criar nomes condicionais com um, dois ou mais valores. No exemplo abaixo, a variável WT-TP-OPERACAO pode receber os valores “I”, “A”, “E” ou “C” que estão associados ao nome OPERACAO-VALIDA:

03 WT-TP-OPERACAO PIC X(001) VALUE SPACES.
    88 OPERACAO-VALIDA       VALUE “I” “A” “E” “C”.

Com isso, codificar o teste…

IF OPERACAO-VALIDA

…seria a mesma coisa que…

IF WT-TP-OPERACAO = “I” OR “A” OR “E” OR “C”

Você também pode estabelecer um range de valores para a condição. No exemplo abaixo a condição CATEGORIA-VALIDA seria verdadeira se a variável WT-CATEGORIA receber qualquer valor entre A e F:

03 WT-CATEGORIA PIC X(001) VALUE SPACES.
    88  CATEGORIA-VALIDA   VALUE “A” THRU “F”.

O uso do nível 88 é uma questão de preferência pessoal: existem instalações que recomendam, outras que proíbem. Uns acreditam que o programa fica mais documentado, pois o nome deixa claro que tipo de teste está sendo realizado. Outros acham que dificulta a manutenção, pois você teria que ir à DATA DIVISION para checar que valores estão sendo realmente testados.

Nomes condicionais são muito usados para flags e switches. É relativamente comum encontrar programas assim:

    03 FIM-DE-ARQUIVO PIC X(001) VALUE “N”.
        88 EOF `                 VALUE “S”.

`...

    READ ARQUIVO AT END SET EOF TO TRUE.

O comando SET condição TO TRUE torna a condição verdadeira, estabelecendo para a variável associada o primeiro valor declarado no nível 88. No exemplo acima, o comando SET EOF TO TRUE, na prática, moveu “S” para a variável FIM-DE-ARQUIVO. Se EOF tivesse mais de um valor (digamos, “Y” “S”), o comando teria movido “Y”.

Detalhe curioso: não existe um comando SET condição TO FALSE em COBOL. Isso faria sentido se a variável fosse realmente do tipo booleano. Mas como o nome condicional precisa associar valores a variáveis, com um comando SET … TO FALSE o programa não saberia que valor mover para a variável.


Anterior Conteúdo Próxima