1. Reference Modification — substring direto
A forma mais simples de acessar parte de um campo alfanumérico é a Reference Modification: CAMPO(início:tamanho). Não exige nenhum verbo extra — funciona diretamente em qualquer contexto onde o campo seria usado:
WORKING-STORAGE SECTION. 01 WS-DATA-ISO PIC X(08) VALUE '20260615'. 01 WS-ANO PIC X(04). 01 WS-MES X(02). 01 WS-DIA X(02). 01 WS-DATA-BR X(10). PROCEDURE DIVISION. * Sintaxe: CAMPO(posição-inicial : comprimento) * Posição começa em 1, não em 0 MOVE WS-DATA-ISO(1:4) TO WS-ANO *'2026' MOVE WS-DATA-ISO(5:2) TO WS-MES *'06' MOVE WS-DATA-ISO(7:2) TO WS-DIA *'15' * Montando data no formato DD/MM/AAAA MOVE WS-DIA TO WS-DATA-BR(1:2) MOVE '/' TO WS-DATA-BR(3:1) MOVE WS-MES TO WS-DATA-BR(4:2) MOVE '/' TO WS-DATA-BR(6:1) MOVE WS-ANO TO WS-DATA-BR(7:4) * WS-DATA-BR = '15/06/2026' * Comprimento pode ser variável (campo numérico) 01 WS-TAM PIC 9(02) VALUE 4. MOVE WS-DATA-ISO(1:WS-TAM) TO WS-ANO * Omitir comprimento = até o final do campo MOVE WS-DATA-ISO(5:) TO WS-MES *'0615' — 4 bytes
💗 Para iniciantes — quando usar Reference Modification vs MOVE
Use Reference Modification quando quiser extrair ou substituir parte de um campo sem declarar campos auxiliares para cada fatia. É especialmente útil para converter formatos de data, extrair dígitos de código de barras, ou copiar colunas de um registro de layout fixo. Para copiar o campo inteiro, MOVE simples é sempre mais claro.
2. STRING — concatenando campos
O verbo STRING concatena múltiplos campos em um único destino, copiando cada um até encontrar um delimitador ou usando o tamanho completo (SIZE). O ponteiro (POINTER) controla em qual posição do destino a escrita começa:
WORKING-STORAGE SECTION. 01 WS-NOME PIC X(20) VALUE 'MARIA SILVA '. 01 WS-CPF PIC X(11) VALUE '12345678901'. 01 WS-AGENCIA PIC X(04) VALUE '0001'. 01 WS-MSG PIC X(60). 01 WS-PTR PIC 9(02). PROCEDURE DIVISION. * DELIMITED SPACE: copia até o primeiro espaço * DELIMITED SIZE: copia o campo inteiro MOVE SPACES TO WS-MSG STRING WS-NOME DELIMITED SPACE ' CPF:' DELIMITED SIZE WS-CPF DELIMITED SIZE ' AG:' DELIMITED SIZE WS-AGENCIA DELIMITED SIZE INTO WS-MSG END-STRING * WS-MSG = 'MARIA SILVA CPF:12345678901 AG:0001' * POINTER: controla posição de escrita no destino MOVE SPACES TO WS-MSG MOVE 1 TO WS-PTR STRING 'ERRO-' DELIMITED SIZE INTO WS-MSG WITH POINTER WS-PTR END-STRING * WS-PTR agora aponta para posição 7 (próximo caractere livre) STRING WS-AGENCIA DELIMITED SIZE INTO WS-MSG WITH POINTER WS-PTR END-STRING * WS-MSG = 'ERRO-0001' * ON OVERFLOW: destino ficou pequeno para o resultado STRING WS-NOME DELIMITED SPACE WS-CPF DELIMITED SIZE INTO WS-MSG ON OVERFLOW MOVE 'S' TO WS-OVERFLOW END-STRING
✅ DELIMITED SPACE vs DELIMITED SIZE
DELIMITED SPACE copia apenas até o primeiro espaço em branco — perfeito para nomes e descrições que têm espaços de preenchimento à direita. DELIMITED SIZE copia o campo inteiro, incluindo os espaços — use para campos que não têm preenchimento ou quando você precisa do conteúdo completo. Você pode misturar os dois no mesmo STRING, cada campo com seu próprio delimitador.
3. UNSTRING — desmembrando campos
O UNSTRING faz o caminho inverso: divide um campo em partes usando um ou mais delimitadores. É o equivalente COBOL de um split:
WORKING-STORAGE SECTION. 01 WS-LINHA PIC X(60) VALUE 'JOAO;12345678901;0001;C;1500.00'. 01 WS-NOME PIC X(20). 01 WS-CPF PIC X(11). 01 WS-AG PIC X(04). 01 WS-TIPO PIC X(01). 01 WS-VALOR PIC X(10). PROCEDURE DIVISION. * Divide WS-LINHA em campos usando ';' como separador UNSTRING WS-LINHA DELIMITED BY ';' INTO WS-NOME WS-CPF WS-AG WS-TIPO WS-VALOR END-UNSTRING * WS-NOME = 'JOAO' * WS-CPF = '12345678901' * WS-AG = '0001' * WS-TIPO = 'C' * WS-VALOR = '1500.00'
WORKING-STORAGE SECTION. 01 WS-ENTRADA PIC X(80). 01 WS-PART1 PIC X(30). 01 WS-PART2 PIC X(30). 01 WS-TAM1 PIC 9(02). *tamanho real da parte 1 01 WS-TAM2 PIC 9(02). *tamanho real da parte 2 01 WS-PTR PIC 9(02). *ponteiro de posição 01 WS-QTD-SEG PIC 9(02). *quantos segmentos foram extraídos 01 WS-DELIM PIC X(01). *qual delimitador foi encontrado PROCEDURE DIVISION. * Múltiplos delimitadores: ';' ou ',' MOVE 1 TO WS-PTR MOVE 0 TO WS-QTD-SEG UNSTRING WS-ENTRADA DELIMITED BY ';' OR ',' INTO WS-PART1 DELIMITER IN WS-DELIM COUNT IN WS-TAM1 WS-PART2 COUNT IN WS-TAM2 WITH POINTER WS-PTR TALLYING IN WS-QTD-SEG ON OVERFLOW MOVE 'S' TO WS-OVERFLOW END-UNSTRING * COUNT IN: quantos bytes foram copiados para aquele campo * DELIMITER IN: qual delimitador encerrou o campo * TALLYING IN: total de campos extraídos até agora * POINTER: posição onde parou (permite continuar depois)
🦕 STRING e UNSTRING são inversos
Pense em STRING como colar pedaços de papel em uma tira e UNSTRING como recortar a tira nos espaços marcados. Um arquivo CSV recebido de sistema externo chega como linha única — você usa UNSTRING para separar. Ao montar a resposta para enviar de volta, usa STRING para montar a linha novamente.
4. INSPECT TALLYING — contando caracteres
O INSPECT TALLYING percorre um campo e conta quantas vezes um caractere ou string aparece:
WORKING-STORAGE SECTION. 01 WS-CAMPO PIC X(20) VALUE 'ABRACADABRA'. 01 WS-CNT PIC 9(04) VALUE ZEROS. 01 WS-CNT-ESP PIC 9(04) VALUE ZEROS. PROCEDURE DIVISION. * Conta quantas vezes 'A' aparece MOVE 0 TO WS-CNT INSPECT WS-CAMPO TALLYING WS-CNT FOR ALL 'A' * WS-CNT = 5 * Conta espaços iniciais (leading spaces) MOVE 0 TO WS-CNT-ESP INSPECT WS-CAMPO TALLYING WS-CNT-ESP FOR LEADING SPACES * Conta apenas antes de encontrar 'B' MOVE 0 TO WS-CNT INSPECT WS-CAMPO TALLYING WS-CNT FOR CHARACTERS BEFORE 'B' * WS-CNT = 1 (apenas 'A' antes do primeiro 'B') * Contagem múltipla no mesmo INSPECT MOVE 0 TO WS-CNT WS-CNT-ESP INSPECT WS-CAMPO TALLYING WS-CNT FOR ALL 'A' WS-CNT-ESP FOR ALL 'B'
| Cláusula | O que conta |
|---|---|
FOR ALL 'X' | Todas as ocorrências de 'X' no campo |
FOR LEADING 'X' | Ocorrências de 'X' apenas no início (até encontrar outro) |
FOR CHARACTERS | Total de caracteres (equivale ao tamanho do campo) |
BEFORE 'Y' | Conta somente até encontrar 'Y' |
AFTER 'Y' | Começa a contar somente após encontrar 'Y' |
5. INSPECT REPLACING — substituindo caracteres
O INSPECT REPLACING substitui caracteres ou strings dentro de um campo, no lugar (in-place):
WORKING-STORAGE SECTION. 01 WS-CPF PIC X(14) VALUE '123.456.789-09'. 01 WS-TEXTO PIC X(30) VALUE '0001200034005600'. 01 WS-NOME PIC X(20) VALUE ' JOAO SILVA '. PROCEDURE DIVISION. * Remove pontuação do CPF: troca '.' e '-' por SPACE INSPECT WS-CPF REPLACING ALL '.' BY SPACE INSPECT WS-CPF REPLACING ALL '-' BY SPACE * WS-CPF = '123 456 789 09' * Substitui zeros à esquerda por espaços INSPECT WS-TEXTO REPLACING LEADING '0' BY SPACE * WS-TEXTO = ' 1200034005600' * Substitui apenas os primeiros espaços (trim esquerdo) * e para ao encontrar caractere diferente INSPECT WS-NOME REPLACING LEADING SPACES BY '*' * WS-NOME = '***JOAO SILVA ' * FIRST: substitui apenas a primeira ocorrência INSPECT WS-TEXTO REPLACING FIRST '00' BY 'XX' * Combinando TALLYING e REPLACING no mesmo INSPECT INSPECT WS-TEXTO TALLYING WS-CNT FOR ALL '0' REPLACING ALL '0' BY '9'
6. INSPECT CONVERTING — conversão em massa
O INSPECT CONVERTING é um atalho para substituir um conjunto de caracteres por outro conjunto — posição a posição. É o equivalente ao tr do Unix:
WORKING-STORAGE SECTION. 01 WS-TEXTO PIC X(30) VALUE 'maria silva'. 01 WS-CAMPO PIC X(10) VALUE 'a1b2c3d4e5'. PROCEDURE DIVISION. * Converte minúsculas para maiúsculas * Cada char na 1ª lista é substituído pelo char na mesma posição da 2ª INSPECT WS-TEXTO CONVERTING 'abcdefghijklmnopqrstuvwxyz' TO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' * WS-TEXTO = 'MARIA SILVA' * Substitui letras minúsculas por '@' e dígitos por '#' INSPECT WS-CAMPO CONVERTING 'abcde0123456789' TO '@@@@@###########' * WS-CAMPO = '@#@#@#@#@#'
✅ CONVERTING é muito útil para normalização
Em produção é comum receber arquivos de sistemas externos com codificação mista — minúsculas que deveriam ser maiúsculas, acentos que não foram convertidos de EBCDIC corretamente, separadores decimais com vírgula em vez de ponto. INSPECT CONVERTING resolve isso em uma única instrução, sem precisar de loop ou campo auxiliar.
7. Exemplo completo
Um sub-programa que recebe uma linha CSV com dados de cliente, desmembra os campos, normaliza o nome para maiúsculas e formata o CPF para exibição:
IDENTIFICATION DIVISION. PROGRAM-ID. PARSELIN. * Entrada: ' joao silva ;12345678901;0001;C' * Saída: nome=JOAO SILVA, cpf=123.456.789-01, ag=0001, tipo=C DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-LIXO PIC 9(02). LINKAGE SECTION. 01 LK-LINHA PIC X(80). 01 LK-SAIDA. 05 LK-NOME PIC X(30). 05 LK-CPF-FMT PIC X(14). *999.999.999-99 05 LK-AGENCIA PIC X(04). 05 LK-TIPO PIC X(01). 05 LK-RETORNO PIC X(02). 88 PARSE-OK VALUE '00'. 88 PARSE-ERRO VALUE '01'. 01 LK-CPF-BRUTO PIC X(11). *CPF sem formatação PROCEDURE DIVISION USING LK-LINHA LK-SAIDA LK-CPF-BRUTO. 0000-INICIO. PERFORM 1000-DESMEMBRA PERFORM 2000-NORMALIZA-NOME PERFORM 3000-FORMATA-CPF GOBACK. 1000-DESMEMBRA. * Separa os campos da linha CSV INITIALIZE LK-SAIDA UNSTRING LK-LINHA DELIMITED BY ';' INTO LK-NOME LK-CPF-BRUTO LK-AGENCIA LK-TIPO TALLYING IN WS-LIXO ON OVERFLOW SET PARSE-ERRO TO TRUE GOBACK END-UNSTRING SET PARSE-OK TO TRUE. 2000-NORMALIZA-NOME. * Remove espaços iniciais trocando por '@' e depois limpando * e converte para maiúsculas INSPECT LK-NOME CONVERTING 'abcdefghijklmnopqrstuvwxyz' TO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. 3000-FORMATA-CPF. * LK-CPF-BRUTO = '12345678901' * Monta '123.456.789-01' via STRING com Reference Modification MOVE SPACES TO LK-CPF-FMT STRING LK-CPF-BRUTO(1:3) DELIMITED SIZE '.' DELIMITED SIZE LK-CPF-BRUTO(4:3) DELIMITED SIZE '.' DELIMITED SIZE LK-CPF-BRUTO(7:3) DELIMITED SIZE '-' DELIMITED SIZE LK-CPF-BRUTO(10:2) DELIMITED SIZE INTO LK-CPF-FMT END-STRING. * LK-CPF-FMT = '123.456.789-01'
8. Erros comuns
1. Não inicializar o POINTER antes do STRING
* ERRADO: WS-PTR tem valor residual de execução anterior STRING WS-CAMPO-A DELIMITED SPACE WS-CAMPO-B DELIMITED SPACE INTO WS-RESULTADO WITH POINTER WS-PTR *WS-PTR = 47 da vez anterior! END-STRING * CORRETO: reinicializa antes de cada STRING com POINTER MOVE 1 TO WS-PTR MOVE SPACES TO WS-RESULTADO STRING WS-CAMPO-A DELIMITED SPACE WS-CAMPO-B DELIMITED SPACE INTO WS-RESULTADO WITH POINTER WS-PTR END-STRING
2. TALLYING não zera automaticamente
* ERRADO: WS-CNT acumula entre chamadas INSPECT WS-CAMPO1 TALLYING WS-CNT FOR ALL 'A' * WS-CNT = 5 INSPECT WS-CAMPO2 TALLYING WS-CNT FOR ALL 'A' * WS-CNT = 5 + 3 = 8 (não é 3!) * CORRETO: zera antes de cada contagem independente MOVE 0 TO WS-CNT INSPECT WS-CAMPO2 TALLYING WS-CNT FOR ALL 'A' * WS-CNT = 3
3. Reference Modification com posição calculada fora dos limites
* Se WS-POS = 0 ou > tamanho do campo → abend S0C5 MOVE WS-CAMPO(WS-POS:1) TO WS-CHAR *ARRISCADO * CORRETO: valide antes IF WS-POS >= 1 AND WS-POS <= LENGTH OF WS-CAMPO MOVE WS-CAMPO(WS-POS:1) TO WS-CHAR END-IF * LENGTH OF retorna o tamanho declarado do campo em bytes
4. UNSTRING sem limpar os campos de destino
* Se a linha tem só 3 campos e o UNSTRING espera 5, * os campos 4 e 5 ficam com o conteúdo anterior UNSTRING WS-LINHA-CURTA DELIMITED BY ';' INTO WS-NOME WS-CPF WS-AG WS-TIPO WS-VALOR *esses dois ficam com lixo END-UNSTRING * CORRETO: limpe os destinos antes do UNSTRING INITIALIZE WS-NOME WS-CPF WS-AG WS-TIPO WS-VALOR UNSTRING WS-LINHA-CURTA DELIMITED BY ';' INTO WS-NOME WS-CPF WS-AG WS-TIPO WS-VALOR END-UNSTRING
🟣 Para quem já programa — FUNCTION TRIM e intrínsecas
O Enterprise COBOL (IBM) e outros compiladores modernos suportam funções intrínsecas para manipulação de texto: FUNCTION TRIM(WS-CAMPO LEADING) remove espaços à esquerda, FUNCTION UPPER-CASE(WS-TEXTO) converte para maiúsculas sem INSPECT CONVERTING, FUNCTION LENGTH(WS-CAMPO) retorna o tamanho. Em ambientes que suportam essas funções, elas são mais legíveis do que as combinações de INSPECT. Verifique a versão do compilador antes de adotá-las em produção.