Escolar Documentos
Profissional Documentos
Cultura Documentos
Maio de 2009
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
ÍNDICE:
1. INTRODUÇÃO....................................................................................................................................... 1
4. BIBLIOGRAFIA .................................................................................................................................. 35
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
1. Introdução
O Visual Basic em geral e a versão 2008, em particular, possui um conjunto de
características que visam ajudar tanto o programador principiante como o experiente a
desenvolver aplicações de forma simples, rápida e divertida.
Neste contexto e no âmbito do módulo Estruturas de Dados Compostas, pretende‐se
que, no final da frequência do módulo, os alunos sejam capazes de:
• compreender a importância das estruturas compostas na programação de
computadores;
• saber quando usar/definir tipos baseados em estruturas;
• analisar problemas, conceber algoritmos através de uma abordagem modular
descendente e desenvolver os algoritmos através de uma metodologia de
programação procedimental estruturada e recorrendo a dados estruturados;
• reconhecer a importância da utilização de vectores de estruturas e ficheiros
binários;
• reutilizar dados estruturados (estruturas, vectores de estruturas, ficheiros
binários);
• compreender os algoritmos de manipulação de vectores de estruturas e
ficheiros binários;
• codificar os programas em Visual Basic e testar as aplicações resultantes.
Refira‐se que nos exemplos apresentados as aplicações são essencialmente do tipo
consola considerando‐se ser esta a melhor forma de se aprender uma linguagem de
programação, uma vez que o objectivo principal está nos elementos da linguagem.
2. Conceitos Gerais
System é o namespace de nível hierárquico mais alto da plataforma .NET possuindo
uma quantidade muito significativa de membros, tais como: classes, estruturas, interfaces
e enumerações. Contém ainda outros namespaces, como por exemplo System.Lin,
System.Collections e System.IO.
O tipo de dados Object é classe base de toda a hierarquia da plataforma .NET e do
Visual Basic: todos os outros tipos de dados (estruturas e classes) derivam de e podem ser
convertidos para um Object.
System.Collections contém várias classe que definem diversas colecções de objectos,
incluindo listas, pilhas, filas e dicionários.
‐ 1 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
System.IO possui classes que permitem ler e escrever para ficheiro e streams e outras
que disponibilizam suporte a ficheiros e directórios.
3. Estruturas de Dados Compostas
Estruturas de dados e algoritmos são temas fundamentais da ciência da computação,
sendo utilizados nas mais diversas áreas do conhecimento e com os mais diferentes
propósitos de aplicação. Sabe‐se que algoritmos manipulam dados. Quando estes dados
estão organizados de forma coerente, caracterizam uma forma, uma estrutura de dados.
São a organização e os métodos que manipulam esta determinada estrutura que lhes
conferem singularidade. As estruturas de dados são chamadas tipos de dados compostos
que se dividem em dois: homogéneos (vectores e matrizes) e heterogéneos (colecções,
estruturas). As estruturas homogéneas referem‐se a conjuntos de dados formados pelo
mesmo tipo de dado primitivo. As estruturas heterogéneas são conjuntos de dados
formados por tipos de dados primitivos diferentes (colecções, estruturas). A escolha de
uma estrutura de dados apropriada pode tornar um problema complicado num de
solução relativamente trivial. A área de estudo das estruturas de dados está em
permanente desenvolvimento, assim como o de algoritmos.
Podemos definir dados como os elementos sobre os quais serão efectuadas operações
e tipo de dado como o conjunto de valores ao qual pertence um dado. Exemplos de tipos
de dados referem‐se: inteiro, real, caracter, etc.
Várias e diferentes classificações surgem na literatura para os tipos de dados. Uma que
os classifica em: primitivos (não podem ser decompostos, por exemplo: inteiro, real,
lógico e caracter) e derivados (definidos a partir dos tipos primitivos, por exemplo:
vetores, matrizes, registos, etc.).
Os tipos de dados derivados podem ainda ser classificados em homogéneos (agrupam
dados primitivos do mesmo tipo, por exemplo: vectores, strings e matrizes) ou
heterogéneos agrupam dados primitivos de tipos diferentes, por exemplo as colecções e
as estruturas.
Os tipos de dados podem ainda ser classificados em: estáticos ou dinâmicos. Os tipos
de dados estáticos têm tamanho sempre finito, por exemplo: tipos primitivos, colecções,
vectores e matrizes. Os tipos de dados dinâmicos têm um tamanho que pode variar
durante o seu tempo de vida, referem‐se: às pilhas, às filas, às listas, deques e árvores.
Assim sendo, define‐se uma estrutura de dados como um tipo derivado concebido com
o objectivo de ser manipulado de forma sistemática por algoritmos.
Surgem neste âmbito as colecções de dados que correspondem a estruturas de dados
que armazenam zero ou mais elementos (dados) relacionados e que podem ser
manipulados como um conjunto. Uma colecção deve disponibilizar operações para:
adicionar dados, remover dados, actualizar dados e definir valores.
‐ 2 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
As colecções de dados podem ainda ser classificadas em duas categorias: lineares (nas
quais todos os elementos contidos têm um único sucessor e um único antecessor) e não
lineares (os elementos não mantêm uma relação sequencial, mas sim outro tipo de
ordem entre si, como por exemplo, hierárquica). Enquanto numa colecção linear os
elementos estão normalmente ordenados sequencialmente pela posição (primeiro,
segundo, terceiro, etc.), numa colecção não linear os elementos não possuem uma ordem
posicional. Dois exemplos de colecções lineares no mundo real são os sócios de um
qualquer clube ou um dicionário de palavras, as quais podem ser modeladas através de
vectores ou uma List, ou ainda uma HashTable ou uma SortedList. Por outro lado,
também no mundo real, um organograma de uma empresa ou um conjunto de produtos
são exemplos de colecções não lineares (o primeiro exemplo poderia ser modelado com
uma árvore e o segundo com conjuntos).
Figura 1 – Estruturas de Dados Lineares
As colecções lineares podem ainda ser classificadas: de acesso sequencial e acesso
directo. As filas (Queue) e as pilhas (Stack) são exemplos de colecções de acesso
sequencial. Os vectores (Array) e as listas ordenadas (SortedList) são exemplos de
colecções de acesso directo.
Neste documento teremos por objectivo caracterizar estruturas de dados compostas
lineares de acesso directo, nomeadamente, vectores de estruturas e ficheiros. Assim,
abordaremos as principais estruturas de dados associadas a colecções da plataforma
.NET, para além de abordar a classe Collection, definida no namespace
Microsoft.VisuaIBasic.
Por norma, as variáveis são de um só tipo (numérico, texto, booleano, etc.) mas por
vezes surge a necessidade de associar numa variável tipos de dados distintos. Neste caso
define‐se um tipo de dado composto. Considerando que este documento pretende ser
um texto de apoio ao módulo de “Estruturas de Dados Compostas” usaremos vectores
de estruturas e ficheiros.
‐ 3 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Os ficheiros surgem como uma colecção externa de dados que tem associada uma
estrutura de dados designada por "stream". Os ficheiros são usados para guardar grandes
volumes de informação.
Suponha que para um registo de um carro, é necessário guardar as seguintes
informações: matricula, marca, ano de fabrico e o proprietário. Em vez de utilizar quatro
variáveis distintas (Matricula, Marca, Ano_Fabrico, Proprietario), pode‐se criar um tipo
composto de variáveis como o exemplo abaixo:
TIPO regCarro
Matricula texto
Marca texto
Ano_Fabrico numero
Proprietario texto
FIM TIPO
3.1. Estruturas
Uma estrutura é uma variável que pode conter tipos de dados diferentes ou ainda é
um tipo de dado cujo formato é definido pelo programador. Uma estrutura fornece uma
forma conveniente de agrupar numa unidade um conjunto de variáveis de diferentes
tipos.
Uma estrutura é uma generalização do tipo definido pelo utilizador suportado por
versões anteriores do Visual Basic. Em adição aos campos, as estruturas podem expor
propriedades, métodos e eventos. Uma estrutura pode implementar uma ou mais
interfaces, e é possível declarar níveis de acesso individuais para cada campo. Pode
combinar itens de tipos diferentes para criar uma estrutura de dados. Uma estrutura
associa um ou mais elementos entre eles e com a estrutura em si.
Estruturas são úteis quando se deseja que uma única variável armazene várias
componentes relacionadas de informação. Por exemplo, convém manter o nome, a
categoria e o salário de um funcionário juntos. Poderia usar diversas variáveis para essas
informações, ou você poderia definir uma estrutura e usá‐la para uma única variável
funcionário. A vantagem da estrutura torna‐se aparente quando você tem muitos
funcionários e, portanto, muitas instâncias da variável.
3.1.1. Declaração de uma estrutura
Os elementos individuais duma estrutura podem ser variáveis normais, vectores,
matrizes ou outras estruturas. Os nomes dos elementos dentro duma estrutura devem
ser distintos, apesar do nome de um elemento poder ser igual ao nome duma variável
qualquer fora da estrutura.
‐ 4 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
A declaração de estruturas é feita obrigatoriamente fora de qualquer subrotina (na
zona de declarações). Os elementos de uma estrutura podem ser de qualquer tipo,
incluindo vectores.
Quando um elemento de uma estrutura é do tipo vector, a dimensão do vector não
deve ser definida na declaração da estrutura; a indicação da dimensão deve ser deixada
para mais tarde, devendo usar‐se para o efeito a instrução ReDim.
Ao declarar cada elemento pode ser especificado um nível de acesso. Por defeito a
acessibilidade é Public.
Sintaxe:
Structure tipoEstrutura
Dim nomeMembro1 As tipoMembro1
Dim nomeMembro2 As tipoMembro2
. . .
End Structure
Exemplo:
Structure systemInfo
End Structure
3.1.2. Declaração das variáveis
Depois de criada uma estrutura, é possível declarar variáveis no nível de procedimento
e no nível de módulo como esse tipo. Se se pretender que uma estrutura seja privada,
esta deve ser declarada usando a palavra‐chave Private.
Sintaxe:
Dim nomeVar As tipoEstrutura
Exemplo:
Dim sistema as systemInfo
‐ 5 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
3.1.3. Acesso a valores de uma estrutura
Para atribuir e recuperar valores de elementos de uma variável do tipo estrutura deve
usar‐se o operador de acesso do membro (.) entre o nome da variável de estrutura e o
nome do elemento da seguinte forma:
Sintaxe:
nomeVar.nomeMembro
Exemplo:
mySystem.cPU = "486"
Dim tooOld As Boolean
If yourSystem.purchaseDate < #1/1/1992# Then tooOld = True
3.1.4. Passagem de estruturas com argumentos de Funções ou Procedimentos
É possível passar uma estrutura como um argumento de um procedimento ou função,
como se ilustra a seguir:
Dim currentCPUName As String = "700MHz Pentium compatible"
No cabeçalho da subrotina especifica‐se da seguinte forma:
Public Sub fillSystem(ByRef someSystem As systemInfo)
someSystem.cPU = currentCPUName
someSystem.memory = currentMemorySize
someSystem.purchaseDate = Now
End Sub
Na chamada à subrotina deve indicar‐se fillSystem (system_info)
O exemplo anterior passa a variável estrutura por referência, o que permite que o
procedimento modifique os seus elementos de modo que as alterações tenham efeito no
ponto de chamada.
‐ 6 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
3.1.5. Tipo retornado por uma função
Pode‐se retornar uma estrutura a partir de uma Function, como se ilustra a seguir:
Dim allSystems(100) As systemInfo
Dim i As Integer
For i = 1 To 100
allSystems(i)
Next i
' Process error: system with desired purchase date not found.
End Function
3.1.6. Estruturas Encaixadas
Estruturas podem conter outras estruturas, como se ilustra a seguir:
Structure driveInfo
type As String
size As Long
End Structure
Structure systemInfo
cPU As String
memory As Long
diskDrives() As driveInfo
purchaseDate As Date
End Structure
ReDim allSystems(1).diskDrives(3)
allSystems(1).diskDrives(0).type = "Floppy"
‐ 7 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
3.2. Vectores de Estruturas
Os vectores são estruturas de dados lineares e estáticas, isto é, são compostos por um
número fixo (finito) de elementos de um determinado tipo de dados. O tempo de acesso
aos elementos de um vector é muito rápido, sendo considerado constante: o acesso aos
elementos é feito pelo seu índice no vector. Porém, a remoção de elementos pode ser
penalizadora se não for desejável que haja espaços "vazios" no meio do vector, pois nesse
caso é necessário "arrastar" de uma posição todos os elementos depois do elemento
removido. Essa é uma estrutura muito recomendada para casos em que os dados
armazenados mudam pouco ao longo do tempo.
Refira‐se no entanto a possibilidade da implementação de vectores dinâmicos. Assim
sendo, é possível distinguir:
• Vector estático contem um número fixo de elementos e é alocado durante o
tempo de compilação.
Dim a(10) As Integer
• Vector dinâmico é criado usando técnicas de alocação e gestão dinâmica de
memória e pode ser redimensionado.
Dim a() As Integer
ReDim a(9)
Como referido anteriormente os elementos de um vector podem ser de qualquer tipo,
incluindo estruturas. Assim como os elementos de uma estrutura podem ser de qualquer
tipo, incluindo vectores ou matrizes.
Quando um elemento de uma estrutura é do tipo vector, a dimensão do vector não
deve ser definida na declaração da estrutura; a indicação da dimensão deve ser deixada
para mais tarde, devendo usar‐se para o efeito a instrução ReDim, como se ilustra de
seguida:
Structure systemInfo
cPU As String
memory As Long
diskDrives() As String
purchaseDate As Date
End Structure
Dim mySystem As systemInfo
ReDim mySystem.diskDrives(3)
‐ 8 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Dim allSystems(100) As systemInfo
ReDim allSystems(5).diskDrives(3)
allSystems(5).CPU = "386SX"
3.3. Streams
É uma colecção externa de dados que tem associada uma estrutura de dados
designada por "stream". Os ficheiros são usados para guardar grandes volumes de
informação. A operação de leitura retira dados de um "stream" de entrada. A operação
de escrita junta novos dados ao fim de um "stream" de saída [1].
Object
Stream TextReader
TextWriter
BufferedStream NetworkStream
Legenda:
IdentedTextWriter HtmlTextWriter
Bytes Caracteres
Figura 2 ‐ Hierarquia de Streams, Readers e Writers
Um stream é uma abstracção de uma sequência de bytes como, por exemplo, um
ficheiro, um dispositivo de input/output, dados numa rede ou mesmo um vector de
dados armazenados em memória. A vantagem desta abstracção é que é possível escrever
na janela de consola ou num ficheiro de forma idêntica. Esta secção aborda a
entrada/saída (Input/Output ‐ I/O) em aplicações de consola e com ficheiros e descreve
algumas das classes e funcionalidades .NET associadas mais importantes que permitem
manipular streams.
‐ 9 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
A plataforma .NET possui uma grande variedade de classes para manipular diversos
tipos de streams. Por exemplo, é possível manipular streams como uma sequências de
bytes (típico de um computador), ou ainda trabalhar com classes de mais alto nível que
trabalham com caracteres.
3.3.1. O namespace System.IO
O namespace System.IO é onde residem as classes que permitem ler e escrever de
streams e ficheiros, para além de classes que permitem realizar operações básicas sobre
ficheiros, directórios e caminhos para ficheiros e directórios.
Para se usar estas classes deverá incluir a seguinte declaração no início do programa:
Imports System.IO
Tabela 1 ‐ Classe System.IO
Classe Descrição
BinaryReader e BinaryWriter Estas classes permitem ler e escrever tipos de dados
básicos (Integer, Double, Long, String, ...) como valores
binários com uma codificação específica.
Directory Disponibiliza métodos de classe (Shared) para criar,
mover e aceder a directórios e subdirectórios.
DirectoryInfo Disponibiliza métodos de instância para criar, mover e
aceder a directórios e subdirectórios .
DriveInfo Disponibiliza acesso a informação sobre drives.
File Disponibiliza métodos de classe (Shared) para criar,
copiar, eliminar, mover e abrir ficheiros, e ajuda na
criação de objectos FileStream.
Filelnfo Disponibiliza métodos de instância para criar, copiar,
eliminar, mover e abrir ficheiros, e ajuda na criação de
objectos FileStream.
FileSystemlnfo Representa a classe base para as classes Filelnfo e
Directorylnfo.
Path Realiza operações em instâncias do tipo string que
contêm informação sobre um caminho (path), associado
a ficheiros ou directórios
Stream A classe Stream é a classe da qual derivam todos os
streams. Um stream é uma abstracção de uma sequência
de bytes.
‐ 10 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
StreamReader e Implementam, respectivamente, um objecto do tipo
StreamReader TextReader e TextWriter que permitem ler e escrever
num stream (ficheiro) de caracteres.
StringWriter e StringReader Implementam, respectivamente, um objecto do tipo
TextReader e TextWriter que permitem ler e escrever
num stream de caracteres em memória.
TextReader e TextWriter Representam as classes base para as classes de input
StreamReader e StringReader e output StreamWriter e
StringWriter.
O namespace System.IO define um importante conjunto de enumerações, sendo as
mais importantes apresentadas na tabela seguinte.
Tabela 2 ‐ Enumerações de System.IO
Enumeração Descrição
FileAccess Define constantes para ler, escrever, ou ler e escrever de um ficheiro.
Usada em construtores das classes File, Filelnfo e FileStream.
FileAtributes Define atributos para ficheiros e directórios (usada com as funções Dir,
GetAttr e SetAttr do Visual Basic) .
FileMode Um parâmetro FileMode é usado em vários construtores da classe
FileStream e nos métodos Open das classes File e Filelnfo, especificando
como um ficheiro é aberto.
FileOptions Especifica constantes de ficheiros avançadas, usadas na construção de
um objecto FileStream.
FileShare Especifica constantes que controlam o tipo de acesso que outros
objectos FileStream podem ter ao mesmo ficheiro.
Usado no construtor de um FileStream.
SearchOption Possui constantes que especificam se a pesquisa deve ocorrer no
directório corrente ou no directório corrente e seus sub‐directórios.
Usada no método GetDirectories de objectos Directorylnfo.
SeekOrigin Usada no método Seek de diversas classes, esta enumeração possui
campos que representam os pontos de referência para posicionamento
de streams.
Associado à leitura e escrita em ficheiros está o conceito de programação robusta.
Sempre que tentar abrir um ficheiro para leitura e/ou escrita ou tentar aceder a detalhes
‐ 11 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
associados a ficheiros e directórios, é necessário usar o bloco Try...Catch para recolher as
excepções associadas a estas operações. O namespace System.IO possui diversas classes
de excepção, algumas das quais são apresentadas na tabela seguinte.
Tabela 3 ‐ Classe de excepção de System.IO
Excepção Descrição
DirectoryNotFoundException Esta excepção é lançada quando parte de um ficheiro ou
directório não é encontrado.
DriveNotF ound Exception Esta excepção é lançada quando se tenta aceder a um
drive que não está disponível.
EndOfStreamException Esta excepção é lançada quando se tenta ler depois do fim
do ficheiro.
FileFormatException Esta excepção é lançada quando um stream ou ficheiro de
input não se encontra de acordo com um determinado
formato .
FileNotFoundException Esta excepção é lançada quando se tenta aceder a um
ficheiro que não existe ou ocorre uma falha no disco.
InvalidDataException Esta excepção é lançada quando um stream de dados
possui um formato inválido.
IOException Esta excepção é lançada quando existe um problema
genérico de input/output.
PathTooLongException Esta excepção é lançada quando um nome de caminho ou
nome de ficheiro excede o número de caracteres máximo
definido pelo sistema
A hierarquia das excepções anteriores pode ser resumida pela seguinte estrutura:
• System.Exception
System.SystemException
System.FileFormatException
System .lnvalidOataException
System.IOException
• System.IO.OirectoryNotFoundException
• System.IO.OriveNotFoundException
• System.IO.EndOfStreamException
• System.IO.FileNotFoundException
• System.IO.PathTooLongException
‐ 12 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
De salientar que o bloco Try...Catch permite testar mais do que uma excepção, por
forma a executar código de acordo com a excepção lançada. No entanto, apenas um
bloco Catch é executado. Por outras palavras, caso se use outras excepções que não
apenas a classe base da qual todas as excepções derivam (System.Exception) deve‐se
começar por testar excepções mais específicas (como, por exemplo,
System.IO.FileNotFoundException), testando depois as menos específicas (por exemplo,
System.IOException) e só por último as mais genéricas (por exemplo,
System.SystemException ou então System.Exception).
Antes de se entrar em mais detalhes sobre as operações de I/O sobre streams e
ficheiros, é importante salientar que existem streams baseados em bytes e streams
baseados em caracteres. Por um lado, muitos dispositivos são orientados ao byte (8 bits)
no que diz respeito às operações de I/O. Por outro, os seres humanos comunicam através
de caracteres, sendo que um Char ocupa 16 bits (2 bytes).
3.3.2. Classe System.IO.Stream
A classe Stream é a classe base para todos os streams de bytes. Um stream é uma
abstracção de uma sequência de bytes como, por exemplo, um ficheiro ou um dispositivo
de input/output. A classe Stream e as classes derivadas (BufferedStream, FileStream e
MemoryStream, por exemplo), encapsulam conceitos associados a diferentes tipos de
input/output, escondendo detalhes ao programador sobre aspectos específicos do
sistema operativo ou dos dispositivos manipulados.
O conceito de Stream envolve três operações fundamentais:
1. Leitura (read) ‐ A leitura de streams consiste na transferência de dados de um
stream para uma estrutura de dados como, por exemplo, um vector de bytes.
2. Escrita (write) ‐ A escrita em streams consiste na transferência de dados de uma
estrutura de dados para um stream.
3. Posicionamento (seek) ‐ Os streams podem permitir a definição de um valor relativo
à posição corrente dentro de um stream (posição para leitura/escrita). Esta capacidade
depende do tipo de armazenamento permanente associado ao stream.
Dependendo do dispositivo de input/output associado, os streams podem suportar
apenas algumas destas capacidades. Uma aplicação pode e deve testar estas capacidades
através das propriedades CanRead, CanWrite, e CanSeek. Estas propriedades retornam
um valor Boolean que indica se o stream permite realizar as três operações acima
descritas.
Para ler e escrever em streams dados de diversos formatos usam‐se os métodos Read
e Write. No caso do stream permitir definir a posição corrente, para acesso directo aos
‐ 13 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
dados, usam‐se os métodos Seek e SetLength e as propriedades Position e Length para
retornar e definir a posição e o comprimento do stream.
Um conceito importante associado a alguns streams é a técnica de buffering. Esta
técnica utiliza uma área de armazenamento temporário (por exemplo, a memória RAM)
para melhorar o tempo de resposta de alguns streams (como, por exemplo, ficheiros em
disco rígido). Para este tipo de stream, o método Flush pode ser usado para enviar todos
os dados existentes na memória temporária (buffers) para o dispositivo de
armazenamento permanente.
Por último, depois de se utilizar um stream deve‐se chamar o método Close. Este
método faz o flush dos dados armazenados nos buffers e liberta recursos associados ao
sistema operativo. A tabela seguinte apresenta uma selecção dos principais métodos da
classe Stream.
Tabela 4 ‐Métodos da classe Stream
Método Descrição
BeginRead Inicia uma leitura assíncrona.
BeginWrite Inicia uma escrita assíncrona.
Close Fecha o stream, fazendo, se necessário, o flush de todos os buffers,
e libertando os recursos associados.
EndWrite Espera que uma escrita assíncrona termine.
EndRead Espera que uma leitura assíncrona termine.
Flush Envia todos os dados armazenados nas áreas de armazenamento
temporário (buffers) para o dispositivo de armazenamento
associado (ficheiro, memória, etc.).
Read Lê bytes do stream e avança a posição corrente de acordo com o nº
de bytes lidos.
ReadByte Lê um byte dostream e avança a posição corrente um byte.
Seek Define a posição corrente do stream. Nem todos os streams
suportam esta funcionalidade.
SetLength Define o cumprimento do stream truncando ou aumentando o
comprimento do stream, de acordo com o tamanho actual. Este
método só é suportado caso o stream suporte tanto a escrita como
o posicionamento .
Write Escreve bytes no stream e avança a posição corrente de acordo com
o número de bytes escritos .
WriteByte Escreve um byte no stream e avança a posição corrente um byte.
‐ 14 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Na tabela seguinte são apresentadas as principais propriedades da classe Stream.
Tabela 5 ‐ Propriedades da classe Stream
Propriedade Descrição
CanRead Retorna um Boolean que indica se o stream suporta leitura.
CanSeek Retorna um Boolean que indica se o stream suporta a definição da
posição corrente para acesso directo.
CanWrite Retorna um Boolean que indica se o stream suporta escrita.
Length. Retorna o número de bytes do stream
Position Retorna um valor que representa a posição corrente do stream. Se o
stream permitir o posicionamento, esta propriedade é de escrita,
permitindo o posicionamento numa determinada posição dentro do
stream.
3.3.3. Entrada/Saída da Consola
A classe Console tem vindo a ser usada desde os primeiros exercícios práticos,
nomeadamente o método de entrada ReadLine e os métodos de saída Write e WriteLine.
A classe Console reside no namespace System e não em System.IO [1].
Uma vez que o namespace System é importado automaticamente como parte da
configuração do ambiente de desenvolvimento integrado, não é necessário preceder o
nome da classe Console com o namespace a que pertence.
Apesar de se ter vindo a usar a classe Console numa grande parte dos programas as
aplicações reais são, de uma forma geral, do tipo Windows, com uma interface gráfica
rica, e não do tipo consola, baseadas em texto. A razão de se utilizar aplicações de
consola tem a ver com o facto de serem mais adequadas como exemplos ilustrativos do
funcionamento da linguagem Visual Basic.
Uma aplicação do tipo consola realiza o input/output com base em três streams
(conhecidos como "standard streams"): um stream para a entrada de dados (input), um
stream para a saída de dados (output) e um outro ainda para o envio de mensagens de
erro. As propriedades da classe Console que retornam estes streams são,
respectivamente, In, Out e Error.
A propriedade In é uma instância do tipo TextReader enquanto que as propriedades
Out e Error são instâncias do tipo TextWriter. Consequentemente, é possível aceder aos
métodos e propriedades definidos nestas duas classes (Console.In.ReadLine e
Console.Out.WriteLine). Contudo, a situação mais comum é usar‐se os métodos
‐ 15 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
disponibilizados pela classe Console, que automaticamente manipulam as propriedades
In, Out e Error (entre outros, Console.ReadLine e Console.WriteLine).
A necessidade de um terceiro stream, o stream de erro (propriedade Console.Error)
tem a ver com o facto de, em algumas aplicações, ser importante existirem streams
distintos para o output e para o envio de mensagens de erro.
A entrada ou leituta de dados com a classe Console é conseguido com três métodos:
Read, ReadLine e ReadKey. Os métodos Read e ReadLine lêem, respectivamente, o
próximo carácter e uma linha de caracteres do stream de standard input. O método
ReadKey lê/retorna o próximo carácter ou tecla de função pressionada pelo utilizador
(permite saber também se as teclas modificadores Ctrl, Shift ou Alt estavam
pressionadas). O programas seguinte ilustra o funcionamento dos métodos Read,
ReadLine e ReadKey.
Exemplo 1: método Read()
Module Module1
Sub Main()
' Para ler um único carácter use o método Read():
' Public Shared Function Read() As Integer.
Console.Write("Pressione um carácter seguido de ENTER: ")
' Lê um carácter e converte-o para Char.
Dim ch As Char = ChrW(Console.Read())
Console.WriteLine("O carácter pressionado foi: " & ch)
Console.Write("Pressione outro carácter seguido de ENTER: ")
' O carácter lido será SEMPRE o "carriage return" (CR).
ch = ChrW(Console.Read())
Console.WriteLine("O carácter pressionado foi: " & ch)
Console.Write("Pressione outro carácter seguido de ENTER: ")
' O carácter lido será SEMPRE o "line feed" (LF).
ch = ChrW(Console.Read())
Console.WriteLine("O carácter pressionado foi: " & ch)
Console.ReadKey()
End Sub
End Module
Como referido, o método Read lê o próximo carácter (um único) do stream de input.
No entanto, para que a leitura seja efectivamente realizada, é necessário pressionar a
tecla Enter. Uma vez que a tecla Enter representa dois caracteres (o "carriage return" ‐
CR, e o "line feed" ‐ LF), quando se tenta ler um único carácter com o método Read o
stream de input lê na realidade três caracteres. Se, de seguida, tentar ler mais caracteres
com o método Read, os caracteres lidos são, respectivamente, o CR e LF. O programa
‐ 16 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
seguinte ilustra este “problema” do método Read (de realçar que o método Read retorna
um Integer; é necessário converter o código Unicode para um Char com a função ChrW).
O programa anterior, apresentado no exemplo 1, gera um output parecido com o
seguinte:
Note que, aparentemente, apenas um carácter foi pressionado (neste caso, o carácter
"s"). No entanto, como a tecla Enter representa os caracteres CR e LF, o stream de input
possui na realidade três caracteres. O primeiro carácter é o "s" mas nas outras duas
leituras de caracteres com o método Read aparentemente não foram lidos caracteres, o
que não é correcto. Os caracteres lidos foram, como referido, CR e LF.
O método ReadLine lê uma linha de caracteres até (excluindo) a tecla Enter. O tipo de
dados de retorno é uma String. Um exemplo de utilização deste método é o apresentado
no programa seguinte.
Exemplo 2: Ler uma string com o método Console.ReadLine()
Module Module1
Sub Main()
' Para ler uma string de caracteres use o método ReadLine():
' Public Shared Function ReadLine() As String.
' Lê uma string.
Console.Write("Introduza uma string: ")
Dim str As String = Console.ReadLine
Console.WriteLine()
Console.WriteLine("A string introduzida foi: " & str)
Console.ReadKey()
End Sub
End Module
‐ 17 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
A saída produzida deverá ser algo semelhante a:
Por último, o método ReadKey. Este método retorna um objecto do tipo
ConsoleKeylnfo (uma estrutura). Esta estrutura possui três propriedades que descrevem a
tecla da janela de consola que foi pressionada (o carácter e o estado das teclas
modificadoras Ctrl, Shift e Alt). O programa seguinte usa um ciclo controlado por uma
sentinela (tecla Esc), para mostrar o valor das três propriedades da estrutura
ConsoleKeyInfo para cada tecla pressionada.
Exemplo 3: Ler uma string: método Console.ReadLine()
Imports System.Text
' Classe StringBuilder.
Module Module1
Sub Main()
' Para ler um carácter ou tecla de função use o método ReadKey():
‘Public Shared Function ReadKey() As System.ConsoleKeyInfo
' Public Shared Function ReadKey(esconder As Boolean) As
System.ConsoleKeyInfo
Console.WriteLine("Pressione uma tecla (eventualmente com
Ctrl/Alt/Shift).")
Console.Write("ESC para terminar: ")
Dim ch As ConsoleKeyInfo = Console.ReadKey()
Do While ch.Key <> ConsoleKey.Escape
Dim sb As New StringBuilder("Pressionou: ")
' Verificar as teclas modificadoras (Ctrl/Alt/Shift).
If ch.Modifiers <> 0 Then
‐ 18 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
If (ch.Modifiers And ConsoleModifiers.Control) <> 0 Then
sb.Append("CTRL+")
End If
If (ch.Modifiers And ConsoleModifiers.Alt) <> 0 Then
sb.Append("ALT+")
End If
If (ch.Modifiers And ConsoleModifiers.Shift) <> 0 Then
sb.Append("SHIFT+")
End If
End If
' Mostrar qual a tecla e qual o carácter correspondente.
sb.Append(ch.Key.ToString())
sb.AppendFormat(" (carácter '{0}').", ch.KeyChar)
' Mostrar o resultado.
Console.WriteLine()
Console.WriteLine(sb.ToString())
' Ler outro carácter.
Console.WriteLine("Pressione uma tecla (eventualmente com
Ctrl/Alt/Shift).")
Console.Write("ESC para terminar: ")
ch = Console.ReadKey()
Loop
End Sub
End Module
3.3.4. Redireccionar o Standard Stream
Os standard streams da classe Console, associados às propriedades In, Out e Error
podem ser redireccionados. Por exemplo, se redireccionar os streams de input e output
para dois ficheiros em disco, o seu programa passará a ler (métodos Read, ReadLine e
ReadKey) do ficheiro especificado, enquanto que o output (métodos Write e WriteLine)
produzido será enviado também para o ficheiro especificado. Se redireccionar os três
streams o programa poderá, num outro exemplo, ler instruções de um ficheiro em disco,
enviar o output para uma ligação em rede e criar um ficheiro associado ao registo de
operações. Para se redireccionar os "standard streams" usam‐se os métodos SetIn,
SetOut e SetError da classe Console. Estes métodos definem, respectivamente, as
propriedades Console.In, Console.Out e Console.Error de acordo com o objecto
TextReader (para o método SetIn) ou TextWriter (para os métodos SetOut e SetError)
especificados. Para recuperar, isto é, anular o redireccionamento, existem os métodos
OpenStandardInput, OpenStandardOutput e OpenStandardError.
Como ilustrado no exemplo seguinte, uma operação como Console.WriteLine não
escreve necessariamente na janela de consola. Se redireccionar o output para um
‐ 19 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
ficheiro, como neste programa, o stream por omissão (a janela de consola) será ignorada
e o resultado será enviado para um ficheiro especificado. No final recupera‐se o stream
de standard output para que se possa novamente escrever na janela de consola.
Exemplo 4: Redireccionar o Standard Stream
Imports System.IO
Module Module1
Sub Main()
Try
' Cria um stream de output.
Dim swout As New StreamWriter("output.txt")
' Redirecciona o "standard output".
Console.SetOut(swout)
' Escreve no "novo" "standard output".
Console.WriteLine("{0,6}{1,6}{2,6}", "n", "n^2", "n^3")
For n As Integer = 1 To 10
Console.WriteLine("{0,6}{1,6}{2,6}", n, n ^ 2, n ^ 3)
Next
' Fecha o stream.
swout.Close()
Catch ex As Exception
Console.WriteLine("O ficheiro já existe e é só de leitura ou
o disco está cheio.")
End Try
Try
'Cria outro stream de output.
Dim swerr As New StreamWriter("log.txt")
'Redirecciona o "standard error".
Console.SetError(swerr)
Try
'A linha seguinte gera uma excepção.
Dim sr As New StreamReader("filenotfound.txt")
Catch ex As Exception
'Escreve no "standard error"(log.txt) a mensagem de erro.
Console.Error.WriteLine("Ocorreu um erro: " & ex.Message)
End Try
' Fecha o stream.
swerr.Close()
Catch ex As Exception
Console.WriteLine("O ficheiro já existe e é só de leitura ou
‐ 20 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
o disco está cheio.")
End Try
3.3.5. Classes StreamReader e StreamWriter
As classes StreamReader e StreamWriter encapsulam um stream orientado a
caracteres. Estas classes derivam, respectivamente, de TextReader e TextWriter. Os
principais métodos definidos na classe TextReader (e, consequentemente, nas classes
derivadas, como StreamReader e StringReader) são descritos na tabela seguinte:
Tabela 6 ‐ Métodos definidos na classe TextReader
Método Descrição
Close Fecha o objecto StreamReader e o stream associado.
Peek Retorna o carácter seguinte sem o remover do stream.
Read Lê o carácter seguinte ou um conjunto de caracteres seguintes.
ReadBlock Lê, no máximo, uma quantidade de caracteres especificados e
escreve os dados num buffer especificado, a partir de um índice
especificado.
ReadLine Lê uma linha de caracteres do stream corrente e retorna‐a como
uma string.
ReadToEnd Lê todos os dados do stream a partir da posição corrente até ao
final do stream.
Os principais métodos definidos na classe TextWriter e, consequentemente, nas
classes derivadas, como StreamWriter, são os listados na tabela seguinte:
‐ 21 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Tabela 7 ‐ Métodos definidos na classe TextWriter
Método Descrição
Close Fecha o objecto Stream associado.
Flush Força todos os dados armazenados em áreas temporárias (buffers) a
serem escritos no stream.
Write Overloaded. Escreve dados no stream.
Writeline Overloaded. Escreve dados no stream, seguidos de um new line.
Para iniciar um StreamWriter a partir de um nome de ficheiro com codificação e
tamanho de buffer por omissão e escrever uma string lida do teclado, pode usar‐se o
seguinte excerto de código:
Dim sw As New StrearnWriter("teste.txt")
sw.WriteLine(Console.ReadLine)
sw.Close()
A classe StreamWriter possui sete construtores. Por exemplo, é possível iniciar um
StreamWriter a partir de uma string, como no caso anterior, ou a partir de um objecto
Stream (FileStream). Existe ainda a possibilidade de combinar estas duas possibilidades
especificando três outros parâmetros comuns:
• O tipo de codificação (Encoding) desejada para o stream;
• Um valor Boolean que controla se se escreve por cima do stream, caso já
exista, ou, caso contrário, se se adiciona ao final do stream;
• Um valor Integer que especifica o tamanho do buffer.
Como realçado anteriormente, as operações de input/output devem ser realizadas, de
forma robusta, dentro de um bloco Try...Catch. Uma vez que as possibilidades de
anomalias (excepções) lançadas nas operações de input/output são diversificadas, o
código anterior ficaria mais robusto da seguinte forma:
Try
sw.WriteLine(Console.ReadLine)
‐ 22 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
sw.Close ()
Catch ex As Exception
End Try
Para se ler de um stream deve usar‐se a classe StreamReader. À semelhança do
construtor da classe StreamWriter, para se criar um objecto StreamReader é possível
passar o nome de um ficheiro (string) ou então um objecto FileStream. Englobe as
operações que podem lançar excepções dentro de um bloco Try ...Catch. O seguinte
fragmento de código abre um StreamReader e, usando um ciclo Do While ... Loop, lê
todo o seu conteúdo, linha a linha, mostrando‐as na janela de consola.
Dim str As String
‘Tenta abrir o ficheiro.
Try
Dim sr As New StreamReader{"teste.txt")
‘Tenta ler do ficheiro.
Try
str = sr.ReadLine
Do While str <> Nothing
Console.WriteLine{str)
str = sr.ReadLine
Loop
Console.WriteLine{"Erro na leitura." & vbNewLine & ex.Message)
‘Fecha o ficheiro.
sr.Close ()
Console.writeLine("Erro ao abrir/ler do ficheiro de leitura: " &
ex.Message)
Note como no fragmento de código anterior é usado um bloco Try ...Catch dentro de
outro bloco Try ...Catch. O primeiro trata a abertura do ficheiro. Caso a chamada ao
construtor gere um erro, é mostrada uma mensagem a indicar que houve um erro ao
abrir o ficheiro. Se não ocorrer uma anomalia, o programa continua e tenta ler do
ficheiro. Se for possível ler do ficheiro, o seu conteúdo é visualizado na janela de consola.
Contudo, caso haja um problema na leitura do ficheiro (impossibilidade de alocar
suficiente memória para o buffer ou então uma eventual leitura para além do fim do
ficheiro), é importante fechar o ficheiro (método Close), uma vez que houve sucesso na
sua abertura.
‐ 23 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
A questão a salientar é, portanto, a seguinte: as operações de abertura de um ficheiro
e a sua leitura (ou escrita) devem estar, normalmente, separadas em blocos Try ...Catch
distintos e encadeados, como ilustrado no exemplo anterior.
Os excertos de código anterior são resumidos pelo seguinte exemplo. O programa
escreve um conjunto de linhas num StreamWriter até que o utilizador pressione a tecla
Enter sem ter introduzido qualquer carácter. De seguida todo o conteúdo do ficheiro é
aberto como um objecto StreamReader e, através de um ciclo controlado pelo fim.
3.3.6. Classes BinaryReader e BinaryWriter
Ficheiros binários são simples sequências de bytes cuja estrutura interna não é
possível editar com um processador de texto. A sua estrutura interna é definida e
mantida pelo programador. Este tipo de ficheiros permite uma maior flexibilidade e
eficiência no armazenamento da informação.
As classes BinaryReader e BinaryWriter representam streams de dados binários,
permitindo, respectivamente, ler e escrever dados em formato binário e não num
formato passível de ser lido por seres humanos. Este é o terceiro tipo de streams definido
pela plataforma .NET, para além de streams de bytes e streams de caracteres.
A classe BinaryWriter define um único método que permite escrever todos os tipos de
dados básicos do Visual Basic: o método Write (não existe o método WriteLine). Para se
escrever num objecto BinaryWriter um Integer, um Long, um Double e um Decimal usa‐
se sempre o mesmo método mas com argumentos dos tipos de dados que se pretendem
escrever: o método Write está sobrecarregado (overloaded), existindo 18 versões do
método.
A classe BinaryReader define métodos que permitem ler todos os tipos de dados
básicos do Visual Basic. Por exemplo, para ler uma String, um Integer, um Long, um
Double e um Decimal usam‐se, respectivamente, os métodos ReadString, Readlnt32,
Readlnt64, ReadDouble e ReadDecimal. É possível também, com o método PeekChar, ler
um carácter sem contudo avançar uma posição (ou seja, o carácter é retornado mas
mantém‐se no stream).
Para criar um objecto BinaryReader ou BinaryWriter o construtor mais comum recebe
um objecto do tipo Stream (relembre que a classe Stream é uma classe abstracta, isto é,
MustInherit, não sendo portanto possível criar instâncias desta classe). De uma forma
geral, o objecto Stream passado ao construtor destas classes é obtido a partir do
construtor da classe FileStream, como ilustrado no seguinte excerto de código:
‘Abre um BinaryReader.
Dim br As New BinaryReader(
New FileStream ("ler.bin ", FileMode.Open) )
‘Abre um BinaryWriter.
‐ 24 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Dim bw As New BinaryWriter(
New FileStream("escrever.bin", FileMode.Create))
o programa seguinte cria um objecto BinaryWriter e escreve no stream uma String, um
Integer e um Double. Como referido, a classe BinaryWriter disponibiliza um único
método para escrita: o método Write, que se encontra sobrecarregado (overloaded).
De seguida é criado um objecto BinaryReader associado ao mesmo ficheiro onde
foram gravados os três dados lidos. Os três valores guardados no ficheiro são lidos,
através dos métodos ReadString, ReadInt32 e ReadDouble, e visualizados na janela de
consola.
Exemplo 5 ‐ Classes BinaryWriter e BinaryReader
' Classes BinaryWriter e BinaryReader.
' Escrever e ler dados binários.
Imports System.IO
Module Module1
Sub Main()
' Escreve dados num BinaryWriter.
Try
' Abre um BinaryWriter.
Dim bw As New BinaryWriter(New FileStream("dados.bin",
FileMode.Create))
' Escreve uma string, um inteiro e um real.
Try
Console.Write("Introduza uma string: ")
Dim str As String = Console.ReadLine
bw.Write(str)
Console.Write("Introduza um número inteiro: ")
Dim i As Integer = CInt(Console.ReadLine)
bw.Write(i)
Console.Write("Introduza um número real: ")
Dim r As Double = CDbl(Console.ReadLine)
bw.Write(r)
Catch ex As IOException
Console.WriteLine("Erro na escrita." & vbNewLine & ex.Message)
End Try
' Fecha o stream.
bw.Close()
Catch ex As Exception
Console.WriteLine("Não foi possível abrir/escrever no ficheiro para
escrita." & vbNewLine & ex.Message)
‐ 25 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
End Try
Console.WriteLine()
' Lê dados de um BinaryReader.
Try
Abre um BinaryReader.
Dim br As New BinaryReader(New FileStream("dados.bin",
FileMode.Open))
' Le a string, o inteiro e o real.
Try
Console.WriteLine("String introduzida: " & br.ReadString())
Console.WriteLine("Número inteiro introduzido: " & br.ReadInt32())
Console.WriteLine("Número real introduzido: " & br.ReadDouble())
Catch ex As IOException
Console.WriteLine("Erro na leitura." & vbNewLine & ex.Message)
End Try
3.3.7. Serialização
Serialização é o processo de conversão de um objecto, ou um grafo de objectos
ligados, numa cadeia contígua de bytes. Tal processo permite, por exemplo, gravar o
estado de um objecto num ficheiro binário e recuperá‐lo mais tarde, com seu último
estado gravado.
A serialização pode ser vista como um mecanismo que permite gravar um objecto num
formato standard. É ainda visto como um método standard de propagar tipos complexos
e, em particular, objectos. Permite escrever, para um OutputStream, um objecto e todos
aqueles que ele directa ou indirectamente referencia (grafo de objectos). A De
serialização permite restaurar, a partir de um InputStream, um grafo de objectos
Como exemplos de utilização referem‐se o seguintes:
• gravar o estado de uma aplicação
‐ 26 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
• cópia de objectos de/para o clipboard (ou outro qualquer mecanismo de
transferência de objectos)
• criação de cópias de reserva de objectos em memória
• transmissão de objectos através de redes de comunicação, para aplicações que
correm noutras máquinas
A Framework Class Library do .NET suporta a serialização e reposição de objectos de
forma automática e fornece vários mecanismos de auxilio à serialização proprietária.
Identificam‐se os seguintes passos para seriar um grafo de objectos:
1. Criação de um stream ‐ Uma stream representa o suporte físico onde o objecto vai
ser serializado (no caso anterior em memória – System.IO.MemoryStream)
2. Criação de um Formatter ‐ Um formatter é um tipo que implementa a interface
System.Runtime.Serialization.IFormatter e que sabe seriar (e repor) um grafo de
objectos num stream. A FCL tem dois formatters:
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
System.Runtime.Serialization.Formatters.Soap.SoapFormatter
3. Chamar o método Serialize() do formatter passando como argumentos um
stream e um objecto (que representa a raiz do grafo de objectos a seriar).
Os exemplos seguintes ilustram os conceitos agora enunciados.
3.3.8. Exercícios Resolvidos
De seguida apresenta‐se um conjunto de exercícios para manipulação de ficheiros
binários. O 2º exercício refere‐se ao tratamento de ficheiros binários e o 3º a ficheiros
binários de acesso directo. Ambos usam um módulo que trata a estrutura “Pessoa”,
apesar das definições da estrutura serem diferentes.
1. Criar um módulo para o tratamento da estrutura Pessoa. Este módulo deve poder:
a. Escrever na consola o conteúdo de uma Pessoa
b. Criar um vector com vários objectos do tipo Pessoa
c. Escrever na consola o conteúdo de um vector de objectos do tipo Pessoa
‐ 27 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Exemplo 6 ‐ Módulo Pessoa
Module ModuloPessoa
‐ 28 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
2. Ficheiros binários de acesso directo:
a. Crie uma Estrutura de Pessoa, com nome, morada, telefone e numero de BI,
adequada para ser usada em Ficheiros binários de acesso directo
(comprimento fixo). Deve incluir um campo sobre se o registo se encontra
activo.
b. Criar uma rotina para adicionar uma pessoa ao ficheiro.
c. Use a rotina de 1.b) e 2.b) em conjunto para criar uma nova rotina que
adicione um conjunto de pessoas ao ficheiro.
d. Criar uma rotina pesquisar a pessoa cujo nome é passado como parâmetro.
e. Criar uma rotina para mostrar todo o conteúdo do ficheiro.
f. Criar uma rotina para eliminar logicamente uma pessoa.
g. Criar uma rotina para eliminar fisicamente uma pessoa.
Exemplo 7 ‐ Ficheiros binários de acesso directo
Module FBinario_directo
Structure Pessoa
<VBFixedString(50)> Dim nome, morada, telefone As String
Dim altura_cm As Integer
Dim activo As Boolean
End Structure
For i = 0 To vp.Length - 1
FilePut(fn, vp(i))
Next
FileClose(fn)
End Sub
Sub Le_Lista_Pessoas_Ficheiro(ByRef vp() As Pessoa)
Dim fn As Integer = FreeFile()
Dim i As Integer = 0
FileOpen(fn, "Dados.txt", OpenMode.Random, OpenAccess.Read, ,
Len(vp(0)))
While Not EOF(fn)
FileGet(fn, vp(i))
i = i + 1
End While
‐ 29 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
FileClose(fn)
End Sub
Function Pesquisa_Uma_Pessoa(ByVal nome As String)
Dim fn As Integer = FreeFile()
Dim p As Pessoa = Nothing
FileOpen(fn, "Dados.txt", OpenMode.Random, OpenAccess.ReadWrite,
,Len(p))
Do
FileGet(fn, p)
Loop While Not EOF(fn) And Trim(p.nome) <> nome
Dim i As Integer
If EOF(fn) And Trim(p.nome) <> nome Then i = -1 Else i = Seek(fn)
FileClose(fn)
Return i
End Function
Sub Apaga_Uma_Pessoa_Logicamente(ByVal nome As String)
Dim i = Pesquisa_Uma_Pessoa(nome)
If (i = -1) Then Return
Dim fn As Integer = FreeFile()
Dim p As Pessoa = Nothing
FileOpen(fn, "Dados.txt", OpenMode.Random, OpenAccess.ReadWrite,
,Len(p))
Seek(fn, i - 1)
FileGet(fn, p)
p.activo = False
Seek(fn, i - 1)
FilePut(fn, p)
FileClose(fn)
End Sub
Sub Apaga_Uma_Pessoa_Fisicamente(ByVal nome As String)
Dim i = Pesquisa_Uma_Pessoa(nome)
If (i = -1) Then Return
Dim fn As Integer = FreeFile()
Dim p As Pessoa = Nothing
FileOpen(fn, "Dados.txt", OpenMode.Random, OpenAccess.ReadWrite,
,Len(p))
Seek(fn, i)
While Not EOF(fn)
FileGet(fn, p)
Seek(fn, i - 1)
FilePut(fn, p)
i = i + 1
‐ 30 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Seek(fn, i)
End While
Dim pn As Pessoa = Nothing
Seek(fn, i - 1)
FilePut(fn, pn)
FileClose(fn)
End Sub
Sub Main()
Dim num_pessoas As Integer = 3
Dim vp1(num_pessoas), vp2(num_pessoas), vp3(num_pessoas) As
Pessoa
Cria_Conjunto_Pessoas(vp1)
Escreve_Lista_Pessoas_Consola(vp1)
Escreve_Lista_Pessoas_Ficheiro(vp1)
Apaga_Uma_Pessoa_Logicamente("nuno - 2")
Le_Lista_Pessoas_Ficheiro(vp2)
Escreve_Lista_Pessoas_Consola(vp2)
Apaga_Uma_Pessoa_Fisicamente("nuno - 1")
Le_Lista_Pessoas_Ficheiro(vp3)
Escreve_Lista_Pessoas_Consola(vp3)
Console.ReadKey()
End Sub
End Module
3. Ficheiros Binários .NET:
a. Crie uma Estrutura de Pessoa, com nome, morada, telefone e numero de BI,
adequada para ser usada em Ficheiros binários .NET (serializavel).
b. Crie uma rotina que adicione um conjunto de pessoas ao ficheiro.
c. Crie uma rotina que liste o conjunto de pessoas do ficheiro.
Exemplo 8 ‐ Ficheiros Binários .NET
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Module FBinarios
<Serializable()> Structure Pessoa
‐ 31 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Dim nome, morada, telefone As String
Dim altura_cm As Integer
Dim activo As Integer
End Structure
Sub Escreve_Lista_Pessoas_Ficheiro(ByRef vp() As Pessoa)
Dim fs As FileStream = New FileStream("DataFile.txt",
FileMode.Create)
Dim formatter As BinaryFormatter = New BinaryFormatter()
For i = 0 To vp.Length - 1
formatter.Serialize(fs, vp(i))
Next
fs.Close()
End Sub
Sub Le_Lista_Pessoas_Ficheiro(ByRef vp() As Pessoa)
Dim fs As FileStream = New FileStream("DataFile.txt",
FileMode.Open)
Dim formatter As BinaryFormatter = New BinaryFormatter()
Dim i As Integer = 0
While fs.Length <> fs.Position
vp(i) = DirectCast(formatter.Deserialize(fs), Pessoa)
i = i + 1
End While
fs.Close()
End Sub
Sub Main()
Dim num_pessoas As Integer = 3
Dim vp1(num_pessoas), vp2(num_pessoas) As Pessoa
Cria_Conjunto_Pessoas(vp1)
Escreve_Lista_Pessoas_Consola(vp1)
Escreve_Lista_Pessoas_Ficheiro(vp1)
Le_Lista_Pessoas_Ficheiro(vp2)
Escreve_Lista_Pessoas_Consola(vp2)
Console.ReadKey()
End Sub
End Module
‐ 32 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
3.4. Exercícios Complementares
1. Defina uma estrutura para descrever restaurantes. A estrutura deve armazenar o
nome, endereço, preço médio e o tipo de comida. Elabore um programa que crie um
vector de estruturas e as funções para:
a) Preencher o vector;
b) Mostrar todos os restaurantes de um certo tipo de comida;
c) Mostre qual o restaurante mais barato de um certo tipo de comida;
d) Ordenar por ordem crescente de preço os restaurantes;
2. Defina uma estrutura capaz de armazenar o nome, o número, o ano de entrada, o
curso e as cadeiras feitas (máximo de 20), por um determinado aluno. Sobre cada cadeira
feita pelo aluno é necessário guardar informação sobre o nome da cadeira e a nota final.
Escreva um programa que crie um vector com informação de vários alunos e as funções
para:
a) Acrescentar alunos ao vector;
b) Determinar qual o aluno, de um determinado curso, com melhor
aproveitamento.
c) Retornar o número de alunos, de um determinado curso, que fizeram 10 ou
menos cadeiras em mais de cinco anos.
3. O Dr. Dentista António Desdentado resolveu informatizar as fichas clínicas dos seus
doentes e, antes de pedir autorização à Comissão de Direitos Liberdades e Garantias da
Assembleia da Republica Portuguesa, decidiu pedir um protótipo aos alunos da disciplina
de Al do ISEP.
A informação relativa aos clientes que o Dr. Desdentado pretende guardar é a seguinte:
Cliente
Nome
Telefone
Profissão
Sexo
Idade
Peso
Dentes (total de 32)
Estado (bom, razoável, perdido, tirado, desvitalizado, implante)
Número de tratamentos já efectuados
Data do próximo tratamento (e.g. 1998‐07‐22)
Tipo do próximo tratamento
‐ 33 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Suponha que os dados dos n clientes se encontram já carregados num vector global em
memória central (máximo de 100 clientes).
a) Defina todas as estruturas e varáveis globais necessárias à resolução das
seguintes alíneas.
b) Faça uma função que retorne a média das idades dos pacientes do Dr.
Desdentado.
c) Faça uma função que recebe como parâmetro uma data e escreva para o ecrã
quais as vítimas que terão que ir ao dentista nessa data.
d) Faça uma função que indique qual o dente mais tratado nos clientes do Dr.
Desdentado.
4. Com vista a melhorar a qualidade de serviço, o restaurante “Boa Comida” resolveu
informatizar a informação relativa aos seus clientes. Esta informação encontra‐se
guardada num vector (máximo de 200 clientes), com o seguinte formato:
Cliente Nome
Telefone
Profissão
Sexo
Idade
Pratos Consumidos (20 elementos do menu)
Nº de doses consumidas
Nº de vezes que consumiu
Data do último consumo(exemplo: “1998‐09‐22”)
Note que os 20 pratos consumidos referem‐se à lista de 20 pratos do restaurante.
a) Defina as estruturas e as variáveis globais necessárias è resolução das
seguintes alíneas.
b) Faça uma função que indique qual o prato preferido dos clientes do
restaurante, tendo em conta o rácio número de doses / número de vezes que
foi consumido.
c) Uma das potencialidades do novo sistema é conseguir prever quais os pratos
que serão consumidos por um cliente, através do seu histórico de consumos.
Elabore uma função que recebendo como parâmetro o nome do cliente e a
data actual, gere a lista de pratos consumidos mais do que uma vez pelo
cliente. Devem ser ignorados os pratos consumidos pela última vez há mais
de um ano.
‐ 34 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
4. Bibliografia
1. O Guia Prático do Visual Basic 2008, Vítor Pereira, ISBN: 978‐989‐615‐067‐9,
Centro Atlântico, 2008
2. Programação em Visual Basic .NET, Vasco Capitão, FCA (www.fca.pt)
3. An Introduction to Programming Using Visual Basic 2005, 6ª Edição, David I.
Schneider, Prentice Hall
4. Informação suplementar da Microsoft: http://msdn.microsoft.com/library.
‐ 35 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Anexo 1 ‐ Classe Console
A classe Console representa a janela do sistema operativo (SO) que permite a
interacção entre este e o utilizador, através de comandos ou instruções de texto inseridas
pelo teclado do computador. Em termos formais, representa o stream de standard input,
output e error das aplicações em modo de consola.
Esta classe disponibiliza membros que suportam as aplicações que leiam caracteres de
e escrevam caracteres para a janela de consola. Não é possível herdar desta classe.
As chamadas a métodos como Write e WriteLine em aplicações em modo Windows
não produzem qualquer efeito. Por outro lado, aplicações sem utilizador, como aplicações
para um servidor, não devem, de uma forma geral, mostrar qualquer output para a
consola.
A seguinte tabela apresenta os métodos disponibilizados pela classe Console.
Tabela 8 ‐ Métodos da classe Console
Método Descrição
Beep Overloaded. Toca um som através das colunas da
consola.
Clear Limpa o buffer da consola e a correspondente janela da
consola de toda a informação.
MoveBufferArea Overloaded. Copia uma área de origem especificada do buffer
de ecrã para uma área destino especificada.
OpenStandard Error Overloaded. Recupera o stream de standard error.
OpenStandardInput Overloaded. Recupera o stream de standard input.
OpenStandardOutput Overloaded. Recupera o stream de standard output.
Read Lê o próximo carácter do stream de standard input.
ReadKey Overloaded. Retorna o próximo carácter ou tecla de função
pressionada pelo utilizador.
ReadLine Lê uma linha de carácteres do stream de standard input.
ResetColor Reinicia as cores das letras e de fundo da consolapara o mseu
valor por omissão.
SetBufferSize Define a altura e largura da área de buffer do ecrã de acordo
com os valores especificados.
SetCursorPosition Define a posição do cursor.
SetError Define a propriedade System.Console.Error como sendo o
‐ 36 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
objecto TextWriter especificado.
SetIn Define a propriedade System.Console.In como sendo o objecto
TextReader especificado.
SetOut Define a propriedade System.Console.Out como sendo o
objecto TextWriter especificado.
SetWindowPosition Define a posição da janela de consola relativa ao buffer do
ecrã.
SetWindowSize Define a altura e largura da janela de consola de acordo com os
valores especificados.
Write Overloaded. Escreve uma representação texto do valor ou
valores especificados no stream de standard output.
WriteLine Overloaded. Escreve uma representação texto do valor ou
valores especificados, seguidos do carácter terminador de
linhas, no stream de standard output.
Referem‐se as seguintes propriedades da classe Console (Tabela 9).
Tabela 9 – Propriedades da classe Console
Propriedade Descrição
BackgroundColor Retorna ou define a cor de fundo da consola.
BufferHeight Retorna‐ou define a altura da área de memória intermédia do
ecrã (buffer) .
BufferWidth Retorna ou define a largura da área de memória intermédia
do ecrã (buffer).
CapsLock Retorna um valor Boolean que indica se a tecla CAPS LOCK
está ligada ou desligada.
CursorLeft Retorna ou define a posição da coluna do cursor dentro da
área de buffer
CursorSize Retorna ou define a altura do cursor dentro de uma célula de
carácter.
CursorTop Retorna ou define a posição da linha do cursor dentro da área
de buffer.
CursorVisible Retorna ou define um valor que indica se o cursor está visível.
Error Retorna o stream de output do standard error.
ForegroundColor Retorna ou define a cor das letras da consola.
‐ 37 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
In Retorna o stream de standard input.
InputEncoding Retorna ou define a codificação que a consola usa para ler
dados.
KeyAvailable Retorna um valor que indica se existe uma tecla premida
disponível no stream de input.
LargestWindowHeight Retorna o maior número de linhas possível da janela de
consola, com base no tipo de letra e resolução do ecrã
correntes.
LargestWindowWidth Retorna o maior número de colunas possível da janela de
consola, com base no tipo de letra e resolução do ecrã
correntes.
NumberLock Retorna um valor Boolean que indica se a tecla LOCK está
ligada ou desligada.
Out Retorna o stream de standard output.
OutputEncoding Retorna ou define a codificação que a consola usa para
escrever dados.
Title Retorna ou define o título a mostrar na barra de títulos da
consola.
TreatControlCAsInput Retorna ou define um valor que indica se a combinação
CTRL+C é tratada como input normal ou como uma
interrupção que é tratada pelo sistema operativo.
WindowHeight Retorna ou define a altura da área da janela de consola.
WindowLeft Retorna ou define a posição mais à esquerda da área da
janela de consola relativa ao buffer do
WindowTop Retorna ou define a posição de topo da área da janela de
consola relativa ao buffer do ecrã .
WindowWidth Retorna ou define a largura da janela de consola.
Oexemplo seguinte ilustra o funcionamento de algumas das principais propriedades e
métodos da classe Console. Entre outros, são demonstrados os métodos Beep,
SetCursorPosition, ReadKey e Clear. Entre as propriedades usadas inclui‐se:
ForegroundColor, CapsLock, NumberLock e CursorVisible.
‐ 38 ‐
Aplicações Informáticas de Gestão
Estruturas de Dados Compostas
www.foresp.pt
Exemplo 9 ‐ Classe Console
' Classe Console.
Module Module1
Sub Main()
' Altera a cor das letras.
Console.ForegroundColor = ConsoleColor.Green
' Emite um beep standard.
Console.Beep()
' Verifica se CapsLock e NumLock estão ligados.
If Console.CapsLock Then Console.WriteLine("CapsLock: ON")
Else Console.WriteLine("CapsLock: OFF")
End If
If Console.NumberLock Then Console.WriteLine("Num Lock: ON")
Else Console.WriteLine("Num Lock: OFF")
End If
' Define a posição do cursor na janela de consola.
Console.SetCursorPosition(5, 3)
' Escreve uma string.
Console.Write("Introduza o seu nome: ")
' Lê uma string.
Dim str As String = Console.ReadLine()
' Define o título da janela como sendo a string lida.
Console.Title = str
' Emite um beep com uma frequência de 1000 Hz
' e uma duração de 500 milissegundos.
Console.Beep(1000, 200)
' Indica que o título da janela foi alterado.
Console.SetCursorPosition(5, 3)
Console.WriteLine("O seu nome é agora o título da janela!")
' Esconde o cursor.
Console.CursorVisible = False
Console.SetCursorPosition(5, 5)
Console.WriteLine("Pressione qualquer tecla para limpar a
janela")
' Lê uma tecla sem a mostrar.
Console.ReadKey(True)
' Limpa a janela de consola.
Console.Clear()
End Sub
End Module
‐ 39 ‐