Manipulação de Ficheiros com Ruby

Neste artigo vamos explorar a linguagem Ruby e os seus recursos para manipulação de ficheiros e directórios bem como as capacidades de Input/Ouput disponíveis. Para isso vão ser utilizadas as bibliotecas mais comuns como Dir, File e IO. Embora existam outras mais recentes e com mais recursos estão são as mais comuns, de simples utilização e que servem para base de bibliotecas mais recentes.

Directórios

Para trabalhar com directórios vamos utilizar a classe Dir. Esta classe disponibiliza diversos métodos que permitem aceder e manipular directórios de forma rápida .

Vamos começar por ver um exemplo simples, um pequeno script que verifica se o directório actual é o definido pelo programa. Depois de passar para o referido directório todo o conteúdo é impresso um a um.

WORK_DIR = "/home/magician/"

if Dir.pwd != WORK_DIR
  Dir.chdir( WORK_DIR )
end

Dir.foreach( WORK_DIR ) { |nome| puts nome }

É possível ainda criar e eliminar directórios de forma muito simples, bastando usar os métodos mkdir e rmdir que a classe Dir dispõe.

Dir.mkdir( "/home/magician/Ruby", 755 )

Dir.rmdir( "/home/magician/Ruby" )

O exemplo acima não faz mais do que criar um directório no caminho dado e com as permissões dadas. Estas permissões são opcionais, podemos apenas dar o caminho. Em seguida eliminamos o directório criado com o método rmdir. Podíamos utilizar o método delete em detrimento ao método rmdir, pois ambos fazem exactamente o mesmo.

A classe Dir também permite criar streams a directórios, permitindo desta forma percorrer o conteúdo de directórios de forma mais flexível do que usando o método foreach mostrado anteriormente.

dir = Dir.open( "/home/magician/" )

dir.path #=> "/home/magician"

dir.tell #=> 0

dir.read #=> "Ficheiro 1"

dir.tell #=> 599320

dir.read #=> "Ficheiro 2"

dir.rewind

dir.close

Como podemos ver na linha 1 é aberta uma ligação ao directório. A partir dai podemos, entre outras coisas, percorrer todos os ficheiros e directórios contidos no directório aberto. O método tell (linha 5 e 9) retorna a localização do apontador sob forma de inteiro, o método read (linha 7 e 11) retorna sob forma de string a próxima entrada no directório, retornando nil quando não existirem mais entradas. Por fim o método rewind (linha 13) permite colocar o apontador da stream de novo no inicio e método close (linha 15) fecha a ligação ao directório.

Ficheiros

Criar um ficheiro não podia ser mais simples, basta utilizar a classe File.

file = File.new( "exemplo.txt", "w" )

O exemplo acima cria o ficheiro exemplo.txt e abre o ficheiro em modo de escrita. Vamos agora ver que outros modos de abertura e criação de ficheiros existem.

  • r: Read-Only. Começa no início do ficheiro.
  • r+: Read-Write. Começa no início do ficheiro.
  • w: Write-Only. Passa o tamanho do ficheiro a zero ou cria um novo ficheiro para escrita.
  • w+: Read-Write. Passa o tamanho do ficheiro a zero ou cria um novo ficheiro para escrita e leitura.
  • a: Write-Only. Começa no fim do ficheiro caso este exista, caso contrário cria o ficheiro para escrita.
  • a+: Read-Write. Começa no fim do ficheiro caso este exista, caso contrário cria o ficheiro para escrita e leitura.
  • b: (DOS/WIN only) Binary.

Depois de criarmos um ficheiro, podemos precisar de apagar o ficheiro ou até mesmo mudar o nome desse ficheiro. Este processo é também muito simples, para isso a classe File disponibiliza os métodos rename e delete que permitem estas operações.

File.new( "exemplo.txt", "w" )
File.rename( "exemplo.txt", "novoExemplo.txt" )
File.delete( "novoExemplo.txt" )

O exemplo acima cria o ficheiro exemplo.txt (linha 1), em seguida o nome do ficheiro é modificado de exemplo.txt para novoExemplo.txt (linha 2), por fim o ficheiro é eliminado (linha 3).

A classe File permite ainda realizar mais algumas operações úteis, como por exemplo verificar se um ficheiro existe, se é um directório, se é possível ler ou escrever no ficheiro, entre outras. Vamos ver alguns exemplos:

File.exist?( "ficheiro.txt" )

File.file?( "ficheiro.txt" )

File.directory?( "ficheiro.txt" )

File.readable?( "ficheiro.txt" )

File.writable?( "ficheiro.txt" )

File.executable?( "ficheiro.txt" )

File.zero?( "ficheiro.txt" )

File.size( "ficheiro.txt" )

File.size?( "ficheiro.txt" )

O exemplo mostra algumas das mais importantes operações sobre ficheiros. Na linha 1, o método exist? verifica se o ficheiro existe retornando true ou false, em seguida nas linhas 3 e 5 verificam se o ficheiro dado no path é realmente um ficheiro ou se é um directório. Nas linhas 7 e 9 os métodos readable? e writable? verificam se é possível ler ou escrever no ficheiro e na linha 11 o método executable? verifica se o ficheiro é ou não executável. No final do exemplo podemos ver os métodos zero?, size e size?. O método zero? verifica se o ficheiro tem ou não tamanho igual a zero, os métodos size e size? fazem exactamente o mesmo que retornar o tamanho do ficheiro em bytes, com a diferença que caso este seja nulo, o primeiro retorna 0 e o segundo nil. Para além destes métodos é ainda possível obter mais algumas informações sobre os ficheiros como por exemplo data de criação, de edição e do ultimo acesso. Vamos por isso ver um exemplo desta funcionalidades.

File.ctime( "ficheiro.txt" )  #=> Thu Jan 21 19:55:16 +0000 2008

File.mtime( "ficheiro.txt" )  #=> Thu Jan 21 19:55:16 +0000 2008
 
File.atime( "ficheiro.txt" )  #=> Thu Jan 25 19:55:16 +0000 2008

Como podemos ver, na linha 1 o método ctime retorna uma string como a apresentada acima com a data e hora a que o ficheiro foi criado, o mesmo se passando com os métodos mtime (linha 3) e atime (linha 5), mas neste caso estes métodos retornam a informação referente à última modificação e ao último acesso do ficheiro respectivamente.

A classe File tem ainda mais um recurso bastante útil que é o chmod. Este método permite alterar as permissões de acesso ao ficheiro. As masks para as permissões são as seguintes (r readw writex execute):

  • 0400 – r para o dono.
  • 0200 – w para o dono.
  • 0100 – x para o dono.
  • 0040 – r para o grupo.
  • 0020 – w para o grupo.
  • 0010 – x para o grupo.
  • 0004 – r para os outros.
  • 0002 – w para os outros.
  • 0001 – x para os outros.
  • 4000 – Altera o user ID na execução.
  • 2000 – Altera o group ID na execução.
  • 1000 – Salva texto em swap mesmo depois de usar.

A utilização do método chmod é muito simples, basta abrir o ficheiro como foi mostrado anteriormente e executar o método chmod.

file = File.new( "ficheiro.txt", "r" )
file.chmod( 0644 )

O exemplo acima dá permissão de de leitura de escrita (4 + 2) ao dono e de leitura ao grupo e todos os outros utilizadores.

Input/Ouput de Ficheiros

Ruby tem ainda mais uma classe muito útil, a classe IO. Esta pode ser usada isoladamente ou em conjunto com a classe File, e permite trabalhar com fluxos de input e output. Em seguida vamos ver alguns exemplos da utilização dos recursos desta classe através da classe File.

src = File.open("exemplo.txt","r")

puts src.readline  #=> Linha 1
puts src.readline  #=> Linha 2
puts src.readline  #=> Linha 3

src.flush
src.close

O exemplo acima abre o ficheiro em modo de leitura e lê as três primeiras linhas do ficheiro. Podíamos ainda utilizar o método gets que tem exactamente o mesmo funcionamento, com excepção de que o readline lança uma excepção quando o ficheiro chega ao fim. Para além de ler é também possível escrever para o ficheiro.

dst = File.open("exemplo.txt","w")

dst.puts("Linha 1")
dst.puts("Linha 2")
dst.puts("Linha 3")

dst.flush
dst.close

Este exemplo utiliza o método puts para escrever no ficheiro de destino, a cada execução do puts é automaticamente inserido um \n, para escrever sem a inserção de nova linha podemos utilizar o método putc este método coloca um carácter e cada vez. Assim encerramos o artigo com toda a informação básica necessária para trabalhar com ficheiros.

API

  1. http://www.ruby-doc.org/core/classes/Dir.html
  2. http://www.ruby-doc.org/core/classes/File.html
  3. http://www.ruby-doc.org/core/classes/IO.html

Publicado na edição 13 (PDF) da Revista PROGRAMAR.