Upload
fabio-moura-pereira
View
171
Download
1
Embed Size (px)
DESCRIPTION
Aula Algoritmos e Programação II - Persistência 01 (Arquivos Java)
Citation preview
UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO
ALGORITMOS E PROGRAMAÇÃO II – 2014.2
Fábio M. Pereira
Roteiro
• Fluxo de E/S
– Byte streams
– Streams de caracteres
– E/S orientada a linha
– Buffered streams
– E/S a partir da linha de comando
– Streams de dados
– Streams de objetos
– Entrada e saída de objetos complexos
• E/S em arquivo
– Objetos File
– Arquivos de acesso aleatório
Fluxo de E/S
• Um fluxo de E/S (entrada/saída – I/O stream) representa uma fonte de entrada ou um destino de saída
• Um fluxo pode representar muitos tipos diferentes de origens e destinos, incluindo arquivos em disco, dispositivos, outros programas, e matrizes de memória
• Streams dão suporte a muitos tipos diferentes de dados, incluindo bytes simples, tipos de dados primitivos, caracteres localizados e objetos
• Alguns streams simplesmente passam os dados, outros manipulam e transformam os dados de maneira útil
Fluxo de E/S
• Não importa como eles funcionam internamente, todos os streams apresentam o mesmo modelo simples aos programas que as utilizam: um stream é uma sequência de dados
• Um programa usa um fluxo de entrada para ler os dados a partir de uma fonte, um item de cada vez:
Fluxo de E/S
• Um programa usa um fluxo de saída para escrever dados para um destino, um item de cada vez:
• O destino fonte de dados e os dados apresentados na figura pode ser qualquer coisa que mantém, gera, ou consome dados
• Obviamente, isso inclui arquivos do disco, mas a origem ou destino pode também ser outro programa, um dispositivo periférico, um socket de rede, ou um array
Byte streams
• Vamos usar o tipo mais básico de fluxo, byte stream, para demonstrar as operações comuns do fluxo de E/S
• Como exemplo de entrada, vamos usar o arquivo xanadu.txt, que contém o seguinte verso:
In Xanadu did Kubla Khan
A stately pleasure-dome decree:
Where Alph, the sacred river, ran
Through caverns measureless to man
Down to a sunless sea.
Byte streams
• Programas usam fluxos de bytes para realizar a entrada e saída de bytes de 8 bits
• Todas as classes de fluxo de bytes são descendentes de InputStream e OutputStream
• Existem muitas classes de fluxo de bytes
• Para demonstrar como streams funcionam, vamos nos concentrar nos arquivos de E/S de fluxos de bytes, FileInputStream e FileOutputStream
• Outros tipos de fluxos de bytes são utilizados da mesma maneira, eles diferem principalmente na forma como eles são construídos
Utilizando byte streams
• Programa CopyBytes, que utiliza byte streams para copiar xanadu.txt:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyBytes {
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("xanadu.txt");
out = new FileOutputStream("copiaxanadu.txt");
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
Utilizando byte streams
• CopyBytes passa a maior parte de seu tempo em um laço simples que lê o fluxo de entrada e escreve no fluxo de saída, um byte de cada vez:
Utilizando byte streams
• Observe que read() retorna um valor int
• Se a entrada é um fluxo de bytes, por que read() não retorna um valor byte?
– Usando um int como um tipo de retorno permite que read() use -1 para indicar que ele tenha atingido o fim do stream
• Fechar um stream, quando ele não é mais necessário é muito importante, tão importante que CopyBytes usa um bloco finally para garantir que ambos os streams serão fechados, mesmo se ocorrer um erro
• Essa prática ajuda a evitar falha de recursos
Utilizando byte streams
• CopyBytes parece ser um programa normal, mas, na verdade, representa uma espécie de baixo-nível de E/S, que devemos evitar
• Uma vez que xanadu.txt contém dados de caracteres, a melhor abordagem é a utilização de streams de caracteres
• Há também streams para tipos de dados mais complexos
• Fluxos de bytes só devem ser usados para E/S mais primitivas
Streams de caracteres
• A plataforma Java armazena valores de caracteres usando convenções Unicode
– Fluxos de E/S de caracteres automaticamente traduzem este formato interno de e para o conjunto de caracteres local
– Em localidades ocidentais, o conjunto de caracteres local é geralmente um super-conjunto de ASCII de 8 bits
• Para a maioria das aplicações, E/S com fluxos de caracteres não é mais complicado do que E/S com fluxos de bytes
• Entrada e saída feito com classes stream que convertem automaticamente de e para o conjunto de caracteres local
Streams de caracteres
• Um programa que utiliza fluxos de caracteres no lugar de fluxos de byte automaticamente adapta-se ao caráter local definido e está pronto para internacionalização sem esforço extra pelo programador
• Se internacionalização não é uma prioridade, podemos simplesmente usar as classes de fluxo de caracteres sem prestar muita atenção às questões de conjuntos de caracteres
• Posteriormente, se internacionalização tornar-se uma prioridade, o programa pode ser adaptado sem extensa recodificação
Usando streams de caracteres
• Todas as classes de fluxo de caracteres são descendentes de Reader e Writer
• Tal como acontece com fluxos de bytes, há classes de fluxo de caracteres que se especializam em arquivo de E/S: FileReader e FileWriter
• O exemplo CopyCharacters ilustra essas classes:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyCharacters {
public static void main(String[] args) throws IOException {
FileReader in = null;
FileWriter out = null;
try {
in = new FileReader("xanadu.txt");
out = new FileWriter("copiaxanadu.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
Usando streams de caracteres
• CopyCharacters é muito semelhante ao CopyBytes
• A diferença mais importante é que CopyCharacters usa FileReader e FileWriter para entrada e saída no lugar de FileInputStream e FileOutputStream
• Note que ambos usam uma variável int para leitura e escrita, no entanto, em CopyCharacters, a variável int
detém um valor de caractere em seus últimos 16 bits, em CopyBytes, a variável int detém um valor byte em seus últimos 8 bits
E/S orientada a linha
• E/S de caracteres ocorre geralmente em unidades maiores do que os caracteres individuais
• Uma unidade comum é a linha: uma sequência de caracteres com um terminador de linha no final
• Um terminador de linha pode ser uma sequência de retorno de carro/avanço de linha ("\r \n"), um único retorno de carro ("\r"), ou um único avanço de linha ("\n")
• Dar suporte a todas os possíveis terminadores de linha permite que os programas leiam arquivos de texto criados em qualquer um dos sistemas operacionais existentes
E/S orientada a linha
• Vamos modificar o exemplo CopyCharacters para usar E/S orientada a linha
• Para fazer isso, temos que usar duas classes que não vimos antes, BufferedReader e PrintWriter
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.IOException;
public class CopyLines {
public static void main(String[] args) throws IOException {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new FileReader("xanadu.txt"));
out = new PrintWriter(new FileWriter("copiaxanadu.txt"));
String l;
while ((l = in.readLine()) != null) {
out.println(l);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
Buffered streams
• A maioria dos exemplos que vimos até agora usam E/S sem buffer
• Isso significa que cada pedido de leitura ou escrita é tratado diretamente pelo sistema operacional, o que pode tornar um programa muito menos eficiente, uma vez que cada um desses pedidos, muitas vezes desencadeia acesso ao disco, a atividade de rede, ou alguma outra operação que é relativamente cara
• Para reduzir esse tipo de sobrecarga, a plataforma Java implementa E/S com buffer – Fluxos com buffer de entrada leem dados a partir de uma área de
memória conhecida como buffer, a API nativa de entrada é chamada apenas quando o buffer está vazio
– Da mesma forma, fluxos de saída em buffer gravam dados em um buffer, e a API de saída nativa é chamada apenas quando o buffer está cheio
Buffered streams
• Um programa pode converter um fluxo sem buffer em um fluxo com buffer usando a expressão de conversão que utilizamos várias vezes, onde o objeto de fluxo sem buffer é passado para o construtor de uma classe de stream com buffer
• Vejamos como podemos modificar as chamadas do construtor no exemplo CopyCharacters para usar E/S com buffer: in = new BufferedReader(new
FileReader("xanadu.txt"));
out = new BufferedWriter(new
FileWriter("copiaxanadu.txt"));
Descarregando buffered streams
• Muitas vezes faz sentido descarregar um buffer em pontos críticos, sem esperar que ele seja preenchido
• Isto é conhecido como flushing the buffer
• Algumas classes buffer de saída dão suporte a autoflush, especificado por um argumento opcional do construtor
• Quando o autoflush está habilitado, certos eventos-chave fazem com que o buffer seja liberado
• Por exemplo, um objeto autoflush PrintWriter libera o buffer a cada invocação de println ou format
• Para liberar um fluxo manualmente, devemos invocar o seu método flush – O método flush é válida em qualquer fluxo de saída, mas não
tem efeito a menos que o fluxo possua um buffer
E/S a partir da linha de comando
• Um programa é muitas vezes executado a partir da linha de comando e interage com o usuário no ambiente de linha de comando
• A plataforma Java dá suporte a esse tipo de interação de duas maneiras: através dos fluxos padrões e por meio do Console
Fluxos padrões
• Fluxos padrões são uma característica de muitos sistemas operacionais
• Por padrão, eles leem a entrada do teclado e escrevem a saída para o display
• Eles também dão suporte a E/S em arquivos e entre os programas, mas esse recurso é controlado pelo interpretador de linha de comando, não pelo programa
• A plataforma Java dá suporte a três fluxos padrões:
– entrada padrão, acessados através System.in
– Saída padrão, acessado através System.out
– E erro padrão, acessado através System.err
Fluxos padrões
• Esses objetos são definidos automaticamente e não precisam ser abertos
• Saída padrão e erro padrão são ambos para saída
– A saída de erro permite que o usuário separadamente desvie a saída regular para um arquivo e ainda seja capaz de ler mensagens de erro
– Para mais informações, consulte a documentação para o seu interpretador de linha de comando
Fluxos padrões
• Podemos esperar que os fluxos padrão sejam fluxos de caracteres, mas, por razões históricas, são fluxos de bytes
– System.out e System.err são definidos como objetos PrintStream
– Embora seja tecnicamente um fluxo de bytes, PrintStream usa um objeto de fluxo de caracteres interno para emular muitas das características dos fluxos de caracteres
• Em contrapartida, System.in é um fluxo de bytes sem características de transmissão de caracteres
• Para usar entrada padrão como um fluxo de caracteres, empacote System.in em InputStreamReader: InputStreamReader cin = new InputStreamReader(System.in);
O Console
• Uma alternativa mais avançada para os fluxos padrões é o Console
• Este é um objeto único, pré-definido do tipo Console
que tem a maioria dos recursos fornecidos pelos fluxos padrão, e outros além
– O Console é particularmente útil para a entrada de senha segura
• O objeto Console também fornece fluxos de entrada e saída, que são verdadeiros fluxos de caracteres, através de seus métodos de leitura e escrita
O Console
• Antes que um programa possa usar o Console, ele deve tentar recuperar o objeto Console invocando System.Console():
– Se o objeto Console está disponível, este método o retorna
– Se System.Console retorna NULL, as operações do console não são permitidas, ou porque o sistema operacional não dá suporte ou porque o programa foi lançado em um ambiente não-interativo
O Console
• O objeto Console dá suporte a entrada de senha segura através do seu método readPassword
• Este método ajuda a entrada de senha segura de duas maneiras:
– Em primeiro lugar, ele suprime o eco, para que a senha não seja visível na tela do usuário
– Em segundo lugar, readPassword retorna um array de caracteres, não uma String, para que a senha possa ser substituída, retirando-a da memória assim que ela não for mais necessária
• O exemplo Password é um programa protótipo para alterar a senha de um usuário
– Ele demonstra vários métodos do Console
import java.io.Console;
import java.util.Arrays;
import java.io.IOException;
public class Password {
public static void main (String args[]) throws IOException {
Console c = System.console();
if (c == null) {
System.err.println("Sem console.");
System.exit(1);
}
String login = c.readLine("Entre com o login: ");
char[] senhaAntiga = c.readPassword("Entre com sua " +
"senha anterior: ");
...
}
... }
...
if (verifica(login, senhaAntiga)) {
boolean naoCombina;
do {
char[] novaSenha1 =
c.readPassword("Entre com a nova senha: ");
char[] novaSenha2 =
c.readPassword("Digite novamente: ");
naoCombina = ! Arrays.equals(novaSenha1, novaSenha2);
if (naoCombina) {
c.format("Senhas nao combinam. Tente novamente.%n");
} else {
modifica(login, novaSenha1);
c.format("Senha para %s modificada.%n", login);
}
Arrays.fill(novaSenha1, ' ');
Arrays.fill(novaSenha2, ' ');
} while (naoCombina);
}
Arrays.fill(senhaAntiga, ' ');
}
...
}
...
// Método verifica simples
static boolean verifica(String login, char[] senha) {
return true;
}
// Método modifica simples
static void modifica(String login, char[] senha) {}
}
Streams de dados
• Fluxos de dados dão suporte a E/S binária de valores de tipo de dados primitivos (boolean, char, byte, short, int, long, float e double), bem como a valores String
• Todos os fluxos de dados implementam tanto a interface DataInput como a DataOutput
– As implementações mais utilizado dessas interfaces são DataInputStream e DataOutputStream
Streams de dados
• O exemplo demonstra os fluxos de dados, escrevendo um conjunto de registros de dados e, em seguida, lendo-os novamente
• Cada registro é composto por três valores relacionados a um item em uma fatura, como mostra a tabela:
Ordem no Registro
Tipo de Dado
Descrição Método de Saída Método de entrada Valor Exemplo
1 double Preço DataOutputStream
.writeDouble
DataInputStream
.readDouble
19.99
2 int Quantidade DataOutputStream
.writeInt
DataInputStream
.readInt
12
3 String Descrição DataOutputStream
.writeUTF
DataInputStream
.readUTF
“Camiseta
Java”
Streams de dados
• Note-se que DataStream detecta uma condição de fim de arquivo pela captura de EOFException, em vez de testar por um valor de retorno inválido
• Todas as implementações de métodos de DataInput
usam EOFException em vez de valores de retorno
• Note também que cada gravação (write) especializada em DataStreams é exatamente igual ao correspondente de leitura (read) especializado
• Cabe ao programador se certificar de que os tipos de saída e tipos de entrada são combinados, desta maneira: o fluxo de entrada é composto por dados binários simples, sem nada para indicar os tipos de valores individuais ou onde começam no stream
Streams de objetos
• Assim como os fluxos de dados dão suporte a E/S de tipos de dados primitivos, fluxos de objetos dão suporte a E/S de objetos
• A maioria, mas não todas as classes padrões dão suporte a serialização de seus objetos: aquelas que implementam a interface Serializable
• As classes de fluxo de objetos são ObjectInputStream e ObjectOutputStream
• Essas classes implementam ObjectInput e ObjectOutput, que são sub-interfaces de DataInput e DataOutput
Streams de objetos
• Isso significa que todos os métodos de E/S de dados primitivos cobertos na seção Streams de Dados também são implementadas em fluxos de objetos
• Assim, um fluxo de objeto pode conter uma mistura de valores primitivos e objetos
• O exemplo ObjectStreams ilustra isto
Streams de objetos
• ObjectStreams cria o mesmo aplicativo do fluxo de dados, com um par de mudanças: – Em primeiro lugar, os preços são agora objetos BigDecimal
– Em segundo lugar, um objeto Calendar é gravado no arquivo de dados, o que indica uma data de fatura
• Se readObject() não devolver o tipo de objeto esperado, tentar convertê-lo para o tipo correto pode lançar uma ClassNotFoundException
• Neste exemplo simples, isto não pode acontecer, por isso, não tentamos capturar a exceção, em vez disso, notificamos o compilador que estamos cientes do problema, adicionando ClassNotFoundException à cláusula throws do método main
Entrada e saída de objetos complexos
• Os métodos writeObject e readObject são simples de usar, mas eles contêm uma lógica de gerenciamento de objetos muito sofisticada
• Isso não é importante para uma classe como Calendar, que apenas encapsula valores primitivos, mas muitos objetos contêm referências a outros objetos
• Se readObject reconstituir um objeto a partir de um stream, ele tem que ser capaz de reconstituir todos os objetos que o objeto original referencia, esses objetos adicionais podem ter suas próprias referências, e assim por diante
Entrada e saída de objetos complexos
• Nesta situação, writeObject atravessa toda a teia de referências a objetos e escreve todos os objetos na teia para o fluxo
• Assim, uma única chamada de writeObject pode fazer com que um grande número de objetos sejam enviados para o stream
Entrada e saída de objetos complexos
• writeObject é invocado para escrever um único objeto chamado a
• Este objeto contém referências a objetos b e c, enquanto b contém referências a d e e
• Invocando writeObject(a) não escreve apenas a, mas todos os objetos necessários para reconstituir a, de modo que os outros quatro objetos nesta teia são escritos também
• Quando a é lido por readObject, os outros quatro objetos são lidos também, e todas as referências dos objetos originais são preservadas
Entrada e saída de objetos complexos
• Podemos nos perguntar o que acontece se dois objetos no mesmo fluxo contêm referência a um único objeto
– Será que eles irão se referir a um único objeto quando são lidos de volta?
– A resposta é "sim“
• Um fluxo só pode conter uma cópia de um objeto, embora possa conter qualquer número de referências a ele
• Assim, se explicitamente escrevermos um objeto para um fluxo duas vezes, realmente estaremos escrevendo apenas a referência duas vezes
Entrada e saída de objetos complexos
• Por exemplo, se o código a seguir escreve um objeto ob duas vezes em um stream: Object ob = new Object();
out.writeObject(ob);
out.writeObject(ob);
• Cada writeObject deve combinar com um readObject, assim o código de leitura do stream de volta poderia parecer com: Object ob1 = in.readObject();
Object ob2 = in.readObject();
• Isto resultaria em duas variáveis, ob1 e ob2, que fazem referência a um único objeto
Entrada e saída de objetos complexos
• No entanto, se um único objeto é gravado em dois fluxos diferentes, ele é efetivamente duplicado, se um programa ler ambos os fluxos de volta vai ver dois objetos distintos
E/S em arquivo
• Até agora nos concentramos em streams, que fornecem um modelo simples para ler e gravar dados
• Streams trabalham com uma grande variedade de fontes e destinos de dados, incluindo arquivos de disco, no entanto, streams não dão suporte a todas as operações que são comuns com os arquivos do disco
• Iremos nos concentrar em arquivos de E/S não stream
• Existem dois tópicos: – File é uma classe que nos ajuda a escrever código
independente de plataforma que analisa e manipula arquivos e diretórios
– Arquivos de acesso aleatório dão suporte ao acesso não-sequencial a dados de arquivo em disco
Objetos File
• A classe File (arquivo) torna mais fácil escrever código independente de plataforma que analisa e manipula arquivos
• O nome desta classe pode enganar: instâncias de File
representam os nomes dos arquivos, e não os arquivos – O arquivo correspondente ao nome do arquivo pode até não
existir
• Por que criar um objeto File para um arquivo que não existe? – Um programa pode usar o objeto para analisar um nome de
arquivo
– Além disso, o arquivo pode ser criado, passando o objeto File
para o construtor de algumas classes, como FileWriter
Objetos File
• Se o arquivo existir, um programa pode examinar seus atributos e realizar várias operações no arquivo, por exemplo, renomear, apagar, ou mudar suas permissões
Um arquivo possui muitos nomes
• Um objeto File contém uma string com o nome de arquivo usado para construí-lo
• Essa string nunca muda ao longo do tempo de vida do objeto
• Um programa pode usar o objeto File para obter outras versões do nome do arquivo, alguns dos quais podem ou não ser o mesmo que a string de nome de arquivo original passado para o construtor
• Suponha que um programa crie um objeto File com a chamada do construtor: File a = new File("xanadu.txt");
Um arquivo possui muitos nomes
• O programa invoca um número de métodos para obter diferentes versões do nome do arquivo
• O programa é então executado tanto em um sistema Microsoft Windows (no diretório c:\java\exemplos) como em um sistema Linux (no diretório /home/cafe/java/exemplos)
• A tabela mostra o que os métodos retornariam:
Método Chamado Retorno no Windows Retorno no Linux
a.toString() xanadu.txt xanadu.txt
a.getName() xanadu.txt xanadu.txt
a.getParent() NULL NULL
a.getAbsolutePath
()
c:\java\exemplos\xanadu.txt /home/cafe/java/exemplos/xanadu.txt
Um arquivo possui muitos nomes
• Em seguida, o mesmo programa constrói um objeto File
a partir de um nome de arquivo mais complicado, usando File.separator para especificar o nome do arquivo de uma forma independente da plataforma:
File b = new File(".." + File.separator +
"exemplos" + File.separator + "xanadu.txt");
• Embora b se refira ao mesmo arquivo que a, os métodos retornam valores ligeiramente diferentes, como mostra a Tabela
Um arquivo possui muitos nomes
• Vale a pena mencionar que File.compareTo() não considera a e b serem o mesmo, apesar deles se referirem ao mesmo arquivo, os nomes usados para construí-los são diferentes
Método Chamado Retorno no Windows Retorno no Linux
b.toString() ..\examples\xanadu.txt ../examples/xanadu.txt
b.getName() xanadu.txt xanadu.txt
b.getParent() ..\exemplos ../exemplos
b.getAbsolutePath
()
c:\java\exemplos\..\exemplo
s\xanadu.txt
/home/cafe/java/exemplos/xanadu.txt
b.getCanonicalPath
()
c:\java\exemplos\xanadu.txt /home/cafe/java/exemplos/xanadu.txt
Um arquivo possui muitos nomes
• O exemplo FileStuff cria objetos File de nomes passados na linha de comando e exerce vários métodos de informações sobre eles
• Será instrutivo executar FileStuff em uma variedade de nomes de arquivos
• Certifique-se de incluir os nomes de diretórios, bem como os nomes dos arquivos que na verdade não existem
• Tente passar para FileStuff uma variedade de nomes de caminhos relativos e absolutos
Manipulando arquivos
• Se um objeto File dê nome a um arquivo real, um programa pode usá-lo para realizar uma série de operações úteis com o arquivo – Estas incluem passar o objeto para o construtor de um stream
para abrir o arquivo para leitura ou escrita
• O método delete apaga o arquivo imediatamente, enquanto o método deleteOnExit exclui o arquivo quando a máquina virtual termina
• setLastModified define a data/hora de alteração do arquivo
• Por exemplo, para definir o tempo de modificação de xanadu.txt para o horário atual, um programa pode fazer:
new File("xanadu.txt")
.setLastModified(new Date().getTime());
Manipulando arquivos
• O método renameTo() renomeia o arquivo
– Note-se que a sequência de nome de arquivo por trás do objeto File permanece inalterada, portanto, o objeto File não vai referenciar o arquivo renomeado
Trabalhando com diretórios
• File possui alguns métodos úteis para trabalhar com diretórios
• O método mkdir cria um diretório
• O método mkdirs faz a mesma coisa, depois de criar primeiro os diretórios pais que ainda não existam
• Os métodos list e listFiles listam o conteúdo de um diretório:
– O método list retorna uma matriz de strings contendo os nomes dos arquivos
– Enquanto listFiles retorna uma matriz de objetos File
Métodos estáticos
• File contém alguns métodos estáticos úteis
• O método createTempFile cria um novo arquivo com um nome exclusivo e retorna um objeto File referindo-se a ele
• O listRoots retorna uma lista de nomes de raízes do sistema de arquivos:
– No Microsoft Windows, estes serão os diretórios raízes de unidades montadas, como a:\ e c:\
– Em sistemas UNIX e Linux, este será o diretório raiz, /
Arquivos de acesso aleatório
• Arquivos de acesso aleatório permitem o acesso não sequencial, ou aleatório, ao conteúdo de um arquivo
• Considere o formato de arquivo conhecido como ZIP
– Um arquivo ZIP contém arquivos e é tipicamente comprimido para economizar espaço
– Ele também contém uma entrada de diretório no final que indica onde os vários arquivos contidos no arquivo ZIP começam
Arquivos de acesso aleatório
• Suponha que queiramos extrair um arquivo específico a partir de um arquivo ZIP
• Se usarmos um fluxo de acesso sequencial, temos que:
1. Abrir o arquivo ZIP
2. Pesquisar o arquivo ZIP até localizar o arquivo que desejamos extrair
3. Extrair o arquivo
4. Fechar o arquivo ZIP
• Usando este procedimento, em média, temos que ler a metade do arquivo ZIP antes de encontrar o arquivo que desejamos extrair
Arquivos de acesso aleatório
• Podemos extrair o mesmo arquivo a partir do arquivo ZIP de forma mais eficiente, usando o recurso de busca de um arquivo de acesso aleatório, seguindo estes passos:
1. Abrir o arquivo ZIP
2. Procurar a entrada de diretório e localizar a entrada para o arquivo que desejamos extrair do arquivo ZIP
3. Procurar (para trás), dentro do arquivo ZIP pela posição do arquivo a extrair
4. Extrair o arquivo
5. Fechar o arquivo ZIP
• Este algoritmo é mais eficiente porque lemos somente a entrada de diretório e do arquivo que desejamos extrair
Arquivos de acesso aleatório
• A classe java.io.RandomAccessFile implementa ambas as interfaces DataInput e DataOutput e, portanto, pode ser usado tanto para leitura como escrita
• RandomAccessFile é semelhante ao FileInputStream e FileOutputStream em que você especifica um arquivo no sistema de arquivos nativo para abrir ao criá-lo
• Quando criamos um RandomAccessFile, devemos indicar se vai ser apenas para leitura ou também para gravá-lo (Devemos ser capazes de ler um arquivo, a fim de escrever nele)
Arquivos de acesso aleatório
• O código a seguir cria um RandomAccessFile para ler o arquivo chamado xanadu.txt: new RandomAccessFile("xanadu.txt", "r");
• E o código a seguir abre o mesmo arquivo tanto para leitura quanto para escrita: new RandomAccessFile("xanadu.txt", "rw");
• Depois que o arquivo foi aberto, podemos usar os métodos comuns read e write definidos nas interfaces DataInput e DataOutput para executar E/S no arquivo
Arquivos de acesso aleatório
• RandomAccessFile dá suporte à noção de um ponteiro de arquivo
• O ponteiro do arquivo indica o local atual no arquivo
• Quando o arquivo é criado pela primeira vez, o ponteiro do arquivo é definido como 0, indicando o início do arquivo
• Chamadas de métodos de leitura e escrita ajustam o ponteiro do arquivo pelo número de bytes lidos ou gravados
Arquivos de acesso aleatório
• Além dos métodos normais de E/S em arquivos, que implicitamente movem o ponteiro do arquivo quando ocorre a operação, RandomAccessFile contém três métodos para manipular explicitamente o ponteiro do arquivo:
– int skipBytes(int) move o ponteiro do arquivo para a frente um número especificado de bytes
– void seek(long) posiciona o ponteiro do arquivo antes do byte especificado
– long getFilePointer() retorna o byte atual de localização do ponteiro de arquivo
Referências
The Java Tutorial Fourth Edition: A Short Course on the Basics
Sharon Zakhour, Scott Hommel, Jacob Royal, Isaac Rabinovitch, Tom Risser, Mark Hoeber
...............................................
Publisher: Addison Wesley Professional
Pub Date: September 29, 2006
UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO
ALGORITMOS E PROGRAMAÇÃO II – 2014.2
Fábio M. Pereira