Ordenação de string com números

12 respostas
Onixx2

Pessoal, gostaria de saber como faço para ordenar um array de arquivos por nome.
Estou utilizando o “org.apache.commons.io.comparator” para ordenar, só que eu caio naquele problema típico de números entre a string. Exemplo a ordem certa seria:
pagina1
pagina2
pagina3

pagina10
pagina11
pagina12

só que como o método de ordenação, ordena apenas string o resultado sai
pagina1
pagina11
pagina12
pagina13

pagina2
pagina20
pagina21
etc…

Vou continuar procurando na net, se encontrar posto aqui.

12 Respostas

erico_kl

não tem alguma forma de vc ordenar as páginas como o exemplo abaixo?:

pagina01 pagina02 ... pagina11 pagina12
Se você quiser essa ordenação mesmo vai ter que implementar o método compareTo do seu jeito, pois lembre que você está ordenando Strings e não números, e em String, “18” vem antes de “2”, assim como “abacate” vem antes de “abas”

E

Bom, basicamente você teria três casos em que os números são significativos:

a) O número fica no fim:

pereira10
pereira2

b) O número fica no começo:

20casas
2casas

c) O número fica no meio.

aba2cadabra
aba10caxi

No primeiro caso, é necessário pegar as 2 strings e ver se elas coincidem até chegar até a parte numérica.
No segundo caso, você precisa pegar a parte numérica, comparar, e se for igual comparar também a parte de strings.
No terceiro caso, eu gostaria que você definisse se “aba2cadabra” fica antes ou depois de “aba10caxi”

Onixx2

“aba2cadabra” viria antes de “aba10caxi” pois os numeros tem total prioridade, na verdade o texto é descarável só os números me interessam, de maneira que a lista de arquivos ficaria assim:

zz1zz
aa2xf
dskl3
4onst
blabla5
6_de_30
lks7lkfs

no caso do sexto paga-se o primeiro número, o segundo é descartável.

g4j

Psvtec:
“aba2cadabra” viria antes de “aba10caxi” pois os numeros tem total prioridade, na verdade o texto é descarável só os números me interessam, de maneira que a lista de arquivos ficaria assim:

zz1zz
aa2xf
dskl3
4onst
blabla5
6_de_30
lks7lkfs

no caso do sexto paga-se o primeiro número, o segundo é descartável.

Bem estranho este padrão aí. Imagine que vc tenha mais de 1000 arquivos e tiver arquivo sem número…
Bom, eu tentaria testar todos os caracteres. Quando encontrar um número, verifica se o seguinte também o é. Se sim, concatena e procura o próximo, senão, você já tem o seu número.

Para ordenar crie uma classe (Arquivo por ex.) que tenha o atributo número. Daí é só implementar Comparable.

G
import java.util.Comparator;


public class CampareStringAsNumbers implements Comparator<String>  {

	private int GetValueInt(String s){
		int value = 0;
		for (char c : s.toCharArray()) {
			//if (Character.isDigit(c)){//o q isso faz realmente?
			if (isNumber(c)){
				value = value * 10 + (c - '0');
			}
		}
		return value;
	}
	private boolean isNumber(char c) {
		return (c >= '0' && c <= '9');
	}
	
	@Override
	public int compare(String o1, String o2) {
		return GetValueInt(o1) - GetValueInt(o2);
	}
}
public static void main(String[] args) {
		List<String> l = new ArrayList<String>();
		l.add("pagina01");
		l.add("M|||LT|00|");
		l.add("|E100");
		l.add("pagina2");
		l.add("pa3gina01");
		Collections.sort(l, new CampareStringAsNumbers());
		
		for (String s : l) {
			System.out.println(s);			
		}
			
	}

veja se funciona ai

G

saída:

M|||LT|00|
pagina01
pagina2
|E100
pa3gina01

nel

Supondo que seja o primeiro caso, "String seguida de número", basicamente isso resolve:

List<String> list = new ArrayList<String>(5);
		list.add("palavra3");
		list.add("palavra1");
		list.add("palavra0");
		list.add("palavra4");
		list.add("palavra2");
		
		Collections.sort (list, new Comparator<String>() {

			@Override
			public int compare(String o1, String o2) {
				Integer i1 = new Integer (o1.split("[a-zA-Z]+")[1]);
				Integer i2 = new Integer (o2.split("[a-zA-Z]+")[1]);
				
				return i1.compareTo (i2);
			}
		});
		for (String str : list)
			System.out.println(str);

É a melhor solução ? Não. Eu sei que há falhas nisso e desconsidera a String, ou seja, não importa a precedência da String, ele apenas considera o número ao fim.
Há erro se não houver número na String, por exemplo. Mas só quis dar uma noção do que pode fazer. Para ficar "100%", é considerar tudo o que entanglement citou.

nel

Faz isso GilsonNunes: http://docs.oracle.com/javase/7/docs/api/java/lang/Character.html#isDigit(char)

G

obrigado Nel.

é q vi q na implementação fazia tanta coisa, e eu queria apenas saber se c in [‘0’…‘9’].

nel

obrigado Nel.

é q vi q na implementação fazia tanta coisa, e eu queria apenas saber se c in [‘0’…‘9’].

Sim, ele faz outras considerações importantes para verificar se é um digito, não somente o modelo “LATIN”.
É bem interessante seu uso e funciona bem, o que é mais importante.

T

Bom dia a todos!

PSVTEC, esse problema é clássico e também de fácil compreensão, vi que está ciente do porque a ordenação não funciona da maneira esperada, pois você está priorizando os números que na verdade são textos (String), dessa forma os números são apenas textos que não tem significado número, por isso não é possível efetuar cálculos, e nada que seja um comportamento numérico sem fazer uma conversão!

Você disse que estes nome são nomes de arquivos, correto?!
Estes arquivos são criados por você ou já é um legado ou você pode alterar o padrão sem interferir no projeto…

Eu já vi muito isso e de tudo que vi e fiz há duas soluções mais sensatas, a primeira delas e usar convenção, por exemplo:

00000001TextoQualquer
00000002TextoQualqueasdfasdfdas
00000003TextoQualqueroddffdfd
00000004TextoQualquerofasdfasdf

Lembre-se que os Zeros são apenas para adequar a convenção a prática, dessa forma os números serão reconhecidos e ordenados corretamente! Porque na String no momento da ordenação é verificada uma letra de cada vez, por isso o 2 vem antes do 20 e não antes do 3.

E outra solução é você criar outro método para :
Usar o número capturado do nome do arquivo para ser o index/flag da lista (Dictionary por exemplo) para alocar a posição do item na lista, exemplo:

Você pega o número do nome do arquivo e adiciona no INDEX referente ao número que está no nome do arquivo!
Assim terá uma lista na ordem correta…
Sei que não ficou muito claro, mas primeiro veja se a convenção é melhor opção…

Caso opte pela escrita de um método e tenha dúvidas, ajudaremos você na elaboração do mesmo…

Obs: Eu não conheço algum método da API Java que faça essa ordenação, por isso essas são minhas alternativas.
Abraços…

Onixx2

Obrigdo a todos que responderam!

respondendo o que me perguntaram, eu não tenho controle do nome dos arquivos, a única regra que rege o nome é que a ordem deve ser identificada no nome do arquivo, um outro processo fora do meu programa vai nomear o arquivo de qualquer maneira, contanto que o numero da pagina seja identificada exe.:"pagina1.txt", "1ºpagina.txt", "pg1.txt" ou apenas "1.txt".

O projeto está atrasado e precisei ser rápido, então eu resolvi o problema da primeira maneira que deu, provavelmente isso ajudará outras pessoas. Fiz da segunte maneira:

utilizei o códio para tirar apenas os números dos nomes dos arquivos:
public static int somenteDigitos(String palavra) {
    String digitos = "";
    char[] letras  = palavra.toCharArray();
    for (char letra : letras) {
        if(Character.isDigit(letra)) {
            digitos += letra;
        }
    }
    return Integer.parseInt(digitos);
}

Como o número extraido é necessariamente a posição no array, crio um array auxiliar pegando a posição que veio do nome do arquivo:

public static File[] NumeroAscendente(File[] files){
            File[] aux = new File[files.length];
            for(int i =0; i < files.length; i++ ){
                int x = somenteDigitos(files[i].getName());//pego a posição do array vindo do nome do arquivo
                //dessa forma se onome do arquivo é pagina15, esse arquivo será colocado na posição 15 do array
                
                if (aux.length > x-1) {//este if garante que o numero passado não seja maior que o tamanho do array
                    
                    aux[x - 1] = files[i];//aleatoriamente os arquivos são colocados 
                                          //na posição certa até que o array esteja completo
                }
            }            
            return aux;
        }//obs ao final do prcesso se a sequencia não for mantida, o array terá posições null, 
        //você deve trara-las, no meu caso, eu trato isso em outra parte do codigo.

Não é a forma mais limpa, mas vai resolver o problema de muita gente.
ah, tive que abrir mão de tratar o caso do nome do arquivo ser 10_de_30, nesse caso o número retornado será 1030 e provavelmente será maior que o array e por tanto ignorado :( fora isso, qualquer outra situação: "15ºpagina", "pag15ina", "pagina15" todas entrarão no indce 15 do array auxiliar. Se mais de uma pagina15 for identificada, apenas a ultima será considerada.

Criado 13 de agosto de 2012
Ultima resposta 14 de ago. de 2012
Respostas 12
Participantes 7