Tutorial Qt – Capitulo 07 – Arquivos, diretórios e streams

Esse capitulo trata de arquivos e ações relacionados a eles. Cobrirá as partes importantes da plataforma de arquivos do Qt e as classes relacionadas ao sistema de arquivos. Depois de cada introdução das classes algum código será mostrada para demonstrar o uso dessas classes.;

QFile

O Qt manipula os arquivos como objetos. Por exemplo, um arquivo é representado por uma instância da classe QFile. O arquivo não tem que existir, nem estar aberto. O método exists() é usado para determinar se o arquivo existe. Se existir, pode ser removido usando o método remove(). Para leitura e escrita (ou ambos) o arquivo precisa ser aberto. Isso é feito usando o método open(). Um arquivo pode ser aberto em diversos modos, os mais comuns são:

  • IO_ReadOnly abre o arquivo somente para leitura.
  • IO_WriteOnly abre o arquivo para escrita.
  • IO_ReadWrite abre o arquivo para leitura e escrita.
  • IO_Append abre o arquivo para anexação de dados.

Quando estiver trabalhando com arquivos, isto é, anexando informações ao arquivo, é necessário abrir o arquivo como IO_WriteOnly | IO_Append para que o arquivo possa ser alterado. Quando chamar o método open() ele retornará TRUE se o arquivo for aberto com sucesso ou FALSE se ocorrer um erro ao abrir o arquivo. Se nenhum dos modos de escrita for usado o arquivo será criado se não existir apenas.

Quando estiver manipulando os objetos de arquivos, algumas vezes é desejável checar se uma arquivo está aberto ou não. Isso é feito facilmente com o método isOpen(). Quando tiver finalizado o trabalho com um arquivo, chame o método close(0 para fecha-lo. Isso salva qualquer buffer ainda não salvo no disco. Se for necessário efetuar esse salvamento sem fechar o arquivo, uso o método flush().

O exemplo abaixo mostra a classe QFile sendo usada. Logo em seguida, uma sessão do terminal onde a aplicação foi executada. O comando ‘touch’ cria o arquivo f.txt enquanto o comando ‘chmod -r’ remove o acesso para leitura do arquivo.


#include <qapplication.h>
#include <qfile.h>
#include <iostream>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QFile f( "f.txt" );
if( !f.exists() )
{
// It does not exist
std::cout << "The file does not exist." << std::endl;
return 0;
}
// It exists, open it
if( !f.open( IO_ReadOnly ) )
{
// It could not open
std::cout << "Failed to open." << std::endl;
return 0;
}
// It opened, now we need to close it
std::cout << "It worked." << std::endl;
f.close();
return 0;
}

 

$ ./qfile

The file does not exist.

$ touch f.txt

$ ./qfile

It worked.

$ chmod -r f.txt

$ ./qfile

Failed to open.

Streams

Um arquivo por si só não é muito útil. Possui alguns membros primitivos para leitura e escrita mas é usando uma classe de stream para fazer a leitura e/ou escrita o sistema torna-se mais fácil de usar e mais flexível. para manipular informação em texto use QTextStream. PAra dados binário QDataStream, mas esse capítulo foca apenas na manipulação de texto.

O leitor curioso pode já ter descoberto que as classes de stream se relacionam com a classe QFile pela classe IODevice. Isso significa que qualquer código que use um stream para ler ou escrever, por exemplo, um arquivo pode também ler de sockets, banco de dados, memória, etc. Isso torna possível a criação de um código bastante flexível e re-usável.

O QTextStream fornece os operadores << e >> para os tipos comuns. No segundo tutorial da Trolltech, no capitulo sobre Elementos de dados, seção entitulada “reading and Writing Data Elements”, é demonstrado como implementar esses operadores para qualquer classe ou estrutura usada.

Usando a propriedade do stream de texto a formatação da saída pode ser controlada. Por exemplo, a base (binária, octal, decimal, hexadecimal) dos números da saída podem ser controlados.

Finalmente, o método atEnd() retorna um valor booleano que indica se existe mais dados na fonte de dados em uso.

O exemplo abaixo mostra como usar um stream de texto em combinação com um arquivo. Em seguida, é mostrada uma sessão do terminal onde a aplicação é executada.O comando ‘wc -l’ conta o número de linhas de um arquivo dado.


#include <qapplication.h>
#include <qfile.h>
#include <qtextstream.h>
#include <iostream>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QFile f( "f.txt" );
if( !f.open( IO_WriteOnly | IO_Append ) )
{
std::cout << "Failed to open file." << std::endl;
return 0;
}
QTextStream ts( &f );
ts << "This is a test-run." << endl;
f.close();
return 0;
}

$ wc -l f.txt

wc: f.txt: No such file or directory

$ ./stream

$ wc -l f.txt

1 f.txt

$ ./stream

$ wc -l f.txt

2 f.txt

Caminhos de dirtórios

Plataformas diferentes possuem maneiras diferentes de especificar caminhos. O Widows possui drives e pastas, como c:\foo\bar, enquanto o UNIX e o MacOX (que é derivado do UNIX), usa uma raiz única com as pastas, como /foo/bar. Elas também diferem no caractere usado para separar as pastas uma das outras. Para representar esses caminhos de uma forma independente de plataforma, o Qt fornece a classe QDir.

Quando estiver explorando um arquivo em um sistema de arquivos, deve-se ter um local para começar. Para esse propósito, o QDir possui alguns membros estáticos. Primeiro é o método current(), que simplesmente retorna uma representação do diretório atual. Depois, existe o método root() que retorna a raiz do drive atual. Para sistemas com diversos drives, existe o método drive() que retorna um conjunto de instâncias de QFileInfo() (não tente apagar o ponteiro retornado, ele pertence ao Qt). Finalmente, temos o método home() que aponta para o diretório do usuário atual.

Quando for se mover pelos diretórios, é possível começar por um dos método estáticos mencionados no parágrafo anterior ou por uma string. Cada string pode ser “limpa” usando o método cleanDirPath(). A existência de um caminho pode ser verificado usando o método exists(). Se um caminho relativo for fornecido, o método convertToAbs() faz a conversão para um caminho absoluto. Para se mover entre diretórios, existem os métodos cd() e cdUp(). Eles funcionam exatamente igual os comandos cd e ‘cd ..’ do bash/cmd/dos. Quando o método isRoot() retorna TRUE não existe uso para o comando cdUp().

Pode ser útil se mover entre os diretórios, mas é mais útil ainda fazer alguma coisa. O método mkdir() tenta criar um diretório. Ele retorna TRUE se conseguir, ou FALSE em caso contrário. Os métodos rmdir() e rename() também retornam TRUE se conseguirem remover e renomear o diretório, respectivamente. O método rename() pode renomear arquivos também, mas para remover arquivo o método remove() é usado.

Quando estiver se movendo pelo sistema de arquivos, e atuando em partes dele, é importante saber quais opções existem. Antes de partir para interagir com o conteúdo do diretório atual, existem dois métodos a serem discutidos.

Em primeiro lugar, temos setSorting(). Esse método torna possível controlar em qual ordem as entradas irão aparecer. O método é controlado por opções OR que juntas formam um conjunto de flags. Existem quatro modos exclusivos que não podem ser combinados: QDir::Name, QDir::Time, QDir::Size e Unsorted. Além desses, existem alguns modificadores para ignorar um caso específico, colocar diretórios primeiro, etc. Eles são detalhados aqui.

Existe também o método setFilter. Esse método controla os itens que serão mostrados quando o conteúdo do diretório estiver sendo iterado. As opções são colocadas juntas pelo atributo OR para formar um filtro. Como existem muitas opções, recomendo a leitura da documentação oficial.

O exemplo abaixo mostra um exemplo do uso do QDir. Ele simplesmente passa por todos os sub-diretórios do diretório atual e exibe-os no terminal.


#include <qapplication.h>
#include <qdir.h>
#include <iostream>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QDir currentDir = QDir::current();
currentDir.setFilter( QDir::Dirs );
QStringList entries = currentDir.entryList();
for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry )
std::cout << *entry << std::endl;
return 0;
}

Observe que um QStringList é retornado. Para acessar seus membros um ConstIterator é usado. Isso funciona tão bem quanto seus equivalentes STL list<string> e list<string>::const_iterator e não serão discutidos aqui.

Mais informações sobre Arquivos e Diretórios

O setFilter pode ser útil para localizar entradas no diretório que sejam interessantes mais existe muito mais informações associadas com cada entrada. Ao invés de usar entryList() pode ser usado entryInfoList(). Isso retorna uma lista de instâncias de QFileInfo que guardam todas as informações associadas a cada entrada. Uma coisa que vale a pena notar é que a lista retornada pela chamada de entryInfoList pertence ao objeto QDir e não pode ser deletada. A lista retornada é reutilizada na próxima chamada aos mesmos métodos, assim, se a lista precisa ser usada depois precisa ser copiada.

Que informação cada entrada de QFileInfo contém? Em primeiro lugar, podemos saber o tipo da entrada usando isDir, isFile e isSymLink. Esses métodos retornam um valor valor booleano que é TRUE se a entrada referida for o que o nome do método indica.

Também é interessante saber como a entrada é chamada. Para esse propósito existem vários métodos. Primeiro, o caminho pode ser conhecido usando filePath(). O caminho absoluto pode ser dado pelo método absFilePath(). O nome do arquivo pode ser encontrado usando o fileName() enquanto o nome base (sem extensão) e a extensão podem ser encontradas usando baseName() e extension(), respectivamente.

Também é interessante saber os atributos de uma entrada. Para isso. os métodos booleanos isReadable(), isWriteable(), isExecutable() e isHidden() existem.

Ainda existe informações sobre data/hora relacionadas a entrada. Essa informação é acessado pelos métodos created(), lastModified() e lastRead(). Esses métodos retornam instâncias de QDateTime. Essa classe pode ser convertida para objetos QString usando o método toString().

O exemplo abaixo demonstra como obter um conjunto de objetos QFileInfo a partir de um objeto QDir e percorrer todas elas.


#include <qapplication.h>
#include <qdir.h>
#include <iostream>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QDir currentDir = QDir::current();
currentDir.setFilter( QDir::Files );
const QFileInfoList *infos = currentDir.entryInfoList();
const QFileInfo *info;
QFileInfoListIterator infoIt( *infos );
while( (info=infoIt.current()) != 0 )
{
std::cout << "Base name: " << info->baseName( TRUE ) << std::endl;
std::cout << "Extension: " << info->extension( TRUE ) << std::endl;
std::cout << "Created: " << info->created().toString() << std::endl;
++infoIt;
} }

Sumário

Não existe código para ser baixado para esse capítulo.

Traduzido de http://www.digitalfanatics.org/projects/qt_tutorial/chapter08.html

  • KodaiMagneto

    Cara por favor me ajude .. é o seguinte,eu criei um arquivo .txt binário em C no CodeBlocks.Eu criei um qrc no QT desse arquivo (salvando na pasta do projeto etc),o programa abre o arquivo mas somente quando utilizo esta rotina:

    QFile myFile(":/filmes.txt");

    if(myFile.open(QIODevice::ReadOnly))
    {
    QTextStream in(&myFile);
    while( !in.atEnd() )
    {
    QString line = in.readLine();
    ui->textEdit->setPlainText(line);
    }
    }

    Ele ler linha por linha e de fato imprime no textEdit que quero,só que como está em binário ele imprimiu o "lixo".O que quero é ler esse arquivo binário depois colocar o conteúdo em um QString e depois passar para o textEdit.

    Se puder me ajudar ficarei realmente grato.

    • Olá Filipe,
      Não sei entendi 100% o que você quer. Quando se gera um binário, não se obtém \”lixo\” e sim código de máquina (zeros e uns), que ao serem visualizados em um editor de texto, aparece um monte de caracteres sem sentido. Qual sua intenção em ler esse arquivo? Se for para executar um binário de dentro de outro, acredito que existam funções para isso (tem que pesquisar um pouco, não sei exatamente quais são, mas tenho certeza que existe algo para fazer isso).
      Atenciosamente,
      Kleber