Escola Politécnica da Universidade de São Paulo
Laboratório de Programação
Orientada a Objetos para
Engenharia Elétrica
Aula 9: Manipulação de dados e
operações em arquivos
PCS3111
Agenda
1. Noções de arquivos
2. Entrada e Saída (modo texto)
3. Entrada e Saída (modo binário)
4. Visão geral C C++
5. Abertura de arquivos e tratamento de erros
2
Arquivo
Noção intuitiva
• Sequência de bytes (stream)
• Armazenamento não-volátil
• Identificados por uma string (nome de arquivo)
Visão geral
• Muito mais abrangente do que simplesmente
armazenamento
• Abstração unificadora em sistemas operacionais
Abstração de hardware (rede, video, hds, ...)
Comunicação entre processos
Obs.: processo = programa em execução
4
Sistema Operacional
Camada de software responsável por fornecer
serviços básicos de forma unificada a
programas em execução
Responsável pelos detalhes de manipulação de
arquivos
Disponibiliza uma API unificada para operações
sobre arquivos
5
Sistema Operacional
Comunicação
1. O processo requisita ao SO a abertura de um arquivo
2. O S.O. realiza a abertura do arquivo e devolve ao
processo um identificador do arquivo aberto
3. O processo pede ao SO uma operação (leitura/escrita)
utilizando o identificador obtido na etapa 2
4. O processo requisita ao SO o fechamento de arquivo
Algumas mudanças podem não ter sido efetuadas sobre o
arquivo
Número limitado de arquivos que podem ser abertos por um
processo
6
Tipos de Arquivo
Seqüencial
• Exemplo: fitas magnéticas
• Necessário percorrer toda a seqüência de bytes em
busca de uma informação
Acesso Aleatório
• Exemplo: disco rígido
• Permitem o acesso eficiente a informação em qualquer
posição na sequência de bytes
Dispositivos
• Exemplo: monitor, interface de rede, comunicação serial
• Leitura e escrita realizam ações de E/S
• Geralmente só escrita ou só leitura (dificilmente ambos) 7
Modos de Leitura / Escrita
Modo Texto
• Informações codificadas em texto
• Decodificação automática de caracteres
• Suporte a leitura de linhas e palavras
• Compreensível pelo ser humano
Modo Binário
• Informações codificadas em bytes (similar a memória)
• Dependentes de arquitetura
Little endian (byte menos significativo no menor endereço)
Big endian (byte menos significativo no maior endereço)
• Leitura e escrita rápidas 8
Acesso a arquivo em C
Arquivos padrão
• Entrada Padrão (stdin)
Arquivo aberto para leitura
Fonte de dados para o processo
• Saída Padrão (stdout)
Arquivo aberto para escrita
Saída de dados para o processo
• Saída de Erro (stderr)
Arquivo aberto para escrita
Saída de dados alternativa para o processo (geralmente
para comunicar erros)
9
Arquivos Padrão
Entrada e Saída Padrão podem ser qualquer
tipo de arquivo
A Entrada Padrão de um processo pode ser
conectada à Saída Padrão de um outro
processo
Utilizaremos nessa aula o caso mais simples
• Entrada Padrão
Leituras sobre esse arquivo causam a interrupção do
processo e espera pelo usuário fornecer dados via teclado
• Saída Padrão
Escritas nesse arquivo causam a escrita de dados no
terminal em que o programa está executando
10
Entrada e Saída Padrão em C
(Apenas para efeito de comparação)
Também disponível em C++, incluir <cstdio>
No namespace std temos
• stdin → Entrada Padrão
• stdout → Saída Padrão
• stderr → Saída de Erro
stdin, stdout e stderr são variáveis do tipo
FILE*
12
Entrada e Saída Padrão em C
Entrada Padrão
Saída Padrão
13
int i = 0; double x = 0; scanf("%d", &i); // leitura de um inteiro scanf("%lf", &x); // leitura de um ponto flutuante
int i = 42; double x = 3.14159; printf("%d", i); printf("%lf", x);
Leitura de uma linha em C
Necessita de uma região de memória para
armazenar o conteúdo da linha (buffer)
A função fgets precisa ser informada do total
de caracteres
Número máximo de caracteres que serão lidos no
código acima serão 79
Após o último caractere será armazenado o caractere
\0 para seguir a codificação de strings em C
14
char buffer[80]; fgets(buffer, 80, stdin);
Entrada e Saída Padrão em C++
Incluir header <iostream>
No namespace std temos
• cin → Entrada Padrão ("C" in)
• cout → Saída Padrão ("C" out)
• cerr → Saída de Erro ("C" err)
cin é uma variável global do tipo istream
cout e cerr são variáveis globais do tipo
ostream
15
int i = 0; double x = 0; cin >> i; // leitura de um inteiro cin >> x; // leitura de um ponto flutuante
Entrada e Saída Padrão em C++
16
//cin01.cpp #include <iostream> using namespace std; int main() { int i; cout << "Entre i: "; if (cin >> i) cout << "i=" << i << endl; else cout << "Erro de leitura i" << endl; } C:\hae>cin01 Entre i: 20 i=20 C:\hae>cin01 Entre i: vcvdfg Erro de leitura i C:\hae>cin01 Entre i: 123dfdfdwe i=123
Entrada e Saída Padrão em C++
17
//cin03.cpp #include <iostream> using namespace std; int main() { string st; cout << "Entre st: "; cin >> st; cout << "st=" << st << endl; } C:\hae>cin03 Entre st: abc def st=abc Nota: Branco, tab e fim de linha são separadores.
Saída Padrão em C++
18
int i = 42;
double x = 3.14159;
cout << i; // escrita de um inteiro
cout << x; // escrita de um ponto flutuante
cout << "Hello World"; // escrita de uma sequência de
// caracteres terminada em \0
Saída Padrão em C++
19
//cout01.cpp
#include <iostream>
using namespace std;
int main() {
int i = 42;
double x = 3.14159;
cout << i << endl;
cout << x << endl;
cout << "Hello World";
}
Saída:
42
3.14159
Hello World
Redirecionar saída padrão
20
C:\hae>cout01
42
3.14159
Hello World
C:\hae>cout01 > saida.txt
C:\hae>type saida.txt [Linux=cat saida.txt]
42
3.14159
Hello World
C:\hae>
Leitura de uma linha da entrada
padrão em C++ Incluir header <string>
• (using namespace std)
• Usar a função getline
Exemplo
21
string name;
getline(std::cin, name);
Permite ler até o fim da linha, mesmo com branco.
Leitura de uma linha da entrada
Redirecionar entrada
22
//getline01.cpp
#include <iostream>
using namespace std;
int main()
{ string nome;
cout << "Entre nome completo: ";
getline(cin,nome);
cout << "Nome: " << nome << endl;
}
C:\hae>getline01
Entre nome completo: Hae Yong Kim
Nome: Hae Yong Kim
C:\hae>getline01 < getline01.txt
Imprimir para string
23
#include <iostream>
#include <sstream>
using namespace std;
int main()
{ ostringstream sai;
int i=3;
sai << "Testando saida " << "i=" << i << endl;
cout << sai.str();
}
Curiosidade: << e >>
O operador << quando aplicado a números realiza
o shift à esquerda enquanto que o operador >>
realiza o shift à direita
C++ permite a definição de comportamento para a
maioria dos operadores
Os operadores << e >> foram escolhidos para os objetos
ostream e istream por melhor indicarem o deslocamento
de informação
Os operadores << e >> são sobrecarregados
conforme o seu operando
No exemplo anterior para ler ou escrever um int e um
double apresentaram o mesmo formato de invocação 24
Modo binário
Usaremos apenas modo texto nesta disciplina
Leitura e escrita de seqüências de bytes
Não há interpretação dos bytes, se quiser um
inteiro terá que compor o valor do inteiro a partir
de seus bytes
Comuns em formatos de mídias (imagens, sons,
vídeos, etc.)
26
Visão geral C C++
C
Vantagens
• Amplamente conhecido
por desenvolvedores
• (Imitados por outras
linguagens)
Desvantagens
• Compilador C
normalmente não verifica
• Se o número de
parâmetros estão
corretos
• Se os tipos de
parâmetros passados
estão corretos
C++
Vantagens
• O compilador C++ é
capaz de verificar
sobrecargas
• Sem necessidade de
manipulação de ponteiros
Desvantagens
• Overhead comparado ao
C
• Discutível alteração da
semântica dos operadores
<< e >>
28
Abertura de Arquivos
Limites no uso da entrada e saída padrão
• Entrada e Saída Padrão podem estar associadas a
arquivos voláteis (terminal, /dev/null)
• É comum uma aplicação trabalhar com vários
arquivos
• Log, configuração, internacionalização, arquivos de dados,
etc.
Arquivos em C++
• Incluir header <fstream>
• Objetos std::ifstream para leitura (derivados de
std::istream)
• Objetos std::ofstream para escrita (derivados de
std::ostream)
30
Abertura de arquivo
Formato geral
• in_or_out_stream.open(arquivo, parâmetros)
• Os parâmetros in e out são adicionados
automaticamente no método open para objetos do
tipo ifstream e ofstream respectivamente
31
Parâmetros
(múltiplos possíveis usando “|”)
Significado
app (append) escritas no final de arquivo
ate (at end) move para final após abertura
binary (binary) modo binário
in (input) operações de leitura
out (output) operações de escrita
trunc (truncate) apaga conteúdo atual do arquivo
Leitura
"input" ao sair de escopo fecha o arquivo
automaticamente
(RAII – Resource Aquisition Is Initialization)
32
ifstream input;
input.open("/home/user/teste.txt");
int i;
double x;
input >> i; // lê um valor inteiro
input >> x; // lê um valor ponto flutuante
Leitura
33
//leitura01.cpp
#include <iostream>
#include <fstream>
using namespace std;
int main()
{ ifstream ent("leitura01.txt");
int i; ent >> i;
cout << "i=" << i << endl;
double x; ent >> x;
cout << "x=" << x << endl;
}
2
3.14
C:\hae>leitura01
i=2
x=3.14
O arquivo é fechado automaticamente quando “ent” sai do escopo
pelo destruidor.
Tratamento de erros em C++
Erros ao se trabalhar com arquivos
• Arquivo não existe
• Fim de arquivo
• Erro na leitura (string quando esperava inteiro)
• Erro no dispositivo
34
Tratamento de erros em C++
Baseado na verificação de estado
Muito antes de C++ incorporar exceções
Estado composto por 4 bits (mais de um bit
pode estar ativo)
• goodbit: sem erros
• eofbit: fim de arquivo encontrado em operações de
leitura
• failbit: falha na leitura de um valor; erro lógico
• badbit: erro no dispositivo de E/S
35
Dica: pode-se recuperar de um failbit mas não de um badbit
Tratamento de erros em C++
Métodos auxiliares
Conversão automática para bool ajuda a
verificar erros
36
rdstate() good() eof() fail() bad()
goodbit 1 0 0 0
eofbit 0 1 0 0
failbit 0 0 1 0
badbit 0 0 1 1
Exemplo (média)
37
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream in;
in.open("avg.txt");
if (!in) {
cerr << "Arquivo nao encontrado\n";
return EXIT_FAILURE;
}
double total = 0;
int n = 0;
while (in) {
double x;
in >> x;
if (in) {
total += x;
n++;
}
}
if (in.bad()) {
cerr << "Erro de E/S\n";
return EXIT_FAILURE;
}
if (n > 0) {
in.clear();
double avg = total / n;
cout << "media: "
<< avg << "\n";
}
return EXIT_SUCCESS;
}
Exemplo (média)
38
//media01.cpp
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream in("media01.txt");
if (!in) {
cerr << "Arquivo nao encontrado\n";
exit(1);
}
double total = 0;
int n = 0;
double x;
while (in >> x) {
total += x;
n++;
}
if (n > 0) {
double avg = total / n;
cout << "media: " << avg << endl;
}
}
media.txt
2.5
2.5
3.5
3.5
Saída:
media: 3
Fechamento de arquivos
Um arquivo pode ser desconectado de um
programa, chamando o método close( );
39
#include <fstream>
using namespace std;
const int fileCnt = 3;
const char* fileTable [fileCnt] = { "ClariceLispector.txt",
“MachadoDeAssis.txt”, “ManuelBandeira.txt” };
int main() {
ifstream inFile; // não está associado a nenhum arquivo
for (int ix = 0; ix < fileCnt; ix++) {
inFile.open (fileTable[ix]);
// ... Verifica se a abertura teve sucesso
// ... Processa o arquivo
inFile.close( );
}
}
Exercício
Considere um pequeno sistema que simula o
controle de temperatura de um Local
(residência, prédio, etc)
• Atua sobre uma quantidade de espaços (quartos,
salas, escritórios, etc)
• Cada espaço controlado pode conter dispositivos de
refrigeração e também sensores de temperatura e/ou
de presença
Valores podem ser lidos pelo sistema
41
Exercício
O objetivo do exercício é lidar com sensores em um local, tratando dois aspectos básicos do sistema • Gravação de um arquivo de registro (CSV)
A leitura dos dados de cada sensor em cada espaço será simulada por meio de arquivos e o CSV é um arquivo texto
• O sistema deverá possuir um arquivo de configuração, a ser lido ao início de funcionamento do sistema
Obs: Não trataremos de controle de ar-condicionado ou de mudanças na estrutura do local ou dos espaços, apenas SENSORES.
42
Esquema de um Local controlado
43
Sala
“ 55 “
( Ar Condicionado
com Ventilação)
(Sensor de
Temperatura e de
presença)
Copa
“ 11 "
(Ventilador)
(Sensor
Temperatura e de
presença)
Escritorio B
“ 44 “
(Ar condicionado)
(Sensor de
Temperatura)
Escritório A
“ 33 “
(Ar condicionado
com ventilação)
(Sensor de
Temperatura)
Cozinha
“ 22 “
(Ventilador)
(Sensor de
Temperatura e de
presença)
Quarto 4
“ 99 “
(Ventilador)
(Sensor
Temperatura)
Quarto 3
“ 88 “
(Ventilador)
(Sensor
Temperatura)
Quarto 2
“ 77 “
(Ar condicionado)
(Sensor de
Temperatura)
Quarto 1
“ 66 “
( Ar Condicionado com
Ventilação)
(Sensor de
Temperatura)
Características dos Refrigeradores e
Sensores
44
Ar condicionado com
ventilação:
Temperatura: de 10 oC a 30
oC
Ventilação: de 0 a 4
Ventilador:
Não muda a temperatura.
Apenas a percepcão do
usuário.
Ventilação: de 0 a 8.
Ar condicionado simples:
Não tem ventilação.
Temperatura: de 5oC a 30 oC
Sensor de Temperatura/Presença:
Cada espaço controlado pode ter um
ou mais sensores. O sistema coleta
esta informação e o valor lido permite
verificar se o respectivo refrigerados
está funcionando.
Atenção: No exercício, o seu
funcionamento será simulado por
meio de valores aleatórios
Esqueleto
O esqueleto do exercício permite definir sensores
simples com seus identificadores (números
inteiros) e representar o seu estado de alarme em
um painel
O estado do sensor será produzido por meio de
um gerador aleatório
Para iniciar o funcionamento o sistema solicita a
leitura de um arquivo de configuração, que descreve a estrutura do Local
45
Formato do arquivo
A <faixa de valores> depende do tipo do sensor:
• Se o tipo é “T”, então há dois inteiros, mínimo e
máximo
• Se o tipo é “P” então há um inteiro, 1 - ativado, ou 0 -
não
46
<nome do local> <número de espaços> <nome do espaço> <número de sensores> <tipo do sensor> <nome > <faixa de valores> . . . <nome do espaço> <número de sensores> <tipo do sensor> <nome > <faixa de valores> . . .
Exemplo de arquivo de entrada
47
Residencia 3 Sala 3 T SensorTemperatura_1 0 40 T SensorTemperatura_2 0 40 P SensorPresenca_1 1 Quarto 2 T SensorTemperatura_3 0 40 P SensorPresenca_2 1 Banheiro 1 T SensorTemperatura_4 0 40
Exemplo de Painel
48
Residencia(alarme: LIGADO) Sala(alarme: LIGADO) SensorTemperatura_1(0 <= t <= 40) leitura: 10°C seguro: sim SensorTemperatura_2(0 <= t <= 40) leitura: -8°C seguro: nao SensorPresenca_1(ativa o alarme: sim) leitura: MOVIMENTO seguro: nao Quarto(alarme: LIGADO) SensorTemperatura_3(0 <= t <= 40) leitura: 3°C seguro: sim SensorPresenca_2(ativa o alarme: sim) leitura: MOVIMENTO seguro: nao Banheiro(alarme: DESLIGADO) SensorTemperatura_4(0 <= t <= 40) leitura: 17°C seguro: sim
Exemplo de arquivo de saída (CSV)
49
Local;Espaþo;Sensor;Leitura Residencia;Sala;SensorTemperatura_1;-6 Residencia;Sala;SensorTemperatura_2;47 Residencia;Sala;SensorPresenca_1;1 Residencia;Quarto;SensorTemperatura_3;17 Residencia;Quarto;SensorPresenca_2;0 Residencia;Banheiro;SensorTemperatura_4;34
Exercício
Suponha que o Local possa ter dois tipos de sensores
• SensorTemperatura, que dá alarme quando a
temperatura ambiente cai abaixo de um valor limite
• SensorPresença, que dá alarme quando algum
objeto interrompe o feixe de infravermelho
1. Em main, implemente a função carregarArquivo que
ao iniciar ler a configuração de um arquivo, que deverá
ser construído com o padrão descrito anteriormente
2. Implemente a função de CSV no sistema. Ela deverá ser implementada na classe Registro. Construa tanto o
header quanto a implementação
3. Implemente o caso 5 do menu prompt em main 50
Observação sobre o item 3 – Visitor
• Padrão de projeto que permite desvincular a descrição
de uma estrutura da especificação das ações que
devem ser executadas sobre ela;
• Através de uma INTERFACE, torna-se obrigatória a
implementação da ação a ser executada para todos os
elementos (tipos) que compõem a estrutura em questão.
• Cria-se uma INTERFACE com métodos abstratos de
visitação para todos os elementos:
a) Todos os elementos deverão estar associados ao seu
correspondente método de visitação;
b) O nome do método deverá fazer referência ao tipo de
elemento visitado;
c) O método deverá receber como argumento um objeto
correspondente ao tipo de nó visitado.
51
Continuação - Visitor
• Acrescentar, em todas classes que representam os elementos um método de visitação:
a) Todos os métodos deverão ter o mesmo nome;
b) Todos os métodos deverão receber como argumento um objeto da classe Visitor;
c) O corpo desses métodos será composto por um único comando, responsável pela chamada no método de visitação específico, no argumento, conforme o tipo de nó em questão.
• Para cada tipo de operação diferente que se deseje executar sobre um elemento cria-se uma nova classe que implementa a interface VISITOR:
a) Todas as classes assim especificadas deverão conter implementações para todos os métodos relacionados na interface;
b) Cada método é responsável por executar a operação requerida sobre o tipo de elemento a que se refere;
c) A referência THIS deverá ser passada como argumento em toda chamada de métodos de visitação.
52
Exemplo
53
#include <cstdlib>
#include <iostream>
using namespace std;
class Gato;
class Cachorro;
class AnimalVisitor {
public:
virtual void visit(Gato *gato) = 0;
virtual void visit(Cachorro *cachorro) = 0;
};
class Animal {
private:
int idade;
public:
Animal(int age) {
idade = age;
}
virtual void accept(AnimalVisitor *visitor) = 0;
};
class EmissorDeSom : public AnimalVisitor {
void visit(Gato *gato){
cout << "Miau\n";
}
void visit(Cachorro *cachorro){
cout << "Au!\n";
}
};
Exemplo
54
class Cachorro : public Animal {
public:
Cachorro(int age) : Animal(age) {
}
void accept(AnimalVisitor *visitor){
visitor->visit(this);
}
};
class Gato : public Animal {
public:
Gato(int age) : Animal(age) {
}
void accept(AnimalVisitor *visitor){
visitor->visit(this);
}
};
void emitirSom(Animal *animal){
AnimalVisitor *visitor = new EmissorDeSom();
animal->accept(visitor);
// faltou delete
}
int main(int argc, char** argv) {
Gato gato(3);
Cachorro cachorro(5);
Animal* animal[2] = {&gato, &cachorro};
for (int i = 0; i < 2; i++)
emitirSom(animal[i]);
return 0;
}
55
//animal03b.cpp #include <cstdlib> #include <iostream> using namespace std; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< class Gato; class Cachorro; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< class AnimalVisitor { public: virtual void visit(Gato *gato) = 0; virtual void visit(Cachorro *cachorro) = 0; virtual ~AnimalVisitor() {} // Faltou }; class EmissorDeSom : public AnimalVisitor { void visit(Gato *gato); void visit(Cachorro *cachorro); }; class ImprimirIdade : public AnimalVisitor { void visit(Gato *gato); void visit(Cachorro *cachorro); };
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< class Animal { public: int idade; Animal(int age) : idade(age) {} virtual void accept(AnimalVisitor *visitor) = 0; }; class Cachorro : public Animal { public: Cachorro(int age) : Animal(age) {} void accept(AnimalVisitor *visitor); }; class Gato : public Animal { public: Gato(int age) : Animal(age) {} void accept(AnimalVisitor *visitor); };
56
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< void EmissorDeSom::visit(Gato *gato) { cout << "Miau!\n"; } void EmissorDeSom::visit(Cachorro *cachorro) { cout << "Au!\n"; } void ImprimirIdade::visit(Gato *gato){ cout << "Gato com " << gato->idade << " anos\n"; } void ImprimirIdade::visit(Cachorro *cachorro){ cout << "Cachorro com " << cachorro->idade << " anos\n"; } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< void Cachorro::accept(AnimalVisitor *visitor){ visitor->visit(this); } void Gato::accept(AnimalVisitor *visitor){ visitor->visit(this); }
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< void emitirSom(Animal *animal){ AnimalVisitor *visitor = new EmissorDeSom(); animal->accept(visitor); delete visitor; // Faltou } void imprimirIdade(Animal *animal){ AnimalVisitor *visitor = new ImprimirIdade(); animal->accept(visitor); delete visitor; } int main() { Gato gato(3); Cachorro cachorro(5); Animal* animal[2] = {&gato, &cachorro}; for (int i = 0; i < 2; i++) emitirSom(animal[i]); for (int i = 0; i < 2; i++) imprimirIdade(animal[i]); }
Vector
Container seqüencial, implementado como um
array que automaticamente altera seu tamanho
ao se acrescentar elementos ao seu final.
private:
typedef std::vector<int> IntVect;
Nesse caso o apelido IntVect foi definido como
um vetor de inteiros, que o permite alterar o
tamanho dinamicamente (diferentemente de um
array).
58
Unique_ptr
unique_ptr é um tipo de ponteiro especial
que automaticamente
invoca o delete sobre uma memória alocada
dinamincamente ao sair do escopo.
Muito útil para gerência de memória.
59
Referência para acelerar
void f(const string& st) {
cout << st << endl;
}
void g() {
st=“abcdefdfsdfdfdfdfdfdfdfdfdfdfdfdf”;
f(a);
// O programa roda mais rápido
}
61
Top Related