Saltar para o conteúdo
Topos Uranos

Resumo

Esta aula encerra a sétima unidade do curso, dedicada ao texto e aos arquivos. Reescreve com o que foi aprendido o programa da introdução, que perdia dados sem avisar e media mal os nomes com acentos; reconstrói a cadeia de conceitos que vai da string ao formato de um arquivo; separa o que disso vale em qualquer linguagem do que é próprio de C++; reúne o comportamento indefinido da unidade, com o que o detector detém e o que ninguém vê; repete na sua avaliação de saída as habilidades da avaliação de entrada; e termina com seis desafios que combinam a unidade inteira.

Objetivos de aprendizagem

  1. Processar texto real com a sua codificação: prever o tamanho em bytes de uma string, contar as suas letras e evitar as operações que quebram UTF-8.
  2. Ler entradas e arquivos de maneira robusta, distinguindo o dado inválido do fim dos dados e validando cada dado como texto completo.
  3. Produzir arquivos de resultados com um formato especificado, informando cada linha inválida com o seu número.
  4. Reconhecer o comportamento indefinido da unidade (a visão pendente, o índice fora de uma string) e distingui-lo das falhas definidas que também passam em silêncio.
  5. Separar, no que foi aprendido na unidade, o que vale em qualquer linguagem do que é próprio de C++.

O que a unidade alcançou

A unidade propôs-se ensinar a lidar com o texto como dado e a fazer com que um programa leia e escreva arquivos sem quebrar diante de uma entrada malformada. O propósito cumpriu-se apoiando-se apenas no que a introdução declarou como sabido: o caractere como código da unidade 2, os bits da unidade 3, os vetores e os registros da unidade 6 e as funções com contrato da unidade 5. O programa da lista de notas, que lia com >>, perdia Ana Pérez e Luis e declarava “Íñigo” mais longo que “Carmen”, escreve-se agora assim: lê as linhas de um arquivo, separa a nota pelo último espaço, valida-a com a função parseInt da aula Ler bem, informa a linha inválida pelo fluxo de erros, mede os nomes em caracteres e deixa a lista em outro arquivo. Para que o exemplo seja autônomo, o programa escreve primeiro o arquivo de dados, que na prática já existiria.

Programa em C++
#include <charconv>#include <cstddef>#include <fstream>#include <iostream>#include <print>#include <string>#include <string_view>​// Julga se um texto é exatamente um inteiro.// Pré-condição: nenhuma.// Pós-condição: a da aula «Ler bem».bool parseInt(std::string_view text, int& value){    int result{};    const auto last{text.data() + text.size()};    const auto [stop, error]{std::from_chars(text.data(), last, result)};    if (error != std::errc{} || stop != last) {        return false;    }    value = result;    return true;}​// Número de caracteres de um texto UTF-8.// Pré-condição: text é UTF-8 válido.// Pós-condição: devolve o número de caracteres (não de bytes) de text.std::size_t letterCount(std::string_view text){    std::size_t count{0};    for (const char c : text) {        if ((c & 0xC0) != 0x80) {            ++count;        }    }    return count;}​int main(){    {        std::ofstream out{"grades.txt"};        std::println(out, "Carmen 65\nÍñigo 70\nAna Pérez 58\nLuis seis\nTomás 61");    }    std::ifstream in{"grades.txt"};    std::ofstream report{"report.txt"};    if (!in || !report) {        std::println(std::cerr, "Não foi possível abrir os arquivos");        return 1;    }    std::string line{};    std::string longest{};    int lineNumber{0};    while (std::getline(in, line)) {        ++lineNumber;        const std::size_t space{line.rfind(' ')};        int grade{};        if (space == std::string::npos || !parseInt(std::string_view{line}.substr(space + 1), grade)) {            std::println(std::cerr, "Linha {} inválida: «{}»", lineNumber, line);            continue;        }        const std::string name{line.substr(0, space)};        std::println(report, "{:<10}{:>3}", name, grade);        if (letterCount(name) > letterCount(longest)) {            longest = name;        }    }    std::println("Nome mais longo: {} ({} caracteres)", longest, letterCount(longest));}
Saída
Linha 4 inválida: «Luis seis»Nome mais longo: Ana Pérez (9 caracteres)

A tela mostra apenas a mensagem de erro e a conclusão; a lista está no arquivo, que o comando cat (no PowerShell, Get-Content) mostra depois de executar o programa:

Terminal
$ cat report.txtCarmen     65Íñigo      70Ana Pérez  58Tomás      61

Cada parte responde a um defeito da introdução. A leitura por linhas e rfind permitem nomes com espaços; npos e parseInt rejeitam a linha de Luis, informam-na com o seu número e deixam Tomás seguir; letterCount conta caracteres, e “Ana Pérez”, com nove caracteres e dez bytes, ganha com justiça; e report.txt fica completo ao terminar main, quando o seu fluxo se fecha.

Cadeias de caracteres

A aula Cadeias de caracteres apresentou std::string como uma sequência de char com operações de texto. Ensinou a buscar com find, que devolve uma posição ou npos, a comparar sempre esse valor de maneira explícita, e a extrair com substr, que exige uma posição válida; apresentou std::string_view, segura como parâmetro e perigosa se sobreviver ao texto que observa, e as strings de C, como contraste.

O texto não é ASCII

A aula O texto não é ASCII separou o caractere, o seu ponto de código e os seus bytes, e explicou como UTF-8 reparte os bits entre um e quatro bytes. Disso deduziu que size conta bytes, que os caracteres se contam com (c & 0xC0) != 0x80, que inverter, passar para maiúsculas ou cortar byte a byte quebra o texto, que buscar e concatenar não o quebram, e o que a biblioteca padrão não faz.

Ler bem

A aula Ler bem tratou a entrada como fronteira não confiável. Ensinou a ler linhas com std::getline, a distinguir com fail() e eof() o dado inválido, o número que não cabe e o fim dos dados, a recuperar-se com clear e ignore, e a validar um número como texto com std::from_chars, exigindo que seja consumido inteiro; fechou com parseInt e readInt, funções de fronteira com contrato.

Arquivos de texto

A aula Arquivos de texto mostrou que um arquivo é um fluxo, com o estado de std::cin. Fez da verificação da abertura uma regra; demonstrou por que while (!in.eof()) processa um dado a mais; ensinou a escrever com std::println(out, …), substituindo ou acrescentando, e a separar mensagens e resultados com std::cerr; e levou tudo a um formato de linha especificado e validado.