Pular para conteúdo

Lista 07 · Arquivos

Exercícios da Semana 7 — Lendo e escrevendo dados. Estes exercícios testam a lógica de leitura/escrita usando std::istringstream/std::ostringstream (que simulam um arquivo em memória) em vez de arquivo de disco — assim funcionam em qualquer compilador online. O std::ifstream/std::ofstream de verdade fica só na demonstração do capítulo.

Como resolver

Copie o bloco de código de cada exercício, cole no seu compilador online, preencha a parte marcada como ESCREVA SUA SOLUCAO AQUI e rode. A saída mostra quais testes passaram.

ex01

Escreva parseia_linha, que recebe uma linha no formato "nome,idade" e devolve um std::pair<std::string, int> com o nome e a idade já convertida para int. Use std::istringstream e std::getline com delimitador ','.

parseia_linha("Ana,25")   // -> {"Ana", 25}
parseia_linha("Bruno,30") // -> {"Bruno", 30}
// ex01 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <sstream>
#include <utility>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX01_H
#define EX01_H

// Recebe uma linha "nome,idade" e retorna {nome, idade} (idade ja convertida para int).
std::pair<std::string, int> parseia_linha(const std::string& linha);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::pair<std::string, int> parseia_linha(const std::string& linha) {
    // TODO: use std::istringstream(linha), std::getline(..., ',') para o nome,
    // depois std::getline no resto e std::stoi para converter a idade.
    return {"", 0};
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    auto r1 = parseia_linha("Ana,25");
    assert(r1.first == std::string("Ana"));
    assert(r1.second == 25);

    auto r2 = parseia_linha("Bruno,30");
    assert(r2.first == std::string("Bruno"));
    assert(r2.second == 30);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex02

Escreva conta_linhas, que recebe um conteúdo de "arquivo" (várias linhas separadas por \n, como std::string) e devolve quantas linhas não vazias ele tem.

conta_linhas("Ana,25\nBruno,30\nCarla,22")  // -> 3
conta_linhas("")                             // -> 0
conta_linhas("uma linha so")                 // -> 1
// ex02 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX02_H
#define EX02_H

// Retorna quantas linhas nao vazias existem em "conteudo".
int conta_linhas(const std::string& conteudo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
int conta_linhas(const std::string& conteudo) {
    // TODO: use std::istringstream(conteudo) e um while(std::getline(...))
    // contando as linhas que nao estao vazias (linha.empty()).
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(conta_linhas("Ana,25\nBruno,30\nCarla,22") == 3);
    assert(conta_linhas("") == 0);
    assert(conta_linhas("uma linha so") == 1);
    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex03

Dada a struct Pessoa (campos nome e idade, já declarada), escreva parseia_pessoas_validas, que recebe um conteúdo com várias linhas "nome,idade" e devolve um std::vector<Pessoa> — descartando silenciosamente as linhas malformadas, na ordem em que as boas aparecem.

Arquivo de verdade tem lixo dentro: linha em branco, linha sem vírgula, idade que não é número, nome vazio. Um programa que confia no formato quebra no primeiro arquivo real. Aqui a regra é a oposta: uma linha ruim não derruba as outras.

Uma linha é válida quando:

  • tem uma vírgula;
  • o pedaço antes da vírgula (o nome) não está vazio;
  • o pedaço depois da vírgula é composto só de dígitos, e tem pelo menos um.

Cuidado com std::stoi: ela aceita "25anos" (converte os 25 e ignora o resto) e lança exceção em "vinte", derrubando o programa inteiro. Confira que o texto da idade só tem dígitos antes de chamá-la — com um laço e c >= '0' && c <= '9', como você fez no ex04 da Lista 03.

Para saber se a vírgula existe: depois de std::getline(linha_stream, nome, ','), se não havia vírgula na linha, o nome recebe a linha inteira e o resto vem vazio. Ler o segundo pedaço e testar se ele está vazio resolve os dois casos de uma vez.

parseia_pessoas_validas("Ana,25\nlixo\nBruno,30")
// -> { {"Ana", 25}, {"Bruno", 30} }

parseia_pessoas_validas("Ana,vinte\n,40\nCarla,22")
// -> { {"Carla", 22} }
// ex03 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX03_H
#define EX03_H

struct Pessoa {
    std::string nome;
    int idade;
};

// Recebe varias linhas "nome,idade" (separadas por \n) e retorna um vector
// de Pessoa, descartando as linhas malformadas.
std::vector<Pessoa> parseia_pessoas_validas(const std::string& conteudo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::vector<Pessoa> parseia_pessoas_validas(const std::string& conteudo) {
    // TODO: percorra o conteudo linha por linha (std::getline num while).
    // Para cada linha:
    //   - separe nome e idade_texto com std::getline(..., ',') e std::getline;
    //   - descarte se o nome estiver vazio ou se idade_texto estiver vazio;
    //   - descarte se idade_texto tiver algum caractere que nao seja digito;
    //   - senao, adicione Pessoa{nome, std::stoi(idade_texto)} ao resultado.
    return {};
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    auto r1 = parseia_pessoas_validas("Ana,25\nBruno,30");
    assert(r1.size() == static_cast<std::size_t>(2));
    assert(r1[0].nome == std::string("Ana"));
    assert(r1[0].idade == 25);
    assert(r1[1].nome == std::string("Bruno"));
    assert(r1[1].idade == 30);

    // linha sem virgula no meio: as boas continuam vindo
    auto r2 = parseia_pessoas_validas("Ana,25\nlixo\nBruno,30");
    assert(r2.size() == static_cast<std::size_t>(2));
    assert(r2[0].nome == std::string("Ana"));
    assert(r2[1].nome == std::string("Bruno"));

    // idade que nao e numero (stoi lancaria excecao) e nome vazio
    auto r3 = parseia_pessoas_validas("Ana,vinte\n,40\nCarla,22");
    assert(r3.size() == static_cast<std::size_t>(1));
    assert(r3[0].nome == std::string("Carla"));
    assert(r3[0].idade == 22);

    // idade com sufixo: stoi aceitaria "25anos" como 25, e nao pode
    auto r4 = parseia_pessoas_validas("Ana,25anos\nBruno,30");
    assert(r4.size() == static_cast<std::size_t>(1));
    assert(r4[0].nome == std::string("Bruno"));

    // idade vazia depois da virgula
    auto r5 = parseia_pessoas_validas("Ana,\nBruno,30");
    assert(r5.size() == static_cast<std::size_t>(1));
    assert(r5[0].nome == std::string("Bruno"));

    // linhas em branco no meio e no fim
    auto r6 = parseia_pessoas_validas("Ana,25\n\nBruno,30\n");
    assert(r6.size() == static_cast<std::size_t>(2));

    // conteudo vazio e conteudo sem nenhuma linha valida
    assert(parseia_pessoas_validas("").empty());
    assert(parseia_pessoas_validas("lixo\nmais lixo").empty());

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex04

Escreva monta_csv, o inverso do exercício anterior: recebe um std::vector<Pessoa> e devolve o texto no formato "nome,idade" por linha, usando std::ostringstream. A última linha não deve ter \n no final.

monta_csv({{"Ana", 25}, {"Bruno", 30}})  // -> "Ana,25\nBruno,30"
monta_csv({})                             // -> ""
// ex04 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX04_H
#define EX04_H

struct Pessoa {
    std::string nome;
    int idade;
};

// Monta o texto CSV (uma linha "nome,idade" por Pessoa, sem \n no final da ultima linha).
std::string monta_csv(const std::vector<Pessoa>& pessoas);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string monta_csv(const std::vector<Pessoa>& pessoas) {
    // TODO: use um std::ostringstream, escreva "nome,idade" para cada Pessoa,
    // colocando "\n" ANTES de cada linha exceto a primeira (para nao sobrar
    // \n no final). Ao final, retorne stream.str().
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    std::vector<Pessoa> p1 = {{"Ana", 25}, {"Bruno", 30}};
    assert(monta_csv(p1) == std::string("Ana,25\nBruno,30"));

    std::vector<Pessoa> p2 = {};
    assert(monta_csv(p2) == std::string(""));

    std::vector<Pessoa> p3 = {{"Carla", 22}};
    assert(monta_csv(p3) == std::string("Carla,22"));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex05

Escreva soma_idades, que recebe um conteúdo CSV (várias linhas "nome,idade") e devolve a soma de todas as idades.

soma_idades("Ana,25\nBruno,30\nCarla,22")  // -> 77
soma_idades("")                             // -> 0
// ex05 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX05_H
#define EX05_H

// Recebe varias linhas "nome,idade" e retorna a soma de todas as idades.
int soma_idades(const std::string& conteudo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
int soma_idades(const std::string& conteudo) {
    // TODO: percorra as linhas (igual exercicio 3), some as idades de cada uma.
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(soma_idades("Ana,25\nBruno,30\nCarla,22") == 77);
    assert(soma_idades("") == 0);
    assert(soma_idades("Zeca,10") == 10);
    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex06

Escreva nome_mais_velho, que recebe um conteúdo CSV não vazio (várias linhas "nome,idade") e devolve o nome da pessoa com a maior idade.

nome_mais_velho("Ana,25\nBruno,30\nCarla,22")  // -> "Bruno"
// ex06 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX06_H
#define EX06_H

// Recebe varias linhas "nome,idade" (assume conteudo nao vazio) e retorna o
// nome da pessoa com maior idade.
std::string nome_mais_velho(const std::string& conteudo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string nome_mais_velho(const std::string& conteudo) {
    // TODO: percorra as linhas guardando o nome da maior idade vista ate agora.
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(nome_mais_velho("Ana,25\nBruno,30\nCarla,22") == std::string("Bruno"));
    assert(nome_mais_velho("Zeca,10") == std::string("Zeca"));
    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex07

Escreva frequencia_palavras, que recebe o conteúdo de um arquivo de texto (como std::string, do mesmo jeito que nos exercícios anteriores desta lista) e devolve um std::map<std::string, int> com quantas vezes cada palavra aparece.

Palavras são separadas por qualquer espaço em branco — inclusive quebras de linha. std::istringstream já trata os dois casos igual: basta ler com >> até acabar.

frequencia_palavras("um dois\num dois um")
// -> {{"dois", 2}, {"um", 3}}
// ex07 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <map>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX07_H
#define EX07_H

// Retorna quantas vezes cada palavra aparece no conteudo.
// Palavras sao separadas por espacos ou quebras de linha.
std::map<std::string, int> frequencia_palavras(const std::string& conteudo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::map<std::string, int> frequencia_palavras(const std::string& conteudo) {
    // TODO: crie um std::istringstream com o conteudo e leia palavra por
    // palavra com >>. Para cada palavra lida, some 1 no map.
    return {};
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    std::map<std::string, int> f = frequencia_palavras("um dois\num dois um");
    assert(f.size() == 2);
    assert(f["um"] == 3);
    assert(f["dois"] == 2);

    std::map<std::string, int> uma = frequencia_palavras("sozinha");
    assert(uma.size() == 1);
    assert(uma["sozinha"] == 1);

    assert(frequencia_palavras("").empty());
    assert(frequencia_palavras("   \n  ").empty());

    // Maiuscula e minuscula sao palavras diferentes.
    std::map<std::string, int> caixa = frequencia_palavras("Led led");
    assert(caixa.size() == 2);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex08

Escreva filtra_por_idade, que recebe um conteúdo CSV (várias linhas "nome,idade") e uma idade mínima, e devolve um novo conteúdo CSV contendo só as linhas em que a idade é maior ou igual à mínima, na mesma ordem.

Assim como no ex04, a última linha do resultado não deve terminar com \n. Se nenhuma linha passar no filtro, devolva uma string vazia.

filtra_por_idade("ana,20\nbruno,15\ncarla,31", 18)
// -> "ana,20\ncarla,31"
// ex08 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX08_H
#define EX08_H

// Retorna as linhas "nome,idade" do conteudo em que idade >= idade_minima,
// na ordem original e sem \n no fim da ultima linha.
std::string filtra_por_idade(const std::string& conteudo, int idade_minima);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string filtra_por_idade(const std::string& conteudo, int idade_minima) {
    // TODO: leia o conteudo linha a linha com std::getline. Em cada linha,
    // separe o que vem depois da virgula e converte com std::stoi.
    // Monte a saida so com as linhas aprovadas, cuidando para nao deixar
    // um \n sobrando no fim.
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(filtra_por_idade("ana,20\nbruno,15\ncarla,31", 18)
           == std::string("ana,20\ncarla,31"));

    // A idade minima entra: a comparacao e >=.
    assert(filtra_por_idade("ana,18", 18) == std::string("ana,18"));

    assert(filtra_por_idade("bruno,15", 18) == std::string(""));
    assert(filtra_por_idade("", 18) == std::string(""));

    assert(filtra_por_idade("ana,20\nbruno,25", 18)
           == std::string("ana,20\nbruno,25"));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex09

Escreva linha_mais_longa, que recebe o conteúdo de um arquivo (várias linhas separadas por \n) e devolve a linha mais longa. Se houver empate, devolva a primeira; se não houver nenhuma linha não vazia, devolva a string vazia.

É o padrão "guardar o melhor até agora" da Lista 05, agora aplicado a linhas em vez de números. Nada de std::max_element.

linha_mais_longa("ab\nabcd\nabc")   // -> "abcd"
linha_mais_longa("abc\nxyz")        // -> "abc"   (empate: a primeira)
// ex09 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX09_H
#define EX09_H

// Retorna a primeira linha mais longa do conteudo, ou "" se nao houver
// nenhuma linha nao vazia.
std::string linha_mais_longa(const std::string& conteudo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string linha_mais_longa(const std::string& conteudo) {
    // TODO: leia linha a linha com std::getline e guarde a mais longa
    // vista ate agora. Use > (e nao >=) para ficar com a PRIMEIRA em
    // caso de empate.
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(linha_mais_longa("ab\nabcd\nabc") == std::string("abcd"));
    assert(linha_mais_longa("uma linha so") == std::string("uma linha so"));

    // empate: fica a primeira
    assert(linha_mais_longa("abc\nxyz") == std::string("abc"));

    assert(linha_mais_longa("") == std::string(""));
    assert(linha_mais_longa("\n\n") == std::string(""));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex10

Escreva media_notas_csv, que recebe um conteúdo não vazio com várias linhas no formato "nome,nota" (a nota é double) e devolve a média das notas.

É o mesmo parsing que você faria depois de ler um arquivo, só que recebendo o conteúdo já em memória — o que torna a função testável sem depender de arquivo nenhum.

media_notas_csv("Ana,8.0\nBruno,6.0")  // -> 7.0
media_notas_csv("Carla,10.0")          // -> 10.0
// ex10 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>
#include <string>
#include <sstream>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX10_H
#define EX10_H

// Recebe varias linhas "nome,nota" (nao vazio) e retorna a media das notas.
double media_notas_csv(const std::string& conteudo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
double media_notas_csv(const std::string& conteudo) {
    // TODO: percorra as linhas (igual Lista 07), some as notas (std::stod
    // converte string para double) e divida pela quantidade de linhas.
    return 0.0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(std::abs((media_notas_csv("Ana,8.0\nBruno,6.0")) - (7.0)) <= (0.0001));
    assert(std::abs((media_notas_csv("Carla,10.0")) - (10.0)) <= (0.0001));
    assert(std::abs((media_notas_csv("A,5.0\nB,5.0\nC,5.0")) - (5.0)) <= (0.0001));
    std::cout << "Todos os testes passaram!\n";
    return 0;
}