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:

COBOL Reference Modification
       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:

COBOL STRING — forma básica
       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:

COBOL UNSTRING — forma básica
       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'
COBOL UNSTRING avançado — POINTER, COUNT, TALLYING
       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:

COBOL INSPECT TALLYING
       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áusulaO 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 CHARACTERSTotal 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):

COBOL INSPECT REPLACING
       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:

COBOL INSPECT CONVERTING
       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:

COBOL Sub-programa PARSELIN — parse e normalização de linha CSV
       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

COBOL POINTER precisa ser inicializado
             * 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

COBOL Zerar contador antes do INSPECT TALLYING
             * 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

COBOL Validar posição antes de Reference Modification
             * 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

COBOL Campos de destino do UNSTRING podem ter lixo
             * 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.