File f1 = new File(path);
String[] files = f1.list();
Eu obtenho um String[] contendo arquivos (e diretórios) contidos no diretório informado na variável path. Gostaria de submeter individualmente cada posição desse array a um método qualquer.
O problema que eu estou tendo aqui é que dentro do diretório informado (variável path) encontram-se alguns arquivos com nomes acentuados e os caracteres acentuados do arquivo estão sendo armazenados no array como “?”.
Exemplo:
O arquivo Observación.doc está sendo armazenado como Observaci?n.doc.
Quando tento executar alguma operação sobre o arquivo informado nessa posição do array, recebo um FileNotFoundException, por q esse caracter foi substituído e não existe nenhum arquivo chamado “Observaci?n.doc”.
Eu também não gosto de acentuar nomes de arquivos, mas o sistema de arquivos que o programa analiza não está sob o meu controle e eu não tenho como impedir os usuários de criarem arquivos dessa forma.
pelo jeito, a codificação do sistema operacional está diferente da máquina virtual.
como vc tem uma array de string, pode pegar cada item e colocar a codificação correta. Na maioria dos casos é mudar de ISO-8859-1 para UTF-8 ou vice versa.
tem na api por getBytes(String charsetName) ou pelo construtor String(byte[] bytes, String charsetName).
parace q com os streams vc pode jogar os bytes da string em vez do objeto String direto q ai não da problema. Mas existem casos tb que vc não quer trabalhar com streams, como por exemplo apenas manipular o nome de arquivos.
O que é mais estranho é que não consegui replicar o seu problema. Eu tenho o seguinte programa:
import java.util.*;
import java.io.*;
import java.security.*;
import java.nio.*;
import java.nio.channels.*;
import java.math.*;
class AcharArquivosIguais {
public AcharArquivosIguais() {
}
public void listarArquivos (File diretorio, Set<File> arquivos) {
File[] listagem = diretorio.listFiles();
for (File f : listagem) {
if (f.isDirectory() && !f.getName().equals (".") && !f.getName().equals ("..")) {
listarArquivos (f, arquivos);
} else {
arquivos.add (f);
}
}
}
private static byte[] buffer = new byte[1024*1024];
private static String hashFile (File arq) {
String s = "error";
try {
MessageDigest dgst = MessageDigest.getInstance ("SHA1");
FileInputStream fis = new FileInputStream (arq);
int nBytes;
dgst.reset();
while ((nBytes = fis.read (buffer)) > 0) {
dgst.update (buffer, 0, nBytes);
}
byte[] bytes = dgst.digest();
fis.close();
BigInteger bd = new BigInteger (bytes);
s = bd.toString (16);
} catch (NoSuchAlgorithmException ex) {
} catch (IOException ex) {
ex.printStackTrace();
}
return s;
}
public void acharArquivosIguais (Collection<File> arquivos) {
Map<String, List><File>> hash2file = new TreeMap<String, List><File>>();
for (File f : arquivos) {
System.err.print ("\r" + f.getName());
String hash = hashFile (f);
List<File> files;
if (!hash2file.containsKey (hash)) {
files = new ArrayList<File>();
hash2file.put (hash, files);
} else {
files = hash2file.get (hash);
}
files.add (f);
}
System.out.println ();
// Agora vamos ver, nessa lista, que arquivos têm mais de uma entrada.
boolean arquivosRepetidos = false;
for (Map.Entry<String, List><File>> h2f : hash2file.entrySet()) {
if (h2f.getValue().size() > 1) {
arquivosRepetidos = true;
System.out.println ("---");
System.out.println (h2f.getValue().size() + " arquivos com o hash " + h2f.getKey() + ":");
System.out.println ("---");
for (File f : h2f.getValue()) {
System.out.println (" " + f);
}
}
}
if (!arquivosRepetidos) {
System.out.println ("Não foram encontrados arquivos repetidos.");
}
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println ("Sintaxe: java -cp . AcharArquivosIguais diretorio");
System.exit (1);
}
AcharArquivosIguais aai = new AcharArquivosIguais();
Set<File> arquivos = new TreeSet<File>();
System.err.println ("Procurando os arquivos...");
aai.listarArquivos (new File (args[0]), arquivos);
System.err.println ("Lendo os arquivos...");
aai.acharArquivosIguais (arquivos);
}
}
Eu rodei o programa acima em um diretório contendo arquivos com nomes contendo letras acentuadas e espaços, e ele não me lançou nenhuma IOException referente a arquivos não encontrados. Será que é alguma outra coisa (por exemplo, você guardou os nomes em um banco de dados, e depois tentou pegar o nome com acentos - que veio errado, por algum motivo - e tentou localizar o arquivo?)
Para simplificar (ou piorar mais ainda) o problema, apresento abaixo o método dito… problemático:
public static ArrayList<String> getTree(String path) {
ArrayList<String> output = new ArrayList();
if (path.charAt(path.length() - 1) != '/') {
path = path + "/";
}
File f1 = new File(path);
File type_check;
String[] files = f1.list();
for (int i = 0; i < files.length; i++) {
if (!files[i].matches(".snapshot")) { // RETIRAR ISTO
type_check = new File(path + files[i]);
if (type_check.isDirectory()) {
output.addAll(getTree(path + files[i]));
} else {
output.add(path + files[i]);
}
}
}
return output;
}
Como podem observar, é um método simples que retorna de forma recursiva a estrutura de diretórios abaixo do caminho informado na variável path. Cada posição do array de saída será utilizada como parâmetro em um outro método qualquer. Como pode-se observar, esses dados não passaram por nenhum banco de dados.
Realmente o problema está parecendo mesmo ser de codificação conforme o que já foi sugerido aqui.
Observei que não consigo exibir bytes acima de 127. Entretanto entre 0 e 127, não possuo caracteres como “ç, á, é, í, ó, ú, ã, etc” o que me leva a pensar que esses devem ter valores superiores a 127. segue o exemplo (Usando getBytes como também já foi sugerido):
Adaptei o método que você sugeriu ao meu programa e também adaptei os métodos restantes para trabalharem com “File” no lugar de “String”, entretanto o erro persiste.
public static List<File> getTree(File path) {
List<File> output = new ArrayList<File>();
File[] files = path.listFiles();
for (File file : files) {
if (!file.getName().matches(".snapshot")) {
if (file.isDirectory()) {
output.addAll(getTree(file));
} else {
output.add(file);
}
}
}
return output;
}
public static String getMD5Checksum(File file) throws Exception {
byte[] b = createChecksum(file);
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
public static byte[] createChecksum(File file) throws Exception {
InputStream fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
MessageDigest complete = MessageDigest.getInstance("MD5");
int numRead;
do {
numRead = fis.read(buffer);
if (numRead > 0) {
complete.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
return complete.digest();
}
Cada posição da lista obtida pelo método getTree() é submetida ao método getMD5Checksum() que por sua vez chama o método createChecksum().
O programa funciona muito bem até chegar ao arquivo /u/s0b4/2d/filed/préreq.txt, quando a seguinte exception ocorre:
java.io.FileNotFoundException: /u/s0b4/2d/filed/pr?req.txt (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at treecomp.TreeComp.createChecksum(TreeComp.java:92)
at treecomp.TreeComp.getMD5Checksum(TreeComp.java:109)
at treecomp.TreeComp.startComp(TreeComp.java:124)
at treecomp.frm_main.jButton1ActionPerformed(frm_main.java:97)
at treecomp.frm_main.access$000(frm_main.java:16)
at treecomp.frm_main$1.actionPerformed(frm_main.java:45)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
at java.awt.Component.processMouseEvent(Component.java:6038)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
at java.awt.Component.processEvent(Component.java:5803)
at java.awt.Container.processEvent(Container.java:2058)
at java.awt.Component.dispatchEventImpl(Component.java:4410)
at java.awt.Container.dispatchEventImpl(Container.java:2116)
at java.awt.Component.dispatchEvent(Component.java:4240)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4322)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3986)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3916)
at java.awt.Container.dispatchEventImpl(Container.java:2102)
at java.awt.Window.dispatchEventImpl(Window.java:2429)
at java.awt.Component.dispatchEvent(Component.java:4240)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)
Observe que eu não usei String em lugar nenhum, mas mesmo assim o programa substitui o “é” por “?”.
Parece que inevitávelmente terei que resolver o problema da codificação, o problema maior é que eu não faço num idéia de por onde começar.
Podes me ajudar com isso?
Falar “linux” é igual a falar “Windows”. Dá uma idéia aproximada mas não muito boa, se for algum problema ambiental.
Qual Linux? (Fedora, Ubuntu, SuSE etc.?)
Qual a versão do Java que você está usando? poste a informação de “java -version”
Qual a variável de ambiente que determina que linguagem está sendo usada? (echo $LANG)
Após mudar o valor da variável, a primeira coisa que percebi é que o NetBeans ficou em português.
Porém o problema permanece, apesar de agora no lugar de “?” é exibido um quadrado no lugar dos caracteres não reconhecidos.
Outra coisa que notei é que fazendo aquele mesmo teste:
Sabe dizer o tipo do sistema de arquivos em que esses arquivos se encontram? Como ele está montado? Normalmente sistemas em FAT precisam ter suporte ao code page 437 (CP437) e estarem montados dessa forma.
O kernel também precisa ter suporte a esse encoding, espero que o da Red Hat já tenha.
A variável de ambiente LC_ALL normalmente tem que ficar em pt_BR. Outra opção para LANG é pt_BR.ISO-8859-1.
Dica: crie arquivos com acentos em um outro diretório que não seja esse /u/blablabla que você mencionou. Pelo nome, dá a impressão que foi montado via NFS ou outro sistema remoto. Crie em um diretório local na sua máquina Linux, e veja se dá o mesmo problema.