13. Operações com Strings

13. Operações com Strings

A maior parte do trabalho com sistemas comerciais consiste basicamente em processar arquivos, registros e campos. Algumas vezes, porém, é necessário fazer algumas operações com strings, caracter a caracter. O COBOL possui alguns recursos específicos para facilitar a operação com strings.

Substrings

Modificadores de referência são recursos do COBOL que permitem ao programador fazer referências a uma parte de uma variável alfanumérica. Isso também poderia ser feito através da subdivisão da variável em itens elementares, mas os modificadores de referência facilitam esse trabalho.

Veja o exemplo abaixo. A data é uma variável alfanumérica de 10 posições no formato DD/MM/AAAA. Suponha que tenhamos que mover apenas o mês e o ano para um determinado campo. Uma das soluções seria:

01 DATA-X.
    03 FILLER     PIC X(003).
    03 MES-ANO-X  PIC X(007).

...

MOVE MES-ANO-X TO WR-DET-MES-ANO

Usando modificados de referência podemos mover apenas a parte que nos interessa, sem precisar criar itens elementares só para isso:

01 DATA-X PIC X(010).

...

MOVE DATA-X(4:7) TO WR-DET-MES-ANO

O comando acima move apenas a substring que começa na posição 4 e tem tamanho de 7 caracteres. Você poderia usar variáveis – ou uma combinação de variáveis e literais – para indicar a posição inicial e a extensão da substring que pretende acessar. Os três exemplos abaixo são válidos:

MOVE DATA-X(POSINI:TAMSTR) TO WR-DET-MES-ANO
MOVE DATA-X(WT-INI:7) TO WR-DET-MES-ANO
MOVE DATA-X(4:WT-TAM) TO WR-DET-MES-ANO

A extensão pode ser omitida. O exemplo abaixo moveria para a variável WR-DET-MES-ANO a substring de DATA-X que começa na posição 4 e que vai até o último caracter dessa variável:

MOVE DATA-X(4:) TO WR-DET-MES-ANO

Também é possível aplicar modificadores de referência a ocorrências isoladas de uma tabela interna ou array. O próximo exemplo move apenas os três primeiros caracteres da décima segunda ocorrência da tabela interna WV-MES:

MOVE WV-MES(12) (1:3) TO WR-DET-MES

Concatenando strings

O COBOL permite a concatenação de dois ou mais campos alfanuméricos através do comando STRING. Seu formato mais simples é:

STRING campo1 campo2 ... campoN INTO campoX

Neste formato, todo o conteúdo de campo1, campo2 … campoN é copiado, caracter a caracter, para campoX.

01  CAMPO1      PIC X(010) VALUE “COMMON”.
01  CAMPO2      PIC X(010) VALUE “BUSINESS”.
01  CAMPO3      PIC X(010) VALUE “ORIENTED”.
01  CAMPO4      PIC X(010) VALUE “LANGUAGE”.

01 NOME-COMPLETO PIC X(040) VALUE SPACES.

...

STRING CAMPO1 CAMPO2 CAMPO3 CAMPO4 INTO NOME-COMPLETO

DISPLAY “NOME COMPLETO: “ NOME-COMPLETO

O trecho acima, quando executado, mostrará:

NOME COMPLETO: COMMON    BUSINESS  ORIENTED  LANGUAGE

Os espaços entre as palavras são os espaços que existiam à direita de CAMPO1, CAMPO2, CAMPO3 e CAMPO4 (repare que essas variáveis têm dez caracteres de tamanho). O comando concatenou todos os caracteres (inclusive os espaços em branco) e os juntou na variável NOME-COMPLETO porque assumiu a opção default DELIMITED BY SIZE.

Mas o comando STRING permite que sejam estabelecidos outros delimitadores. Seguindo no mesmo exemplo, se fizéssemos…

STRING CAMPO1 CAMPO2 CAMPO3 CAMPO4
    DELIMITED BY SPACE
    INTO NOME-COMPLETO

DISPLAY “NOME COMPLETO: “ NOME-COMPLETO

… teríamos a seguinte saída:

NOME COMPLETO: COMMONBUSINESSORIENTEDLANGUAGE

Observe que o caracter que indicamos na cláusula DELIMITED BY marca o fim da string a ser copiada; o delimitador em si não é copiado. Para inserir um espaço depois de cada palavra poderíamos combinar variáveis e literais, cada uma delas com um tipo de delimitador:

           STRING CAMPO1 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO2 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO3 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO4 DELIMITED BY SPACE
               INTO NOME-COMPLETO

           DISPLAY "NOME COMPLETO: " NOME-COMPLETO

Nesse exemplo o programa exibiria…

NOME COMPLETO: COMMON BUSINES ORIENTED LANGUAGE

…porque as variáveis CAMPO1, CAMPO2, CAMPO3 e CAMPO4 foram concatenadas até o primeiro espaço encontrado, exclusive. Já os literais “ “ (espaço) foram copiados by size.

O comando STRING não apaga o valor do campo de destino antes de iniciar as cópias; ele simplesmente copia o primeiro caracter do primeiro campo para a primeira posição do campo de destino e para de copiar quando chega ao final do campo (DELIMITED BY SIZE) ou encontra o delimitador informado (DELIMITED BY literal). Os caracteres do campo de destino que não receberam conteúdo ficam com seus valores anteriores. Observe o exemplo abaixo:

           MOVE ALL “*” TO NOME-COMPLETO

           STRING CAMPO1 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO2 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO3 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO4 DELIMITED BY SPACE
               INTO NOME-COMPLETO

           DISPLAY "NOME COMPLETO: " NOME-COMPLETO

O campo de destino (NOME-COMPLETO) foi previamente preenchido com asteriscos. Quando o comando STRING é executado os campos e literais de origem são copiados caracter a caracter, deixando os demais com o conteúdo anterior. O resultado do DISPLAY seria:

NOME COMPLETO: COMMON BUSINESS ORIENTED LANGUAGE************************

Por default, os caracteres começam a ser copiados para a primeira posição do campo de destino, mas isso pode ser mudado com a cláusula WITH POINTER:

           MOVE ALL “*” TO NOME-COMPLETO
           MOVE 6 TO PONTEIRO

           STRING CAMPO1 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO2 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO3 DELIMITED BY SPACE
                  " "    DELIMITED BY SIZE
                  CAMPO4 DELIMITED BY SPACE
               INTO NOME-COMPLETO
               WITH POINTER PONTEIRO

           DISPLAY "NOME COMPLETO: " NOME-COMPLETO

Nesse caso, criamos na WORKING uma variável chamada PONTEIRO e iniciamos ela com o valor 6 antes do comando STRING. Ela fará com que o conteúdo concatenado só comece na sexta posição do campo de destino. A saída desse trecho de programa seria:

NOME COMPLETO: *****COMMON BUSINESS ORIENTED LANGUAGE*****************

Separando strings

Quando precisamos separar uma string maior em strings menores, delimitadas por algum caracter ou não, usamos o comando UNSTRING, que em seu formato mais simples pode ser codificado assim:

UNSTRING campo-origem
    INTO campo1 campo2 ... campoN

Vamos ver como funciona num exemplo prático:

       01 ELEMENTOS.
           03 ELEMENTO1 PIC X(020) VALUE SPACES.
           03 ELEMENTO2 PIC X(020) VALUE SPACES.
           03 ELEMENTO3 PIC X(020) VALUE SPACES.
           03 ELEMENTO4 PIC X(020) VALUE SPACES.
   
       01 NOME-COMPLETO PIC X(080) VALUE
          “COMMON BUSINESS ORIENTED LANGUAGE”.

       ...

       UNSTRING NOME-COMPLETO
           INTO ELEMENTO1 ELEMENTO2 ELEMENTO3 ELEMENTO4

       DISPLAY “ELEMENTO1: “ ELEMENTO1
       DISPLAY “ELEMENTO2: “ ELEMENTO2
       DISPLAY “ELEMENTO3: “ ELEMENTO3
       DISPLAY “ELEMENTO4: “ ELEMENTO4

Esse trecho de código exibiria as seguintes mensagens na tela do usuário:

ELEMENTO1: COMMON BUSINESS ORIE
ELEMENTO2: NTED LANGUAGE
ELEMENTO3:
ELEMENTO4:

Repare que o programa copiou o campo de origem (NOME-COMPLETO), caracter a caracter, para os campos de destino (ELEMENTO1, ELEMENTO2, ELEMENTO3 e ELEMENTO4). Ele preencheu os 20 bytes do primeiro campo e depois continuou no segundo. Os campos 3 e 4 ficaram com espaços porque não havia mais conteúdo no campo de origem.

Parar separar o campo de origem palavra por palavra precisamos indicar um delimitador. O trecho…

       UNSTRING NOME-COMPLETO
           DELIMITED BY SPACE
           INTO ELEMENTO1 ELEMENTO2 ELEMENTO3 ELEMENTO4

       DISPLAY “ELEMENTO1: “ ELEMENTO1
       DISPLAY “ELEMENTO2: “ ELEMENTO2
       DISPLAY “ELEMENTO3: “ ELEMENTO3
       DISPLAY “ELEMENTO4: “ ELEMENTO4

…mostraria:

ELEMENTO1: COMMON
ELEMENTO2: BUSINESS
ELEMENTO3: ORIENTED
ELEMENTO4: LANGUAGE

Assim como no comando STRING, qualquer literal pode ser usada como delimitador no comando UNSTRING:

UNSTRING LINHA-PLANILHA
    DELIMITED BY “;”
    INTO CIDADE ESTADO PAIS CEP

Separaria a variável LINHA-PLANILHA…

ITABIRITO;MINAS GERAIS;BRASIL;35450-000

Em…

CIDADE: ITABIRITO
ESTADO: MINAS GERAIS
PAIS: BRASIL
CEP: 35450-000

Agora observe esse outro exemplo, onde a variável LINHA planilha possui mais de um ponto-e-vírgula separando cada campo:

ITABIRITO;;;MINAS GERAIS;;;BRASIL;;;35450-000

Nesse caso, o resultado do UNSTRING seria:

CIDADE: ITABIRITO
ESTADO:
PAIS:
CEP: MINAS GERAIS

Isso aconteceu porque dissemos que nosso delimitador era formado por um único ponto-e-vírgula. O COBOL interpretou que entre o primeiro e o segundo ponto-e-vírgula existia um caracter nulo e por isso preencheu o segundo campo (ESTADO) com esse valor. A mesma coisa aconteceu entre o segundo o segundo e o terceiro ponto-e-vírgula, o que também deixou o campo PAIS vazio.

Para instruir o COBOL a interpretar sucessivas ocorrências de um delimitador como um só precisamos incluir a opção ALL na cláusula DELIMITED BY:

UNSTRING LINHA-PLANILHA
    DELIMITED BY ALL “;”
    INTO CIDADE ESTADO PAIS CEP

Resultaria em…

CIDADE: ITABIRITO
ESTADO: MINAS GERAIS
PAIS: BRASIL
CEP: 35450-000

Pesquisando substrings

Quando queremos verificar se uma substring aparece no conteúdo de uma variável alfanumérica usamos o comando INSPECT:

INSPECT variável TALLYING contador FOR ALL literal

Esse comando vai contar quantas vezes literal aparece no conteúdo de variável e a quantidade será armazenada na variável numérica contador. O exemplo abaixo conta quantas vezes o caracter arroba aparece no conteúdo da variável WT-NM-EMAIL. O resultado é salvo na variável WT-CT-ARROBA:

INSPECT WT-NM-EMAIL
   TALLYING WT-CT-ARROBA
   FOR ALL “@”

O comando INSPECT possui diversas variações. É possível, por exemplo, obter o tamanho efetivamente preenchido em uma variável alfanumérica:

03 NOME         PIC X(020) VALUE “PAULO”.
03 CONTADOR     PIC 9(006) VALUE ZEROS.

...

INSPECT NOME
    TALLYING CONTADOR
    FOR CHARACTERS BEFORE SPACE

DISPLAY “NOME PREENCHIDO COM “ CONTADOR “ CARACTERES”

O exemplo acima mostraria…

NOME PREENCHIDO COM 5 CARACTERES

…pois instruímos o comando a contar a quantidade de caracteres existentes antes do primeiro espaço. Naturalmente, também é possível contar substrings com mais de um caracter:

03 NOME-COMPLETO       PIC X(040) VALUE “JOHN SMITH”.
03 SOBRENOME          PIC X(005) VALUE “SMITH”.
03 CONTADOR           PIC 9(006) VALUE ZEROS.

...

INSPECT NOME-COMPLETO
    TALLYING CONTADOR
    FOR ALL SOBRENOME

DISPLAY “O SOBRENOME APARECE “ CONTADOR “VEZ(ES)”

Esse exemplo acima mostraria:

O SOBRENOME APARECE 1 VEZ(ES)

O comando INSPECT também permite contar mais de uma substring ao mesmo tempo usando mais de um contador. O exemplo abaixo conta quantas vezes cada vogal aparece numa variável alfanumérica:

INSPECT variável
    TALLYING contadorA FOR ALL “A”
             contadorE FOR ALL “E”
             contadorI FOR ALL “I”
             contadorO FOR ALL “O”
             contadorU FOR ALL “U”

Podemos também contar várias substrings usando um único contador:

INSPECT variável
    TALLYING contador FOR ALL “A” ALL “E” ALL “I” ALL “O” ALL “U”

Substituindo substrings

O comando INSPECT possui uma cláusula opcional chamada REPLACING que permite a substituição de strings dentro de uma variável alfanumérica. No exemplo abaixo todas as ocorrências de conteudo1 em variável será substituído por conteudo2:

INSPECT variável
    REPLACING ALL conteudo1 BY conteudo2

O comando INSPECT não pode inserir ou excluir caracteres de uma variável. Por esse motivo o conteúdo anterior e o conteúdo novo devem sempre a mesma quantidade de caracteres.

A opção REPLACING também permite a substituição de todos os caracteres antes ou depois de determinado delimitador:

03 EMAIL PIC X(100) VALUE “CONTATO@ME.COM”.

...

DISPLAY “ANTES  = “ EMAIL

INSPECT EMAIL
    REPLACING CHARACTERS BY SPACES
    AFTER INITIAL “@”

DISPLAY “DEPOIS = “ EMAIL

…mostraria:

ANTES  = CONTATO@ME.COM
DEPOIS = CONTATO@

E existem outras opções posicionais:

Cobol: Exemplo de INSPECT com a cláusula REPLACING
Figura 58. Exemplo de INSPECT com a cláusula REPLACING

Existe um outro formato do comando INSPECT que não é a ideal para a substituição de substrings mas pode facilitar muito a conversão de caracteres.

INSPECT variável
    CONVERTING literal1 TO literal2

Esse comando é mais prático quando precisamos substituir um conjunto de caracteres por outro, pois todos eles podem ser declarados num único literal. O comando substitui o primeiro caracter de literal1 pelo primeiro caracter de literal2, o segundo caracter de literal1 pelo segundo caracter de literal2 e assim sucessivamente.

Veja alguns exemplos:

Cobol: Exemplos de INSPECT com cláusula CONVERTING
Figura 59. Exemplos de INSPECT com cláusula CONVERTING

Processamento de strings, na prática

Vamos ver um exemplo real em que o dado precisa ser processado caracter a caracter. Vamos aproveitar e rever algumas operações de entrada e saída com arquivos indexados.

Cada registro do cadastro de clientes que usamos em alguns exemplos anteriores está com o campo NR-TELEFONE-1 editado de uma maneira diferente, situação muito comum em sistemas legados. Precisamos construir um programa que altere esse campo padronizando a edição do número de telefone da seguinte maneira:

  • Para telefones fixos: (AA) PPPP-MMMM, onde AA é o código de área, PPPP é o prefixo de quatro dígitos dos telefones fixos e MMMM é a milhar
  • Para telefones móveis: (AA) PPPPP-MMMM, onde AA é o código de área, PPPPP é o prefixo de cinco dígitos dos telefones móveis e MMMM é a milhar.

O quadro abaixo mostra como estão os telefones nesse momento e como eles devem ficar após a execução do programa:

Cobol: Arquivo para o programa de exemplo
Figura 60. Número do telefone no cadastro de clientes

Repare que existem registros onde o telefone foi carregado sem edição nenhuma, outros que estão sem código de área, outros ainda editados de com pontos no lugar dos traços… e até alguns que estão com a edição correta.

Nosso próximo programa precisa ler todos os registros desse cadastro e padronizar a edição para que todos os telefones fiquem como aparece na terceira coluna.

Vamos quebrar o problema em sete passos:

  1. Eliminar caracteres não numéricos. Vamos substituir todos os parênteses, traços e pontos por espaços. O telefone do cliente 003502, por exemplo, passaria de “31-3561-1973” para “ 31 3561 1973”.
  2. Separar o número de telefone em campos menores, delimitados por espaços. O telefone do cliente 003502 seria dividido em três variáveis: a primeira ficaria com “31”, a segunda com “3561” e a terceira com “1973”.
  3. Reagrupar os campos menores, aproveitando apenas os caracteres numéricos. O telefone do cliente 003502 ficaria com “3135611973”
  4. Verificar o tamanho da string resultante. O tamanho da string é que vai nos ajudar a decidir se o telefone é fixo ou móvel. Um tamanho de 8 caracteres significa que o telefone é fixo e está sem código de área. Um tamanho 9 significa que o telefone é móvel e também está sem código de área. Tamanho 10 indica que o telefone é fixo mas está com código de área preenchido. Tamanho 11 corresponde a um telefone móvel com código de área preenchido.
  5. Determinar código de área. Como alguns telefones estão sem código de área, precisamos definir um critério para determiná-lo durante a execução do programa. Nosso cadastro hipotético só tem clientes dos estados do Rio de Janeiro e de Minas Gerais. Logo, o código de área será 21 para RJ e 31 para MG.
  6. Aplicar uma máscara de edição ao número de telefone. Se o telefone for fixo, a edição será (AA) PPPP-MMMM. Se o telefone for móvel a edição será (AA) PPPPP-MMMM. Os caracteres que usaremos para preencher AA, PPPP, PPPPP e MMMM vai depender da quantidade de caracteres existentes no telefone reagrupado, que calculamos no passo 4.
  7. Regravar registro do cadastro de clientes com o número de telefone editado.

O programa acessará apenas o cadastro de clientes, arquivo CRA0201. A codificação da IDENTIFICATION, da ENVIRONMENT e da FILE SECTION não têm nenhuma particularidade, por isso veremos o passo a passo para construção do programa a partir da WORKING. Você poderá conferir o programa completo mais adiante nesse capítulo.

Começamos com o file status do cadastro de clientes…

      *================================================================*
       WORKING-STORAGE SECTION.
      *----------------------------------------------------------------*
       01 WT-FILE-STATUS.
           03 WT-ST-CRA0201            PIC  X(002) VALUE SPACES.

E em seguida criamos dois itens de grupo que usaremos para edição de telefones fixos e móveis. O que muda nesses dois campos é o tamanho do item elementar chamado prefixo, que possui 4 caracteres para telefones fixos e 5 caracteres para telefones móveis:

       01 WT-TELEFONE-FIXO.     
           03 FILLER                   PIC  X(001) VALUE "(".
           03 WT-FIXO-AREA             PIC  X(002) VALUE SPACES.
           03 FILLER                   PIC  X(002) VALUE ")".
           03 WT-FIXO-PREFIXO          PIC  X(004) VALUE SPACES.
           03 FILLER                   PIC  X(001) VALUE "-".
           03 WT-FIXO-MILHAR           PIC  X(004) VALUE SPACES.
    
       01 WT-TELEFONE-MOVEL.    
           03 FILLER                   PIC  X(001) VALUE "(".
           03 WT-MOVEL-AREA            PIC  X(002) VALUE SPACES.
           03 FILLER                   PIC  X(002) VALUE ")".
           03 WT-MOVEL-PREFIXO         PIC  X(005) VALUE SPACES.
           03 FILLER                   PIC  X(001) VALUE "-".
           03 WT-MOVEL-MILHAR          PIC  X(004) VALUE SPACES.

Por último declaramos algumas variáveis auxiliares. Precisamos de uma variável para calcular o tamanho dos strings que estiverem em cada telefone:

       01 WT-AUXILIARES.
           03 WT-AUX-TAMANHO           PIC  9(002) VALUE ZEROS.

Em seguida, precisamos de algumas variáveis alfanuméricas que usaremos para quebrar o telefone do cliente em componentes menores. A quantidade de variáveis que precisaremos está diretamente relacionada às diferentes formas de edição dos telefones. Sabemos que o telefone será quebrado em componentes menores separados (delimitados) por espaços. Logo, “26416965” precisará apenas de uma variável; “31 3551 1283” precisaria de três… Vamos criar umas cinco. Podemos ajustar depois dos primeiros testes, se for necessário.

       01 WT-AUXILIARES.
           03 WT-AUX-TAMANHO           PIC  9(002) VALUE ZEROS.
           03 WT-AUX-PARTE-1           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-2           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-3           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-4           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-5           PIC  X(015) VALUE SPACES.

Precisamos também de uma variável para reagrupar as partes que foram quebradas no passo anterior:

       01 WT-AUXILIARES.
           03 WT-AUX-TAMANHO           PIC  9(002) VALUE ZEROS.
           03 WT-AUX-PARTE-1           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-2           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-3           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-4           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-5           PIC  X(015) VALUE SPACES.
           03 WT-AUX-TELEFONE          PIC  X(015) VALUE SPACES.

E mais uma variável auxiliar para guardarmos o código de área que estabeleceremos a partir do Estado (RJ ou MG) de cada cliente:

       01 WT-AUXILIARES.
           03 WT-AUX-TAMANHO           PIC  9(002) VALUE ZEROS.
           03 WT-AUX-PARTE-1           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-2           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-3           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-4           PIC  X(015) VALUE SPACES.
           03 WT-AUX-PARTE-5           PIC  X(015) VALUE SPACES.
           03 WT-AUX-TELEFONE          PIC  X(015) VALUE SPACES.
           03 WT-AUX-AREA              PIC  X(002) VALUE SPACES.

O diagrama estruturado do nosso programa é bem simples. Toda a lógica de edição dos números de telefone acontecerá no parágrafo 2-PROCESSA, que será executado para todos os registros do cadastro de clientes:

Cobol: Diagrama estruturado de programa
Figura 61. Diagrama estruturado do programa CRP0302

O parágrafo 1-INICIA tem apenas a abertura do arquivo e a leitura do primeiro registro. Nossa intenção é processar todos os registros do arquivo de entrada e por isso precisamos acessá-lo sequencialmente. Mas sabemos que o book de declaração desse arquivo usa ACCESS MODE IS DYNAMIC. Logo, para ler esse arquivo sequencialmente precisamos complementar o comando READ com a cláusula NEXT:

      *----------------------------------------------------------------*
      * ABRE ARQUIVOS E LE PRIMEIRO REGISTRO DO ARQUIVO DE ENTRADA    
      *----------------------------------------------------------------*
       1-INICIA.

           OPEN I-O CRA0201

           READ CRA0201 NEXT.

Começamos o parágrafo 2-PROCESSA codificando o primeiro dos sete passos que descrevemos anteriormente. Precisamos substituir todos os caracteres não numéricos por espaços. Para isso, usaremos o comando INSPECT REPLACING:

      *----------------------------------------------------------------*
      * ELIMINA TODOS OS CARACTERES NAO NUMERICOS, QUEBRA A VARIAVEL
      * EM COMPONENTES NUMERICOS, CONCATENA ESSES COMPONENTES, APLICA
      * UMAS MASCARA DE EDICAO AO NUMERO CONCATENADO E ATUALIZA O RE-
      * GISTRO COM O NUMERO EDITADO
      *----------------------------------------------------------------*
       2-PROCESSA.

      * ELIMINA CARACTERES NAO NUMERICOS
           INSPECT CRA0201-NR-TELEFONE-1
              REPLACING ALL "(" BY SPACE
                        ALL ")" BY SPACE
                        ALL "-" BY SPACE
                        ALL "." BY SPACE

Você pode ver o efeito desse comando sobre os números de telefone na figura abaixo. Repare que, agora, só existem números e espaços nesse campo:

Cobol: Efeito do comando INSPECT REPLACING
Figura 62. Efeito do comando INSPECT REPLACING em cada número de telefone processado

Nosso segundo passo precisa quebrar o número de telefone em partes delimitadas por espaços. O comando UNSTRING processa caracter a caracter e para quando não há mais caracteres no campo de origem. Esse Por esse motivo é possível que, em alguns casos, ele não precise de todas as cinco variáveis que usaremos como campo de destino. Como o UNSTRING não faz nenhum tipo de inicialização desses campos, precisamos garantir que, antes da sua execução, todas as variáveis sejam inicializadas com espaços.

* QUEBRA NUMERO DE TELEFONE EM COMPONENTES NUMERICOS
    
           MOVE SPACES TO WT-AUX-PARTE-1
           MOVE SPACES TO WT-AUX-PARTE-2
           MOVE SPACES TO WT-AUX-PARTE-3
           MOVE SPACES TO WT-AUX-PARTE-4
           MOVE SPACES TO WT-AUX-PARTE-5

           UNSTRING CRA0201-NR-TELEFONE-1
               DELIMITED BY ALL SPACES
               INTO WT-AUX-PARTE-1   
                    WT-AUX-PARTE-2
                    WT-AUX-PARTE-3
                    WT-AUX-PARTE-4
                    WT-AUX-PARTE-5

O efeito desse comando pode ser visto na figura abaixo.

Cobol: Efeito do comando UNSTRING
Figura 63. Efeito do comando UNSTRING em cada telefone processado

Repare que, em alguns casos, o UNSTRING não colocou nada na variável WT-AUX-PARTE-1. Isso aconteceu quando o número de telefone transitório começava com um espaço em branco. E esses números ficaram assim depois que substituímos os caracteres não numéricos por espaços.

Agora podemos reagrupar essas partes, também delimitadas por espaços, numa única variável. Como o comando STRING também não inicializa a variável de destino, moveremos espaços para WT-AUX-TELEFONE antes do comando:

* REAGRUPA COMPONENTES DO TELEFONE

           MOVE SPACES TO WT-AUX-TELEFONE

           STRING WT-AUX-PARTE-1
                  WT-AUX-PARTE-2
                  WT-AUX-PARTE-3
                  WT-AUX-PARTE-4
                  WT-AUX-PARTE-5
               DELIMITED BY SPACE
               INTO WT-AUX-TELEFONE

Observe na figura abaixo que agora temos uma variável alfanumérica com todos os caracteres numéricos alinhados à esquerda:

Cobol: Efeito do comando STRING
Figura 64. Efeito do comando STRING em cada número de telefone processado

Estamos quase prontos para mover o número de telefone para a variável editada. Antes disso, precisamos decidir se o telefone é fixo ou móvel, pois as máscaras de edição são diferentes. Para isso precisamos saber quantos caracteres estão preenchidos na variável auxiliar e faremos isso com o comando INSPECT TALLYING:

      * OBTEM O TAMANHO DO TELEFONE APOS A CONCATENACAO

           MOVE ZEROS TO WT-AUX-TAMANHO

           INSPECT WT-AUX-TELEFONE           
               TALLYING WT-AUX-TAMANHO
               FOR CHARACTERS
               BEFORE SPACE

Veja que usando a opção “FOR CHARACTERS” estamos contando quantos caracteres diferentes de espaços temos na variável WT-AUX-TELEFONE. O resultado desse comando é mostrado na figura abaixo:

Cobol: Efeito do comando INSPECT TALLYING
Figura 65. Efeito do comando INSPECT TALLYING sobre cada número de telefone processado

Agora determinaremos o código de área em função do Estado onde cada cliente está localizado. A princípio precisamos fazer isso apenas para os telefones que estão sem código de área (aqueles que têm tamanho 8 ou 9). Para simplificar, testaremos o campo CD-ESTADO de todos os telefones e armazenaremos o código de área numa variável auxiliar:

      * PREENCHE O CODIGO DE AREA EM FUNCAO DO CAMPO ESTADO

           IF CRA0201-CD-ESTADO = "RJ"
               MOVE "21" TO WT-AUX-AREA
           ELSE
               MOVE "31" TO WT-AUX-AREA
           END-IF

E agora estamos prontos para mover os telefones para as variáveis editadas que criamos na WORKING: WT-TELEFONE-FIXO e WT-TELEFONE-MOVEL. Faremos isso testando o tamanho que calculamos anteriormente.

      * PREENCHE O NUMERO DE TELEFONE EDITADO DE ACORDO COM O TAMANHO
      * DO NUMERO RESULTANTE DA CONCATENACAO.
      *
      * SE TAMANHO FOR 8
      * SIGNIFICA QUE E' UM TELEFONE FIXO SEM CODIGO DE AREA
      *
      * SE TAMANHO FOR 10
      * SIGNIFICA QUE E' UM TELEFONE FIXO COM CODIGO DE AREA
      *
      * SE TAMANHO FOR 9
      * SIGNIFICA QUE E' UM TELEFONE MOVEL SEM CODIGO DE AREA
      *
      * SE TAMANHO FOR 11
      * SIGNIFICA QUE E' UM TELEFONE MOVEL COM CODIGO DE AREA

           EVALUATE WT-AUX-TAMANHO
               WHEN 8
                   MOVE WT-AUX-AREA TO WT-FIXO-AREA
                   MOVE WT-AUX-TELEFONE(1:4) TO WT-FIXO-PREFIXO
                   MOVE WT-AUX-TELEFONE(5:4) TO WT-FIXO-MILHAR
                   MOVE WT-TELEFONE-FIXO TO CRA0201-NR-TELEFONE-1
               WHEN 10
                   MOVE WT-AUX-AREA TO WT-FIXO-AREA
                   MOVE WT-AUX-TELEFONE(3:4) TO WT-FIXO-PREFIXO
                   MOVE WT-AUX-TELEFONE(7:4) TO WT-FIXO-MILHAR
                   MOVE WT-TELEFONE-FIXO TO CRA0201-NR-TELEFONE-1
               WHEN 9
                   MOVE WT-AUX-AREA TO WT-MOVEL-AREA
                   MOVE WT-AUX-TELEFONE(1:5) TO WT-MOVEL-PREFIXO
                   MOVE WT-AUX-TELEFONE(6:4) TO WT-MOVEL-MILHAR
                   MOVE WT-TELEFONE-MOVEL TO CRA0201-NR-TELEFONE-1
               WHEN 11
                   MOVE WT-AUX-AREA TO WT-MOVEL-AREA
                   MOVE WT-AUX-TELEFONE(3:5) TO WT-MOVEL-PREFIXO
                   MOVE WT-AUX-TELEFONE(8:4) TO WT-MOVEL-MILHAR
                   MOVE WT-TELEFONE-MOVEL TO CRA0201-NR-TELEFONE-1
               WHEN OTHER
                   MOVE SPACES TO CRA0201-NR-TELEFONE-1
           END-EVALUATE

Observe que estamos fazendo as movimentações usando modificadores de referência, pois eles nos permitem acessar substrings de uma string maior. Isso é necessário porque os componentes que queremos estão em posições diferentes, dependendo de como o telefone foi preenchido no passado. Por exemplo, e o tamanho do telefone for 8 (telefone fixo sem código de área), o prefixo estará nas posições 1 a 4 e a milhar nas posições 5 a 8. Já se o tamanho for 10 (telefone fixo com código de área), o prefixo estará nas posições 3 a 6 e a milhar nas posições 7 a 10. As variáveis editadas, depois de preenchidas, são mostradas na figura abaixo:

Cobol: Variáveis auxiliares editadas
Figura 66. Variáveis auxiliares editadas após execução das movimentações

As variáveis editadas foram movidas para o campo do arquivo no próprio EVALUATE. Agora podemos regravar o registro e ler o próximo a ser processado:

      * MOVE NUMERO EDITADO PARA REGISTRO E ATUALIZA ARQUIVO

           REWRITE CRA0201-REGISTRO

           READ CRA0201 NEXT.

Obviamente, poderíamos ter codificado esse programa sem nenhum comando especial para manipulação de strings. Com um monte de itens elementares, PERFORMs VARYING e IFs teríamos obtido os mesmos resultados. Ao usar UNSTRING, STRING, INSPECT e substrings, porém, conseguimos um programa menor e mais fácil de entender.

O programa completo você pode conferir abaixo:

*================================================================*
IDENTIFICATION DIVISION.
*----------------------------------------------------------------*
PROGRAM-ID.    CRP03021.
AUTHOR.        PAULO ANDRE DIAS.
DATE-WRITTEN.  25/01/2017.
REMARKS.
*----------------------------------------------------------------*
* SISTEMA:      CR - CONTAS A RECEBER
* JOB:          03 - ATUALIZACAO CADASTRAL
* PROGRAMA:     02 - CORRIGE EDICAO DE NUMERO DE TELEFONE
*
* OBJETIVO:     PADRONIZAR A EDICAO DOS NUMEROS DE TELEFONE RE-
*               GISTRADOS NO CADASTRO DE CLIENTES
*
* VERSOES:      DATA    DESCRICAO
*               ------  ---------------------------------------
*               XXXXXX  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*
*----------------------------------------------------------------*

*================================================================*
ENVIRONMENT DIVISION.
*----------------------------------------------------------------*
CONFIGURATION SECTION.
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.

INPUT-OUTPUT SECTION.
FILE-CONTROL.

* CADASTRO DE CLIENTES
COPY CRS0201.

*================================================================*
DATA DIVISION.
*----------------------------------------------------------------*
FILE SECTION.

* CADASTRO DE CLIENTES
COPY CRF0201.

*================================================================*
WORKING-STORAGE SECTION.
*----------------------------------------------------------------*
01 WT-FILE-STATUS.
03 WT-ST-CRA0201            PIC  X(002) VALUE SPACES.

01 WT-TELEFONE-FIXO.
03 FILLER                   PIC  X(001) VALUE "(".
03 WT-FIXO-AREA             PIC  X(002) VALUE SPACES.
03 FILLER                   PIC  X(002) VALUE ")".
03 WT-FIXO-PREFIXO          PIC  X(004) VALUE SPACES.
03 FILLER                   PIC  X(001) VALUE "-".
03 WT-FIXO-MILHAR           PIC  X(004) VALUE SPACES.

01 WT-TELEFONE-MOVEL.
03 FILLER                   PIC  X(001) VALUE "(".
03 WT-MOVEL-AREA            PIC  X(002) VALUE SPACES.
03 FILLER                   PIC  X(002) VALUE ")".
03 WT-MOVEL-PREFIXO         PIC  X(005) VALUE SPACES.
03 FILLER                   PIC  X(001) VALUE "-".
03 WT-MOVEL-MILHAR          PIC  X(004) VALUE SPACES.

01 WT-AUXILIARES.
03 WT-AUX-TAMANHO           PIC  9(002) VALUE ZEROS.
03 WT-AUX-PARTE-1           PIC  X(015) VALUE SPACES.
03 WT-AUX-PARTE-2           PIC  X(015) VALUE SPACES.
03 WT-AUX-PARTE-3           PIC  X(015) VALUE SPACES.
03 WT-AUX-PARTE-4           PIC  X(015) VALUE SPACES.
03 WT-AUX-PARTE-5           PIC  X(015) VALUE SPACES.
03 WT-AUX-TELEFONE          PIC  X(015) VALUE SPACES.
03 WT-AUX-AREA              PIC  X(002) VALUE SPACES.

*================================================================*
PROCEDURE DIVISION.
*----------------------------------------------------------------*
0-PRINCIPAL.

PERFORM 1-INICIA
PERFORM 2-PROCESSA
UNTIL WT-ST-CRA0201 NOT = "00"
PERFORM 3-TERMINA

STOP RUN.

*----------------------------------------------------------------*
* ABRE ARQUIVOS E LE PRIMEIRO REGISTRO DO ARQUIVO DE ENTRADA
*----------------------------------------------------------------*
1-INICIA.

OPEN I-O CRA0201

READ CRA0201 NEXT.

*----------------------------------------------------------------*
* ELIMINA TODOS OS CARACTERES NAO NUMERICOS, QUEBRA A VARIAVEL
* EM COMPONENTES NUMERICOS, CONCATENA ESSES COMPONENTES, APLICA
* UMAS MASCARA DE EDICAO AO NUMERO CONCATENADO E ATUALIZA O RE-
* GISTRO COM O NUMERO EDITADO
*----------------------------------------------------------------*
2-PROCESSA.

* ELIMINA CARACTERES NAO NUMERICOS

INSPECT CRA0201-NR-TELEFONE-1
REPLACING ALL "(" BY SPACE
ALL ")" BY SPACE
ALL "-" BY SPACE
ALL "." BY SPACE

* QUEBRA NUMERO DE TELEFONE EM COMPONENTES NUMERICOS

MOVE SPACES TO WT-AUX-PARTE-1
MOVE SPACES TO WT-AUX-PARTE-2
MOVE SPACES TO WT-AUX-PARTE-3
MOVE SPACES TO WT-AUX-PARTE-4
MOVE SPACES TO WT-AUX-PARTE-5

UNSTRING CRA0201-NR-TELEFONE-1
DELIMITED BY ALL SPACES
INTO WT-AUX-PARTE-1
WT-AUX-PARTE-2
WT-AUX-PARTE-3
WT-AUX-PARTE-4
WT-AUX-PARTE-5

* REAGRUPA COMPONENTES DO TELEFONE

MOVE SPACES TO WT-AUX-TELEFONE

STRING WT-AUX-PARTE-1
WT-AUX-PARTE-2
WT-AUX-PARTE-3
WT-AUX-PARTE-4
WT-AUX-PARTE-5
DELIMITED BY SPACE
INTO WT-AUX-TELEFONE

* OBTEM O TAMANHO DO TELEFONE APOS A CONCATENACAO

MOVE ZEROS TO WT-AUX-TAMANHO

INSPECT WT-AUX-TELEFONE
TALLYING WT-AUX-TAMANHO
FOR CHARACTERS
BEFORE SPACE

* PREENCHE O CODIGO DE AREA EM FUNCAO DO CAMPO ESTADO

IF CRA0201-CD-ESTADO = "RJ"
MOVE "21" TO WT-AUX-AREA
ELSE
MOVE "31" TO WT-AUX-AREA
END-IF

* PREENCHE O NUMERO DE TELEFONE EDITADO DE ACORDO COM O TAMANHO
* DO NUMERO RESULTANTE DA CONCATENACAO.
*
* SE TAMANHO FOR 8
* SIGNIFICA QUE E' UM TELEFONE FIXO SEM CODIGO DE AREA
*
* SE TAMANHO FOR 10
* SIGNIFICA QUE E' UM TELEFONE FIXO COM CODIGO DE AREA
*
* SE TAMANHO FOR 9
* SIGNIFICA QUE E' UM TELEFONE MOVEL SEM CODIGO DE AREA
*
* SE TAMANHO FOR 11
* SIGNIFICA QUE E' UM TELEFONE MOVEL COM CODIGO DE AREA

EVALUATE WT-AUX-TAMANHO
WHEN 8
MOVE WT-AUX-AREA TO WT-FIXO-AREA
MOVE WT-AUX-TELEFONE(1:4) TO WT-FIXO-PREFIXO
MOVE WT-AUX-TELEFONE(5:4) TO WT-FIXO-MILHAR
MOVE WT-TELEFONE-FIXO TO CRA0201-NR-TELEFONE-1
WHEN 10
MOVE WT-AUX-AREA TO WT-FIXO-AREA
MOVE WT-AUX-TELEFONE(3:4) TO WT-FIXO-PREFIXO
MOVE WT-AUX-TELEFONE(7:4) TO WT-FIXO-MILHAR
MOVE WT-TELEFONE-FIXO TO CRA0201-NR-TELEFONE-1
WHEN 9
MOVE WT-AUX-AREA TO WT-MOVEL-AREA
MOVE WT-AUX-TELEFONE(1:5) TO WT-MOVEL-PREFIXO
MOVE WT-AUX-TELEFONE(6:4) TO WT-MOVEL-MILHAR
MOVE WT-TELEFONE-MOVEL TO CRA0201-NR-TELEFONE-1
WHEN 11
MOVE WT-AUX-AREA TO WT-MOVEL-AREA
MOVE WT-AUX-TELEFONE(3:5) TO WT-MOVEL-PREFIXO
MOVE WT-AUX-TELEFONE(8:4) TO WT-MOVEL-MILHAR
MOVE WT-TELEFONE-MOVEL TO CRA0201-NR-TELEFONE-1
WHEN OTHER
MOVE SPACES TO CRA0201-NR-TELEFONE-1
END-EVALUATE

* MOVE NUMERO EDITADO PARA REGISTRO E ATUALIZA ARQUIVO

REWRITE CRA0201-REGISTRO

READ CRA0201 NEXT.

*----------------------------------------------------------------*
* ENCERRAMENTO DO PROGRAMA
*----------------------------------------------------------------*
3-TERMINA.

CLOSE CRA0201.

Anterior Conteúdo Próxima