sábado, 31 de dezembro de 2011

Renomear arquivos

Vamos dizer que você baixou um torrent. Os arquivos do torrent foram todos acrescidos de um sufixo com o nome do uploader e os espaços foram trocados por "+".  Queremos retirar este sufixo e trocar os "+" por espaços.

import java.io.File;
import java.io.IOException;

/**
* renomeia um arquivo, remove um padrão e substitui por outro
* @param arg[0] arquivo ou diretorio
* @param arg[1] padrao a ser procurado
* @param arg[2] novo valor
* @param arg[3] opcional. se igual a "sub" procura nos subdiretórios
*/
public class RenomeiaArquivos {
  String padrao;
  String novoPadrao;
  boolean subDiretorios = false;

  public RenomeiaArquivos(String seq, String novaSeq, boolean sub) {
    padrao = seq;
    novoPadrao = novaSeq;
    subDiretorios = sub;
  }

  private String adequaString(String original) {
    return original.replace(padrao, novoPadrao);
  }

  private boolean renomeiaArquivo(File f) throws IOException {
    if (f.getName().contains(padrao)) {
      String novoNome = adequaString(f.getAbsolutePath());
      return f.renameTo(new File(novoNome));
    }
    return false;
  }

  private void renomeiaDiretorio(File f) throws IOException {
    String[] lista = f.list();
    File arq;
    for(int i=0; i < lista.length; i++) {
     arq = new File(lista[i]);
     if (arq.isFile()) {
       renomeiaArquivo(arq);
     } else if (subDiretorios) {
       System.out.println("Dir>"+arq.getName());
       renomeiaDiretorio(arq);
     }
    }
  }

  private void executar(String nomeArquivo) throws IOException {
    File f = new File(nomeArquivo);
    System.out.println("Renomeando "+f.getName());
    if (f.isFile()) {
      renomeiaArquivo(f);
    } else {
      renomeiaDiretorio(f);
    }
  }

  public static void main(String[] args) {

    if (args.length < 3) {
      System.out.println("Uso: java RenomeiaArquivos {nomeArquivo|nomeDiretorio} \"padrao original\" \"novo padrao\" [sub]");
      System.exit(0); // sair
    }

    boolean sub = (args.length > 3) && (args[3].equalsIgnoreCase("sub"));
    RenomeiaArquivos ren = new RenomeiaArquivos(args[1], args[2], sub);
    try {
      ren.executar(args[0]);
    } catch (IOException e) {
      System.out.println(e.toString());
    }
  }

}

Testem e descubram o erro deste programa.

Notem que ele compila e consegue ser executado. Este exemplo rodamos no linux com open-jdk.
Seria interessante que este programa utilize regex para podermos usar wildcard para o nome de arquivo.

sexta-feira, 30 de dezembro de 2011

Comparar datas de arquivos


Muitas vezes quando desejamos efetuar operações sobre arquivos, como por exemplo, copiar um arquivo de um diretório para outro, queremos ter certeza que não estamos sobrescrevendo o novo pelo antigo. Para saber isto temos que comparar as datas do arquivo de origem com o destino.

Vamos ver uma classe que faz esta comparação.


import java.util.*;
import java.io.File;


/**
* compara a data de dois arquivos
* @param arg[0] arquivo de origem
* @param arg[1] arquivo de destino
*/
public class ComparaDatasArquivos {
  public static void main(String[] args) { 
    if (args.length < 2) {
      System.out.println("Uso: java ComparaDatasArquivos arquivo1 arquivo2");
      System.exit(0);
    }


    // pega os dois arquivos
    File f1 = new File(args[0]); 
    File f2 = new File(args[1]); 


    if (!(f1.exists() && f2.exists())) {
      System.out.println("Arquivo inexistente! Operação cancelada");
      System.exit(0);
    }


    // pega as datas dos arquivos
    long d1 = f1.lastModified();
    long d2 = f2.lastModified(); 
    String situacao;
    if (d1 == d2)
      situacao = "tem mesma data que";
    else if (d1 < d2)
      situacao = "eh mais antigo que";
    else
      situacao = "eh mais novo que";
    System.out.println(f1.getName() + " " + situacao + " " + f2.getName());
  }
}


Compilem e rodem

PS: Notem a construção do if .. else if .. else do final da classe. Quando se tem um comando, não é necessário criar um bloco com { }

Geração de arquivo zip

Arquivos com formato ZIP são armazenados em um único arquivo compactados (ou diversos arquivos parciais). O framework do Java é capaz de gerar arquivos nos formatos GZIP e ZIP conforme definições das RFC 1950, RFC 1951 e RFC 1952.
As classes que manipulam arquivos ZIP estão em java.util.zip. Estas classes são subclasses de java.io.FilterInputStream e java.io.FilterOutputStream. Desta forma a geração de um arquivo lembra bastante a manipulação de uma stream.
Neste pacote encontramos ainda classe para gerar CRC (cyclic redundancy check checksums).
Um arquivo .zip  possui um cabeçalho com informações com o nome dos arquivos e métodos de compressão utilizados. Podemos utilizar ZipInputStream para ler o arquivo zip utilizando construção abaixo:


// encadear a chamada para ZipInputStream com FileInputStream
FileInputStream arquivo = new FileInputStream(nomeDoArquivoZip)
ZipInputStream arquivoZip = new ZipInputStream(arquivo);
ZipEntry arquivoCompactado;
// varre o arquivo compactado buscando cada uma das entradas
while ((
arquivoCompactado = arquivoZip.getNextEntry()) != null) {
   /*
   arquivoCompactado.toString() --> retorna uma string representando a entrada
   arquivoCompactado.getName() --> retorna o nome da entrada (arquivo/diretorio)
   arquivoCompactado.isDirectory() --> retorna se a entrada é um diretório
   arquivoCompactado.getTime()  --> hora da arquivo (entrada)
   arquivoCompactado.getSize()  --> tamanho do arquivo (entrada) descompactado
   arquivoCompactado.getCRC()   --> CRC
   arquivoCompactado.CompressedSize() --> tamanho do arquivo compactado
   */
   arquivoCompactado.closeEntry();
}
arquivoCompactado.close();


Vamos ver um exemplo mais completo que lista os arquivos contidos em um zip.

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;


public class ExemploZipInput {


 // entrada do programa
 public static void main(String[] args) {
   ExemploZipInputFrame frame = new ExemploZipInputFrame();
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setVisible(true);
 }
} // ExemploZipInput


/**
  Esta classe é composta por uma área que apresenta os arquivos texto
  e permite a descompactação. O arquivo zip pode ser selecionado pelo menu.
*/
class ExemploZipInputFrame extends JFrame {
  // tamanho da tela
  public static final int DEFAULT_WIDTH = 600
  public static final int DEFAULT_HEIGHT = 400;


  private JComboBox<String> comboArquivosNoZip;
  private JTextArea areaArquivoTexto; // local para apresentar os arquivos texto
  private JButton btnExtrair; // 
  private String nomeArquivoZip;
  
  public ExemploZipInputFrame() {
    setTitle("Exemplo de utilizacao de java.util.zip");
    setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

    // menu
    JMenuBar menuBar = new JMenuBar();
    // 1. Arquivo
    JMenu menu = new JMenu("Arquivo"); 
    JMenuItem mnuAbrir = new JMenuItem("Abrir");
    menu.add(mnuAbrir);
    mnuAbrir.addActionListener(new ActionAbrirZip()); // classe definida abaixo
    JMenuItem mnuSair = new JMenuItem("Sair");
    menu.add(mnuSair);
    mnuSair.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) { System.exit(0); }
    });
    menuBar.add(menu);
    setJMenuBar(menuBar);


    // adiciona componentes no Frame
    areaArquivoTexto = new JTextArea();
    comboArquivosNoZip = new JComboBox<String>();
    comboArquivosNoZip.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
String nomeArquivo = (String) comboArquivosNoZip.getSelectedItem();
if (nomeArquivo.endsWith(".txt") || nomeArquivo.endsWith(".java")) {
         carregaArquivoNaTextArea(nomeArquivo);
}
      }
    });

    add(comboArquivosNoZip, BorderLayout.NORTH);
    add(new JScrollPane(areaArquivoTexto), BorderLayout.CENTER);
    btnExtrair = new JButton("Extrair");
    btnExtrair.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
// descompacta o conteudo
        descompactaArquivo((String) comboArquivosNoZip.getSelectedItem());
      }
    });
   add(btnExtrair, BorderLayout.SOUTH);
  } // Construtor ExemploZipInputFrame

  /**
   Classe que realiza ação de "Abrir" do menu
  */
  private class ActionAbrirZip implements ActionListener {
    public void actionPerformed(ActionEvent event) {
      // seleciona o arquivo Zip
      JFileChooser arquivoEscolhido = new JFileChooser();
      arquivoEscolhido.setCurrentDirectory(new File("."));
      FiltrosDeArquivos filtro = new FiltrosDeArquivos();
      filtro.adicionarExtensao(".zip");
      filtro.adicionarExtensao(".jar");
      filtro.setDescription("Arquivos ZIP");
      arquivoEscolhido.setFileFilter(filtro);
      int escolha = arquivoEscolhido.showOpenDialog(ExemploZipInputFrame.this);
      if (escolha == JFileChooser.APPROVE_OPTION) {
        nomeArquivoZip = arquivoEscolhido.getSelectedFile().getPath();
        ProcuraNoArquivoZip();
      }
    }
  } // ActionAbrirZip


  /**
    Lista entradas do arquivo ZIP e preenche o conteudo no combobox
  */
  public void ProcuraNoArquivoZip() {
    comboArquivosNoZip.removeAllItems();
     try {
       ZipInputStream arquivoZip =
        new ZipInputStream(new FileInputStream(nomeArquivoZip));
       ZipEntry entradaNoZip = null;
       while ((entradaNoZip = arquivoZip.getNextEntry()) != null) {
         comboArquivosNoZip.addItem(entradaNoZip.getName());
         arquivoZip.closeEntry();
       }
       arquivoZip.close();
     } catch (IOException e) {
       e.printStackTrace();
     }
  }


  /**
     Lê o conteúdo do arquivo para a área
     @param nome do arquivo compactado (entrada do zip)
   */
   public void carregaArquivoNaTextArea(String nome) {
     BufferedReader arquivoLido = null;
 
     try {
       areaArquivoTexto.setText(""); // apaga o conteudo da área de texto
       ZipInputStream arquivoZip =
         new ZipInputStream(new FileInputStream(nomeArquivoZip));
       ZipEntry entradaNoZip = null;
       // varre as entradas
       // acha entrada = "nome"
       while ((entradaNoZip = arquivoZip.getNextEntry()) != null) {
 if (entradaNoZip.getName().equals(nome)) {
   // se achou ... le para área de texto
   arquivoLido = new BufferedReader(new InputStreamReader(arquivoZip));
   String linhaLida;
   while ((linhaLida = arquivoLido.readLine()) != null) {
     areaArquivoTexto.append(linhaLida);
     areaArquivoTexto.append("\n");
   }
}
         arquivoZip.closeEntry();  
       }
       arquivoZip.close();
     } catch (IOException e) {
       e.printStackTrace();
     }
   }


   public void descompactaArquivo(String nome) {
     try {
       areaArquivoTexto.setText(""); // apaga o conteudo da área de texto
       ZipInputStream arquivoZip =
          new ZipInputStream(new FileInputStream(nomeArquivoZip));
       ZipEntry entradaNoZip = null;
       // acha entrada = "nome"
       while ((entradaNoZip = arquivoZip.getNextEntry()) != null) {
         if (entradaNoZip.getName().equals(nome)) {
           BufferedOutputStream arquivoDescompactado =
             new BufferedOutputStream(new FileOutputStream(nome));
           for(int i = arquivoZip.read(); i != -1; i = arquivoZip.read() ) {
     arquivoDescompactado.write(i);
   }
   arquivoDescompactado.close();
 }
 arquivoZip.closeEntry();
       }
       arquivoZip.close();
     } catch (IOException e) {
       e.printStackTrace();
     }
   }


   
} // ExemploZipInputFrame


/**
  Esta classe cria um filtro para localizar os arquivos texto
  Utilizado em JFileChooser quando tenta selecionar um arquivo em ABRIR
*/
class FiltrosDeArquivos extends FileFilter {


   private String descricao = "";
   // guarda a lista de extensões suportadas
   private ArrayList<String> extensoesArquivos = new ArrayList<String>();


  /**
     adiciona uma extensão no filtro
     @param extensão do arquivo tipo ".txt" ou "txt"
  */
   public void adicionarExtensao(String e) {
     if (!e.startsWith(".")) { // acrescenta o ponto
        e = "." + e;
     }
     extensoesArquivos.add(e.toLowerCase());
   }


   /**
      Armazena a descrição
      @param desc: uma descricao para o conjunto de filtros
   */
   public void setDescription(String desc) { descricao = desc; }


   /**
      Returna a descricao
      @return descricao
   */
   public String getDescription() { return descricao; }


   /**
      Indica que pode selecionar a entrada
   */
   public boolean accept(File f) {
     if (f.isDirectory()) return true;
     String name = f.getName().toLowerCase();


     // verifica se a extensão passa no filtro
     for (String e : extensoesArquivos) {
       if (name.endsWith(e)) return true;
     }
     return false; // não achou
   }


}

Vamos ver como ficou:



quinta-feira, 29 de dezembro de 2011

Operações com Datas

Se lembrarmos da definição de Data que representa o número de  milisegundos que passaram deste 1º de Janeiro de 1970 00:00:00.000 GMT. Temos que as operações operações de adicionar ou substrair tempos podem ser manuseadas como operações matemáticas com inteiros:

import java.util.Date;


public class ExemploData3 {
 public static void main(String[] args) {
Date agora = new Date();
long t = agora.getTime();

// 7 dias atrás:
// equivale a achar t menos
//  7 dias x 24 horas x 60 minutos x 60 segundos x 1000 milisegundos
Date seteDias = new Date( t - 7 * 24 * 60 * 60 * 1000);
System.out.println("Sete dias atras era " + seteDias);

// daqui a sete dias
seteDias = new Date( t - 7 * 24 * 60 * 60 * 1000);
System.out.println("Daqui a sete dias serao " + seteDias);
 }
}


Esta é uma classe funcional mas não é a melhor forma de realizar operações sobre datas. O Java possui uma classe denominada java.util.Calendar que permite trabalhar melhor com estes valores:


import java.util.Calendar;
import java.text.SimpleDateFormat;


public class ExemploData4 {


    public static void main(String[] args) {
Calendar data = Calendar.getInstance( );
// formatação a ser utilizada para apresentação das datas
SimpleDateFormat sfmt = new SimpleDateFormat("dd/MM/yyyy '-' hh:mm:ss");
System.out.println("data (agora): " + sfmt.format(data.getTime()));

// obter 2 anos atras
Calendar doisAnosAtras = data;
doisAnosAtras.add(Calendar.DAY_OF_YEAR, -(365*2));
System.out.println("dois anos atras : " +
sfmt.format(doisAnosAtras.getTime()));

Calendar doisAnosAtrasSegundaOpcao = data;
doisAnosAtrasSegundaOpcao.add(Calendar.YEAR, -2);
System.out.println("dois anos atras (segunda opcao): " +
sfmt.format(doisAnosAtrasSegundaOpcao.getTime()));

// compara as datas
// existe um método Calendar.before(data) veja na documentação 
if (doisAnosAtras.after(data)) {
  System.out.println("doisAnosAtras tem data posterior a data");
} else {
  System.out.println("doisAnosAtras tem data anterior a data");
}

if (doisAnosAtrasSegundaOpcao.equals(doisAnosAtras)) {
  System.out.println("doisAnosAtrasSegundaOpcao igual a doisAnosAtras");
} else {
  System.out.println("doisAnosAtrasSegundaOpcao diferente de doisAnosAtras");
}
    }
}



quarta-feira, 28 de dezembro de 2011

java.lang.Date

Anteriormente à especificação JDK 1.1, a classe Date tinha várias funções. Atualmente esta classe representa um instante no tempo contendo data e hora com precisão de até milisegundos. É necessário a utilização de duas classes adicionais DateFormat e Calendar para permitir a formatação da apresentação da data e para realizar a conversão de datas.

Os formatos dos construtores desta classe são:

  • Date()
  • Date(long data)
    • data é um inteiro que representa a quantidade de milisegundos contados a partir de 01/01/1970.

Os outros formatos para os construtores não devem ser utilizados (são considerados deprecated). Para o nosso exemplo utilizarei a classe SimpleDateFormat. Esta classe utiliza um padrão de formatação que é montada como uma string. Existem diversas letras de formatação, que estão resumidas abaixo:



Para colocar um texto que não será interpretado, basta escrevê-lo entre aspas simples ('). Veja no exemplo abaixo como escrevemos as. A repetição das letras gera variações na interpretação do formato pelo Java. Por exemplo, se utilizamos MMM temos o mês em texto na forma de uma abreviatura, se forma MMMM temos por extenso completo, M temos o mês em número, e finalmente MM temos o mês no formato numérico com 2 dígitos.

Vamos ver um exemplo de uma classe simples:


import java.util.Date;
import java.text.SimpleDateFormat;


public class ExemploData {  
     
 public static void main(String[] args) {
   Date Agora = new Date(); // obtem o datastamp da hora da execução


   // impressão usando
   System.out.println("Usando toString = " + Agora.toString());


   // Use a SimpleDateFormat to print the date our way.
   SimpleDateFormat fmt = new SimpleDateFormat ("E dd/MM/yyyy 'as' hh:mm:ss a zzz");
   System.out.println("Usando formatter= " + fmt.format(Agora)); 
   fmt = new SimpleDateFormat ("MMM");
   System.out.println("Mes (abreviado) = " + fmt.format(Agora)); 
   fmt = new SimpleDateFormat ("MMMM");
   System.out.println("Mes (completo)  = " + fmt.format(Agora)); 
 }  
}


O resultado pode ser visto abaixo:



segunda-feira, 26 de dezembro de 2011

Conectar ao um banco de dados MS Access via JDBC

Vamos ver um exemplo simples para acessar um banco de dados em MS Access. Para isto temos que realizar 6 passos indicados na classe abaixo:

import java.awt.*;

import java.sql.*;
public class Connect {
    public static void main(String[] av) {
        // teste é o nome da fonte de dados no ODBC
        String dbURL = "jdbc:odbc:teste"; 
        try {
            // 1. carregar o driver de acesso ao banco de dados que desejamos
    //    neste caso é a bridge para ODBC: jdbc-odbc bridge driver
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");


            System.out.println("Abrindo conexao com "+dbURL);


    // 2. abrir a conexão
    // os parâmetros são:
    //    URL, usuario, senha
            Connection conn = DriverManager.getConnection(dbURL, "admin", "");    


            // Se o objeto SQLWarning estiver disponivel,
            // apresentar na tela os avisos
            SQLWarning warn = conn.getWarnings( );
            while (warn != null) { // podem existir diversos avisos
                System.out.println("SQLState: " + warn.getSQLState( ));
                System.out.println("Messagem: " + warn.getMessage( ));
                System.out.println("Vendor:   " + warn.getErrorCode( ));
                System.out.println("");
                warn = warn.getNextWarning( );
            }


// 3. criar um objeto tipo Statement que será utilizado 
//para buscar os dados do banco de dados
Statement stmt = conn.createStatement( );


// 4. executar a query para buscar os dados
ResultSet rs = stmt.executeQuery("select * from Tabela1");


// 5. mostrar resultados
while (rs.next()) {
int id = rs.getInt("codigo");
String s = rs.getString("descricao");
Date dt = rs.getDate("data");

System.out.println("Codigo  = "+Integer.toString(id));
System.out.println("   Desc = "+s);
System.out.println("   Data = "+dt.toString());
}


// 6. fechar os objetos
rs.close();
stmt.close();
            conn.close(); 


        } catch (ClassNotFoundException e) {
            System.out.println("Nao foi possivel carregar o driver " + e);
        } catch (SQLException e) {
            System.out.println("Acesso ao banco de dados falhou " + e);
        }
    }
}

Vejamos como funciona a classe:

Para que a classe funcione é necessário criar uma entrada no gerenciador de fontes de dados do ODBC como indicado na figura abaixo: