Programar em C++/Entrada e saída de dados 2: diferenças entre revisões

[edição não verificada][edição não verificada]
Conteúdo apagado Conteúdo adicionado
Daveiro (discussão | contribs)
Sem resumo de edição
 
Daveiro (discussão | contribs)
Sem resumo de edição
Linha 6:
 
----
Input/Output with files
 
Nota introdutora: Este capitulo supostamente é colocado uns capítulos mais para o fim, mas acho por bem que se torne as coisas mais interactivas e permitam agora fazer files, dá muito mais entusiasmo. Encontramos aqui conceitos avançados mas poderão ser bem ultrapassados.
 
 
Gravar os dados para um ficheiro
Os dados que guardamos nos programas estão guardados na memória RAM, que é limpa quando o programa ou computador pára de funcionar. Isso implicaria que perderíamos toda a informação!
Porém existe uma maneira para tornar os dados persistentes que é gravar os dados num ficheiro no hard drive ou noutro meio persistente.
Outros meios avançados para guradar dados podem envolver bases de dados relacionais ou XML.
 
O que é um file?
Um file é uma colecção de dados que estão localizados numa memória persistente tipo hard drive, cd-rom, etc.
Para identificarmos o file podemos chamar-lhe um nome (filename). Os filename têm usualmente uma extensão do ficheiro. Esta extensão permite indicar o tipo de dados que estão guardados no ficheiro e o tipo de programa que é usado para aceder ao ficheiro. a extensão são 3 ou 4 letras que seguem a seguir ao filename antes do “.”. Por exemplo: “joão.doc”. Isto diz-me que temos um ficheiro que se chama “joão”, e que tem a extensão .doc que refere usualmente a documentos do WORD.
Outro tipo de extensões podem ser .xls para documentos EXCEL. ou ainda ”.cpp” para ficheiros de código de c++.Para ver todas as extensões existentes de ficheiros, ver…lista de extensões de ficheiros
 
Text file e binary files
Existem na verdade dois tipos de ficheiros os text files e osbinary files.
• Os text files apenas podem guardar texto
• Os binary files podem guardar mais informação, como imagens, base de dados, programas…Por exemplo Word, guarda os seus files em binary files, porque eles para além do texto, guardam também informação acerca da formatação do texto, por exemplo para as tabelas, as listas numeradas, daí aparecerem os caracteres de formatação tipo ã6, ÌL, h5…
os binary files são um tópico avançado.
 
fstream standard library
Até agora temos usado a biblioteca iostream (i de input + o de output + stream), que suporta, entre várias funcionalidades, o objecto cin para ler do standard input (que é usualmente o teclado) e o objecto cout para saída standard (que usualmente é o monitor)
 
Ora agora queremos é ler e escrever para ficheiros e isso requer a biblioteca fstream (f de file + stream).
Esta biblioteca define 3 novos tipos de dados:
• ofstream (apenas para a direção output – out to a file. serve para criar ficheiros e escrever, não serve para ler)
• ifstream (apenas na direcção input – from a file . serve para ler ficheiros, receber dados dos ficheiros, não serve para criar nem escrever)
• fstream (este conjuga os dois tipos anteriores, input e output to file. cria ficheiros, escreve e lê informação dos ficheiros.
 
Abrir um file
Um ficheiro tem de ser aberto para ler ou escrever no ficheiro. É necessário criar uma linha de comunicação entre o file e o objecto stream.
Podemos recorrer a dois métodos para abrir um ficheiro:
1. Usando a função membro chamada de open
2. Usando um constructor.
 
Usando a função membro open (função membro de uma classe)
Esta função tem como primeiro argumento o nome e localização do ficheiro a ser aberto
O segundo membro tem a ver se queremos abrir num modo diferente do defaut
 
Sobre a questão da localização existem 2 tipos, o relative path e o absolute path. Este ultima é indicar o caminho todo c:\\....\\joao.doc. o relative path dispensa essa descrição se o ficheiro estiver na mesma directoria que o programa.
 
Sobre a questão do modo de abertura temos as seguintes modalidades
File Mode Flag Description
ios::app Append mode. The file's existing contents are preserved and all output is written to the end of the file.
ios::ate If the file already exists, the program goes directly to the end of it. Output may be written anywhere in the file. This flag usually is used with binary mode.
ios::binary Binary mode. Information is written to the file in binary format, rather than in the default text format.
ios::in Input mode. Information will be read from the file.
The file will not be created if it does not exist.
ios::out Output mode. Information will be written to the file.
By default, the existing file contents will be overwritten.
ios::trunc If the file already exists, its contents will be truncated, another word for deleted or overwritten.
This is the default mode of ios::out.
 
Podemos ter vários flags ao mesmo tempo tipo
ofstream outfile; //crio o objecto outfile
outfile.open("students.dat", ios::binary | ios::app); //chamo a função membro open do objecto, com o 1º parametro que é o nome do doc e o 2º o modo de abertura.
 
 
Usando o Constructor
O constructor é uma função que é automaticamente chamada quando tentamos criar uma instância de um objecto.
fstream afile; //é criado uma instância do fstream chamada de afile
Os object constructor podem ser overloaded. ie, para o mesmo objecto (o mesmo nome) podemos ter um constructor sem argumentos, 1 argumento, 2 argumentos, etc. no exemplo anterior criamos um sem argumentos. Vamos dar um exemplo com 1 argumentos
ofstream outfile (“joão.doc”, ios::out); //chama o constructor 1 argumento, criando uma instancia ofstream e abrindo o ficheiro joao.doc para output
 
 
Comparando os dois métodos (pela função membro e pelo constructor)
O primeiro método é similar a ter
int age;
age=39;
O segundo método é similar a
int age=39;
 
Abrir um file para leitura
A história aqui é a mesma só tem uma diferença é que no caso de leitura, não será criado nenhum ficheiro caso ele não exista.
 
ifstream a; //cria objecto a
a.open (“joão.doc”); //chama função membro open ao objecto a, com o parâmetro do nome do ficheiro
 
Podíamos fazer o mesmo com o constructor
ifstream a (“joão.doc”);
 
Ou ainda
fstream b;
b.open(“joão.doc”, ios::in)
 
ou ainda, ainda
fstream b(“joao.doc”, ios::in)
 
 
Há mais uma nota a fazer, se quisermos ler e escrever, não podemos usar o ofstream e o ifstream ao mesmo tempo, teremos de usar o fstream. Teremos de fazer
fstream a (“joão.doc”, ios::in | ios::out);
Neste caso o defaut será preservar o conteúdo do ficheiro e o ficheiro será criado caso ele não exista.
 
 
Verificar se o ficheiro foi aberto.
#include <fstream>
#include <iostream>
using namespace std;
 
int main ()
{
ifstream a; //crio objecto a da classe ifstream - leitura
a.open("joao.doc"); //chamo função membro open
cout << "(joao) = " << a << endl; //imprime o objecto
cout << "(joao.fail()) = " << joao.fail() << endl; //chamo função membro fail
system (“pause”);
return 0;
}
 
No caso do ficheiro “joão.doc” não existir
 
(a) = 00000000
(a.fail()) = 1
No caso do ficheiro “joão.doc” existir na mesma directoria que o programa
 
(a) = 0012FE40
(a.fail()) = 0
 
 
Repare que o resultado é a impressão do endereço, do objecto a de ifstream. dá um ponteiro!!
 
 
Fechar um ficheiro
Devemos fechar depois de ler e/ou escrever. Mas porquê se o objecto do ficheiro irá ser fechado assim que o programa acabar? Porque estamos a utilizar recursos com um ficheiro aberto, porque alguns sistemas operativos limitam o nº de ficheiros abertos, e estando este aberto impede que outros se possam abrir e por fim porque apenas se pode abrir um ficheiro que foi fechado.
 
ofstream outfile;
outfile.open("students.dat");
….
outfile.close();
 
 
 
 
Vamos criar um exemplo mais real. Queremos criar um programa que escreva informação inserida pelo utilizador num ficheiro por nós escolhido
#include <fstream>
#include <iostream>
using namespace std;
 
int main ()
{
char data[80]; //criamos um array de 80 letras
ofstream outfile; //criamos objecto da classe outstream
outfile.open("c:\\joao.doc"); //chamamos a função membro da classe para o obejcto criado. Esta função membro cria o file “joão” na directoria c:\\
 
cout << "digite o seu nome: "; //imprime no ecrã a frase
cin.getline(data, 80); //chama função membro getline do objecto cin para ler o que foi escrito
 
outfile << data << endl; //coloca no objecto criado o array
outfile.close(); //fechamos o objecto
system (“pause”);
return 0;
}
 
Podemos ir ver o novo ficheiro em c:\\ com o nome joao.doc e tem lá escrito aquilo que digitámos.
 
Agora vamos tentar ler o que escrevemos no documento criado.
#include <fstream>
#include <iostream>
using namespace std;
 
int main ()
{
char data[80];
ifstream infile;
infile.open("c:\\joao.doc");
infile >> data; //colocamos os dados abertos no array
cout << data << endl;
infile.close();
system (“pause”);
return 0;
}
 
Repare que se tivéssemos escrito duas palavras, apenas uma era apresentada (ela pára no primeiro whitespace), para isso necessitaríamos de repetir:
#include <fstream>
#include <iostream>
using namespace std;
 
int main ()
{
char data[80];
ifstream infile;
infile.open("c:\\joao.doc");
infile >> data; //colocamos os dados abertos no array
cout << data << endl;
infile >> data; //colocamos os dados abertos no array
cout << data << endl;
infile.close();
system (“pause”);
return 0;
}
 
Agora já obtemos 2 palavras e são apresentadas em linhas diferentes.
Mas porra temos de arranjar um método para não estar a repetir constantemente, podemos fazer isso com
infile.getline(data, 80);
 
 
Então ficamos com
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
 
int main ()
{
string data;
 
ofstream outfile;
outfile.open("c:\\joao.doc");
 
cout << "Writing to the file" << endl;
cout << "===================" << endl;
cout << "Enter class name: ";
getline(cin, data);
outfile << data<< endl;
 
cout << "Enter number of students: ";
cin >> data;
cin.ignore(); //esta função membro é para limpar o caractere newline do inputbuffer depois de usar o objecto cin com o operador de extração >>
 
outfile << data<< endl;
outfile.close();
 
ifstream infile;
cout << "Reading from the file" << endl;
cout << "=====================" << endl;
infile.open("c:\\joao.doc ");
 
getline(infile, data);
cout << data << endl;
getline(infile, data);
cout << data << endl;
 
infile.close();
system (“pause”);
return 0;
}
 
 
 
Looping pelo ficheiro.
E se não soubermos quantas linhas têm o file?
O objecto ifstream tem uma função membro que é a eof (e-end+o-of+f-file). esta função não tem parâmetros e retorna true se o end of file foi alcançado e false caso contrário.
No entanto esta função eof não é de confiança com os ficheiros texto como o é para os ficheiros binários (é que nos ficheiros binários não existem espaços em branco).
 
A melhor alternativa é usar a função membro fail.
ifstream infile;
infile.open("joao.doc");
infile >> data;
while(!infile.fail())
{
infile >> data;
cout << data;
}
infile.close();
 
 
Refazendo tudo
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
 
int main ()
{
string data;
 
ofstream outfile;
outfile.open("c:\\joao.doc");
cout << "Writing to the file" << endl;
cout << "===================" << endl;
cout << "Enter class name: ";
getline(cin, data);
outfile << data<< endl;
cout << "Enter number of students: ";
cin >> data;
cin.ignore();
outfile << data<< endl;
outfile.close();
 
ifstream infile;
cout << "Reading from the file" << endl;
cout << "=====================" << endl;
infile.open("c:\\joao.doc");
getline(infile, data);
while(!infile.fail())
{
cout << data << endl;
getline(infile, data);
}
infile.close();
system (“pause”);
return 0;
}
 
 
Agora vamos fazer o nosso programa mais modular:
1. writeFile – para abrir um file para escrita usando o ofstream e
2. readFile - ler do ficheiro usando o ifstream
3. Cada função irá verificar se o ficheiro foi aberto com sucesso
 
 
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
 
bool writeFile (ofstream&, char*);
bool readFile (ifstream&, char*);
 
int main ()
{
string data;
bool status;
 
ofstream outfile;
status = writeFile(outfile, "students.dat");
if (!status)
{
cout << "File could not be opened for writing\n";
cout << "Program terminating\n";
return 0;
}
else
{
cout << "Writing to the file" << endl;
cout << "===================" << endl;
cout << "Enter class name: ";
getline(cin, data);
outfile << data<< endl;
cout << "Enter number of students: ";
cin >> data;
cin.ignore();
outfile << data<< endl;
outfile.close();
}
 
ifstream infile;
status = readFile(infile, "students.dat");
if (!status)
{
cout << "File could not be opened for reading\n";
cout << "Program terminating\n";
return 0;
}
else
{
cout << "Reading from the file" << endl;
cout << "=====================" << endl;
getline(infile, data);
while(!infile.fail())
{
cout << data << endl;
getline(infile, data);
}
infile.close();
}
system (“pause”);
return 0;
}
 
bool writeFile (ofstream& file, char* strFile)
{
file.open(strFile);
if (file.fail())
return false;
else
return true;
}
 
bool readFile (ifstream& ifile, char* strFile)
{
ifile.open(strFile);
if (ifile.fail())
return false;
else
return true;
}
 
 
 
Tá feito!
 
 
Manipuladores
vamos ver uma série de manipuladores
Manipulator Use
boolalpha Causes bool variables to be output as true or false.
noboolalhpa (default) Causes bool variables to be displayed as 0 or 1.
dec (default) Specifies that integers are displayed in base 10.
hex Specifies that integers are displayed in hexadecimal.
oct Specified that integers are displayed in octal.
left Causes text to be left justified in the output field.
right Causes text to be right justified in the output field.
internal Causes the sign of a number to be left justified and the value to be right justified.
noshowbase (default) Turns off displaying a prefix indicating the base of a number.
showbase Turns on displaying a prefix indicating the base of a number.
noshowpoint (default) Displays decimal point only if a fractional part exists.
showpoint Displays decimal point always.
noshowpos (default) No "+" prefixing a positive number.
showpos Displays a "+" prefixing a positive number.
skipws (default) Causes white space (blanks, tabs, newlines) to be skipped by the input operator, >>.
noskipw s White space not skipped by the extraction operator, >>.
fixed (default) Causes floating point numbers to be displayed in fixed notation.
Scientific Causes floating point numbers to be displayed in scientific notation.
nouppercase (default) 0x displayed for hexadecimal numbers, e for scientific notation
uppercase 0X displayed for hexadecimal numbers, E for scientific notation
 
 
Setting Output Width
setw(w) - sets output or input width to w; requires to be included.
width(w) - a member function of the iostream classes.
 
Filling White Space
setfill(ch) - fills white space in output fields with ch; requires to be included.
fill(ch) = a member function of the iostream classes.
 
Setting Precision
setprecision(n) - sets the display of floating point numbers at precision n. This does not effect the way floating point numbers are handled during calculations in your program.
 
Exemplificando o uso de manipuladores
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
 
int main()
{
int intValue = 15;
 
cout << "Integer Number" << endl;
cout << "Default: " << intValue << endl;
cout << "Octal: " << oct << intValue << endl;
cout << "Hex: " << hex << intValue << endl;
cout << "Turning showbase on" << showbase << endl;
cout << "Dec: " << dec << intValue << endl;
cout << "Octal: " << oct << intValue << endl;
cout << "Hex: " << hex << intValue << endl;
cout << "Turning showbase off" << noshowbase << endl;
cout << endl;
 
double doubleVal = 12.345678;
 
cout << "Floating Point Number" << endl;
cout << "Default: " << doubleVal << endl;
cout << setprecision(10);
cout << "Precision of 10: " << doubleVal << endl;
cout << scientific << "Scientific Notation: " << doubleVal << endl;
cout << uppercase;
cout << "Uppercase: " << doubleVal << endl;
cout << endl;
 
bool theBool = true;
 
cout << "Boolean" << endl;
cout << "Default: " << theBool << endl;
cout << boolalpha << "BoolAlpha set: " << theBool << endl;
cout << endl;
 
string myName = "John";
 
cout << "Strings" << endl;
cout << "Default: " << myName << endl;
cout << setw(35) << right << "With setw(35) and right: "
<< myName << endl;
cout.width(20);
cout << "With width(20): " << myName << endl;
cout << endl;
system (“pause”);
return 0;
}
 
 
 
 
 
Exercícios Improvisados
 
1. Quero colocar num documento Word, uma lista das combinações possíveis entre a,b,c e d. com a respectiva ordenação e quantidade.
a. Quero que seja a pessoa a escolher o nome do ficheiro e escreve também a localização.
b. Quero que seja depois tansformado num sistema modular.
 
 
2. arranjar uma maneira para contar o nº de espaços em branco, o nº de caracteres …o que quisermos de um dado documento
 
#include <iostream>
#include <fstream>
using namespace std;
 
int main()
{
int blank_count = 0;
int char_count = 0;
int sentence_count = 0;
char ch;
 
ifstream object("c:/Documents and Settings/Joaodaveiro/Ambiente de trabalho/jo.txt");
 
if (! object)
{
cout << "Error opening input file" << endl;
return -1;
}
 
while (object.get(ch))
{
switch (ch)
{
case ' ':
blank_count++;
break;
case '\n':
case '\t':
break;
case '.':
sentence_count++;
break;
default:
char_count++;
break;
}
}
cout << "There are " << blank_count << " blanks" << endl;
cout << "There are " << char_count << " characters" << endl;
cout << "There are " << sentence_count << " sentences" << endl;
system ("pause");
return 0;
}