Como resolver problema com multiple points na leitura de txt?

10 respostas Resolvido
java
J

Não estou conseguindo compreender esse erro alguem pode me dar uma luz?

Erro informado

Exception in thread "main" java.lang.NumberFormatException: multiple points
	at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1914)
	at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.base/java.lang.Double.parseDouble(Double.java:651)
	at java.base/java.lang.Double.valueOf(Double.java:614)
	at Application.controller.NegociacaoController.controle(NegociacaoController.java:53)
	at Application.Main.main(Main.java:15)
public class NegociacaoController {

	// Método para verificar se o arquivo existe e não é um diretório
	public static boolean isFileExists(File file) {
		return file.exists() && !file.isDirectory();
		}

	public static void controle(String paht2) throws IOException {

		String path = "C:\\Users\\vigjo\\OneDrive\\Área de Trabalho\\01-PROJEOT-FANATICOS\\COMPRA-VENDA-NUINVEST\\extrato\\original/extrato-original.csv";
		
		String[] dados;
		FileReader arquivo = new FileReader(new File(path));

		// xxxxxxxxxxxxxxxxxxxxxxxxx
		File file = new File(path);
		FileInputStream entrada = new FileInputStream(new File(path));
		if (isFileExists(file)) {
			System.out.println("Arquivo existe");
			Scanner sc = new Scanner(arquivo);
			ArrayList<Negociacao> lista = new ArrayList<>();

			Double preco;
			int cont = 0;
			while (sc.hasNext()) {
				Negociacao neg = new Negociacao();
				String linha = sc.nextLine();

				if (linha != null && !linha.isEmpty()) {
					// System.out.println(linha);
					dados = linha.split("\\;");

					neg.setDt_negociacao(dados[0]);
					neg.setConta(Integer.parseInt(dados[1]));
					neg.setAtivo(dados[2].replace("\"", ""));
					neg.setPreco_cota(Double.parseDouble(dados[3].replaceAll(",", ".")));
					neg.setQtd_compra(Integer.parseInt(dados[4]));
					neg.setQtd_venda(Integer.valueOf(dados[5]));
					//ATE AQUI ESTA LENDO E SETANDO NA CLASSE

					//AQUI PRA BAIXO DA O PROBLEMA DE  multiple points
//					neg.setTotalCompra(Double.valueOf(dados[6].replaceAll(",", ".")));
//				    neg.setTotalVenda(Double.valueOf(dados[7].replaceAll(",", ".")));

					
					lista.add(neg);

				}

			}
			for (Negociacao n : lista) {
				System.out.println("Contador: " + cont++);
				System.out.println(n);
				System.out.println();

			}


		} else {
			System.out.println("File doesn't exist or program doesn't have access " + "to the file");
		}

	}
}

meu txt

16/12/2022;3566708;“MXRF11”;9,91;15;0;148,65;0,00
19/12/2022;3566708;“MXRF11”;9,81;1;0;9,81;0,00
19/12/2022;3566708;“MXRF11”;9,81;11;0;107,91;0,00
19/12/2022;3566708;“MXRF11”;9,81;3;0;29,43;0,00
23/12/2022;3566708;“IRBR3F”;0,92;5;0;4,60;0,00
23/12/2022;3566708;“MXRF11”;10,01;10;0;100,10;0,00
26/12/2022;3566708;“KISU11”;8,05;7;0;56,35;0,00
26/12/2022;3566708;“VGHF11”;9,23;6;0;55,38;0,00
26/12/2022;3566708;“VGHF11”;9,27;10;0;92,70;0,00
27/12/2022;3566708;“VGHF11”;9,30;19;0;176,70;0,00
28/12/2022;3566708;“VGHF11”;9,35;20;0;187,00;0,00
02/01/2023;3566708;“HABT11”;88,90;1;0;88,90;0,00
02/01/2023;3566708;“IRBR3F”;0,90;2;0;1,80;0,00
02/01/2023;3566708;“KISU11”;8,00;4;0;32,00;0,00
02/01/2023;3566708;“KISU11”;8,00;1;0;8,00;0,00
04/01/2023;3566708;“HABT11”;90,65;1;0;90,65;0,00
06/01/2023;3566708;“KISU11”;7,99;8;0;63,92;0,00
06/01/2023;3566708;“SNFF11”;87,96;1;0;87,96;0,00
09/01/2023;3566708;“KISU11”;7,99;1;0;7,99;0,00
09/01/2023;3566708;“SNFF11”;88,19;1;0;88,19;0,00
10/01/2023;3566708;“KISU11”;8,00;8;0;64,00;0,00
11/01/2023;3566708;“AMAR3F”;1,39;1;0;1,39;0,00
11/01/2023;3566708;“KISU11”;8,00;13;0;104,00;0,00
13/01/2023;3566708;“AMAR3F”;1,32;1;0;1,32;0,00
13/01/2023;3566708;“AMER3F”;3,61;1;0;3,61;0,00
17/01/2023;3566708;“KISU11”;7,99;18;0;143,82;0,00
18/01/2023;3566708;“IRBR3F”;1,13;0;7;0,00;7,91
18/01/2023;3566708;“VCRI11”;8,97;36;0;322,92;0,00
18/01/2023;3566708;“VCRI11”;8,97;15;0;134,55;0,00
19/01/2023;3566708;“HABT11”;90,74;6;0;544,44;0,00
19/01/2023;3566708;“RANI3F”;7,83;10;0;78,30;0,00
23/01/2023;3566708;“VCRI11”;8,95;20;0;179,00;0,00
23/01/2023;3566708;“VGHF11”;9,18;24;0;220,32;0,00
24/01/2023;3566708;“MGLU3F”;4,10;20;0;82,00;0,00
24/01/2023;3566708;“MGLU3F”;4,10;1;0;4,10;0,00
24/01/2023;3566708;“MGLU3F”;4,10;1;0;4,10;0,00
24/01/2023;3566708;“MGLU3F”;4,10;2;0;8,20;0,00
24/01/2023;3566708;“MGLU3F”;4,10;1;0;4,10;0,00
25/01/2023;3566708;“VCRI11”;8,98;9;0;80,82;0,00
26/01/2023;3566708;“MGLU3F”;4,24;1;0;4,24;0,00
31/01/2023;3566708;“SNFF11”;88,89;0;2;0,00;177,78
31/01/2023;3566708;“VCRI11”;8,94;20;0;178,80;0,00
02/02/2023;3566708;“KISU11”;7,98;10;0;79,80;0,00
02/02/2023;3566708;“RANI3F”;8,05;0;3;0,00;24,15
02/02/2023;3566708;“RANI3F”;8,05;0;7;0,00;56,35
03/02/2023;3566708;“KISU11”;8,05;30;0;241,50;0,00
06/02/2023;3566708;“VGHF11”;9,08;10;0;90,80;0,00
07/02/2023;3566708;“VGHF11”;9,01;11;0;99,11;0,00
08/02/2023;3566708;“HABT11”;88,31;1;0;88,31;0,00
09/02/2023;3566708;“KISU11”;7,99;3;0;23,97;0,00
09/02/2023;3566708;“MGLU3F”;4,24;0;2;0,00;8,48
09/02/2023;3566708;“MGLU3F”;4,24;0;5;0,00;21,20
09/02/2023;3566708;“MGLU3F”;4,24;0;10;0,00;42,40
09/02/2023;3566708;“MGLU3F”;4,24;0;6;0,00;25,44
09/02/2023;3566708;“MGLU3F”;4,24;0;1;0,00;4,24
09/02/2023;3566708;“MGLU3F”;4,24;0;1;0,00;4,24
09/02/2023;3566708;“MGLU3F”;4,24;0;1;0,00;4,24
09/02/2023;3566708;“VCRI11”;8,81;12;0;105,72;0,00
09/02/2023;3566708;“VSLH11”;7,36;1;0;7,36;0,00
10/02/2023;3566708;“VCRI11”;8,74;1;0;8,74;0,00
16/02/2023;3566708;“KISU11”;7,96;2;0;15,92;0,00
23/02/2023;3566708;“NUBR33”;4,13;1;0;4,13;0,00
24/02/2023;3566708;“NUBR33”;4,11;0;1;0,00;4,11
07/03/2023;3566708;“DEVA11”;82,40;14;0;1.153,60;0,00
07/03/2023;3566708;“KISU11”;7,99;0;1;0,00;7,99
07/03/2023;3566708;“KISU11”;7,99;0;1;0,00;7,99
07/03/2023;3566708;“KISU11”;7,99;0;10;0,00;79,90
07/03/2023;3566708;“KISU11”;7,99;0;1;0,00;7,99
07/03/2023;3566708;“KISU11”;7,99;0;2;0,00;15,98
07/03/2023;3566708;“KISU11”;7,99;0;36;0,00;287,64
07/03/2023;3566708;“KISU11”;7,99;0;1;0,00;7,99
07/03/2023;3566708;“KISU11”;7,99;0;3;0,00;23,97
07/03/2023;3566708;“KISU11”;7,99;0;25;0,00;199,75
07/03/2023;3566708;“KISU11”;7,99;0;13;0,00;103,87
07/03/2023;3566708;“KISU11”;7,99;0;2;0,00;15,98
07/03/2023;3566708;“KISU11”;7,99;0;2;0,00;15,98
07/03/2023;3566708;“KISU11”;7,99;0;8;0,00;63,92
07/03/2023;3566708;“VCRI11”;8,40;2;0;16,80;0,00
09/03/2023;3566708;“CVCB3”;4,11;100;0;411,00;0,00
09/03/2023;3566708;“DEVA11”;75,69;0;10;0,00;756,90
09/03/2023;3566708;“DEVA11”;75,70;0;1;0,00;75,70
09/03/2023;3566708;“DEVA11”;75,70;0;1;0,00;75,70
09/03/2023;3566708;“DEVA11”;75,70;0;1;0,00;75,70
09/03/2023;3566708;“DEVA11”;75,71;0;1;0,00;75,71
09/03/2023;3566708;“VGHF11”;9,06;31;0;280,86;0,00
09/03/2023;3566708;“VGHF11”;9,06;40;0;362,40;0,00
10/03/2023;3566708;“VCRI11”;8,38;2;0;16,76;0,00
14/03/2023;3566708;“VGHF11”;9,06;4;0;36,24;0,00
15/03/2023;3566708;“PETR4F”;22,98;2;0;45,96;0,00
15/03/2023;3566708;“VGHF11”;9,08;2;0;18,16;0,00
21/03/2023;3566708;“UNIP3F”;69,98;2;0;139,96;0,00
21/03/2023;3566708;“VGHF11”;9,06;2;0;18,12;0,00
04/04/2023;3566708;“DEVA11”;61,08;2;0;122,16;0,00
04/04/2023;3566708;“VCRI11”;7,88;4;0;31,52;0,00
04/04/2023;3566708;“VGHF11”;8,89;5;0;44,45;0,00
05/04/2023;3566708;“VGHF11”;8,86;1;0;8,86;0,00
10/04/2023;3566708;“VGHF11”;8,79;2;0;17,58;0,00
11/04/2023;3566708;“AMAR3F”;0,65;1;0;0,65;0,00
11/04/2023;3566708;“AMAR3F”;0,65;3;0;1,95;0,00
11/04/2023;3566708;“AMAR3F”;0,65;3;0;1,95;0,00
11/04/2023;3566708;“AMAR3F”;0,65;89;0;57,85;0,00
12/04/2023;3566708;“SYNE3F”;3,24;3;0;9,72;0,00
17/04/2023;3566708;“AMAR3F”;0,76;1;0;0,76;0,00
17/04/2023;3566708;“AMAR3F”;0,77;3;0;2,31;0,00
17/04/2023;3566708;“VGHF11”;8,78;2;0;17,56;0,00
04/05/2023;3566708;“AMAR3F”;0,67;2;0;1,34;0,00
04/05/2023;3566708;“DEVA11”;47,85;1;0;47,85;0,00
04/05/2023;3566708;“VGHF11”;8,94;4;0;35,76;0,00
08/05/2023;3566708;“HABT11”;84,00;1;0;84,00;0,00
09/05/2023;3566708;“POSI3F”;7,08;10;0;70,80;0,00
09/05/2023;3566708;“VCRI11”;7,92;1;0;7,92;0,00
12/05/2023;3566708;“TECN3F”;3,17;4;0;12,68;0,00
12/05/2023;3566708;“TECN3F”;3,18;1;0;3,18;0,00
12/05/2023;3566708;“TECN3F”;3,19;62;0;197,78;0,00

10 Respostas

W
Solucao aceita

O problema é quando vc invoca o parseDouble com um argumento que tem mais 2 um ponto decimal. Por exemplo:

Double.parseDouble("1.234.56");

No seu CSV, na sétima coluna, tem uma linha com o valor 1.153,60. Neste valor vc substitui a virgula por um ponto deixando ele assim 1.153.60 e gerando a exceção.

Como vc está usando Scanner para ler o arquivo, eu recomendo que vc tire um maior proveito dela simplificando seu código da seguinte maneira:

public static void controle(String paht2x) throws IOException {
  File file = new File("extrato-original.csv");

  if (isFileExists(file)) {
    Scanner sc = new Scanner(new FileReader(file))
      .useDelimiter("[;\n]")
      .useLocale(Locale.forLanguageTag("pt-BR"));

    ArrayList<Negociacao> lista = new ArrayList<>();

    int cont = 0;

    while (sc.hasNext()) {
      Negociacao neg = new Negociacao();

      neg.setDt_negociacao(sc.next());
      neg.setConta(sc.nextInt());
      neg.setAtivo(sc.next().replace("\"", ""));
      neg.setPreco_cota(sc.nextDouble());
      neg.setQtd_compra(sc.nextInt());
      neg.setQtd_venda(sc.nextInt());
      neg.setTotalCompra(sc.nextDouble());
      neg.setTotalVenda(sc.nextDouble());

      lista.add(neg);
    }

    for (Negociacao n : lista) {
      System.out.println("Contador: " + cont++);
      System.out.println(n);
      System.out.println();
    }
  }
}

Repare em como eu criei o Scanner.

Scanner sc = new Scanner(new FileReader(file))
  .useDelimiter("[;\n]")
  .useLocale(Locale.forLanguageTag("pt-BR"));

O .useDelimiter("[;\n]") me permite ler coluna por coluna do CSV sem precisar usar split e .useLocale(Locale.forLanguageTag("pt-BR")) nos permite ser números no nosso formato que usa vírgula para separar a parte quebrada e ponto para agrupar de 3 em 3 sem precisar usar o replace.

Obs.: O problema desta sugestão é que se houver linhas em branco, ou colunas falando, vai dar erro, mas se os dados vierem sempre certinhos como no seu exemplo dá tudo certo.

J

Show de bola. Obrigado eu estava a 5 dias tentando resolver isso. Vlw

PROBLEMA RESOLVIDO

W

Fico feliz!

Mas neste meio tempo eu pensei em uma solução mais imediata e que não exige tanta modificação no seu código original.

Basta primeiro substituir os pontos por uma string vazia e só depois substituir a virgula por ponto. Assim:

Double.parseDouble("1.234,56".replace(".", "").replace(",", "."));
J

Ótima solução tb

J

Outra dúvida…
Caso que tenha uma coluna que eu deseja não lista ela, como eu faço?

por ex: Colunas => data | conta | ativo
eu quero não lista a coluna conta, vc pode me ajudar tb?

J

Outra dúvida…
Caso que tenha uma coluna que eu deseja não lista ela, como eu faço?

por ex: Colunas => data | conta | ativo
eu quero não lista a coluna conta, vc pode me ajudar tb?

W

Usando aquele jeito com Scanner? Se for, basta vc dar um next “em falso”, tipo assim:

obj.setData(sc.next());
sc.next(); // 👈🏽 "next em falso"
obj.setAtivo(sc.next());
S

Veja qual é o conteúdo de dados[6] e dados[7] quando o problema acontece.

J

Amigo ja consegui resolver. vlw pela dica.
Aonde que eu marco que a pergunta foi resolvida?

H

Sei que o tópico já foi resolvido, mas gostaria de complementar com alguns detalhes.

A solução com Scanner sugerida pelo @wldomiciano funciona em muitos casos, mas se está lidando com um CSV (que é o formato do seu arquivo), muitas vezes é melhor usar bibliotecas dedicadas a este formato, em vez de fazer tudo na mão.

Sei que no caso específico não acontecem os problemas que vou citar, mas é algo pra se ter em mente no futuro.

Por exemplo, em um CSV é perfeitamente válido ter o separador como parte de um campo:

"campo 1";"campo 2; tem um monte de ponto-e-vírgula; e não é separador; tudo isso é o campo 2; não pode separar";"campo 3"

Repare que no campo 2, há vários ; dentro do texto. Se usarmos o Scanner considerando que ; é o separador, ele vai quebrar o campo 2 em vários. Mas isso estará errado, porque os ; que estão dentro das aspas fazem parte do texto do campo 2. O valor dele é toda a string "campo 2; tem um monte de ponto-e-vírgula; e não é separador; tudo isso é o campo 2; não pode separar".

Outro caso é quando dentro do próprio texto também tem aspas. Por exemplo:

"campo 1";"campo 2 tem aspas ""dentro do campo""."

No caso, o valor do campo 2 é o texto campo 2 tem aspas "dentro do campo". (sim, dentro do texto coloca-se "" para indicar que é o próprio caractere ", e não as aspas de fechamento do campo - ou seja, essas aspas fazem parte do texto). Se vc fizer um replace removendo todas as aspas, vai acabar removendo essas também. Claro que daria para mudar removendo somente do início e fim, mas pra que ter esse trabalho todo quando uma biblioteca já faz isso pra vc?

E tem também o caso de ter quebras de linha no meio do texto:

"campo 1";"campo 2 tem
várias linhas
e é perfeitamente válido";"campo 3"

Sim, isso é um CSV válido, o campo 2 é um texto com 3 linhas. Se vc configurar o Scanner para usar a quebra de linha como separador, ele erroneamente vai quebrar esse campo em 3. Já um parser de CSV trata isso pra vc automaticamente.


Existem várias bibliotecas para manipular CSV e que tratam desses casos (e muitos outros) pra vc. Por exemplo, o commons-csv:

import java.io.File;
import java.io.FileReader;
import java.text.NumberFormat;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;

public class Teste {
    public static void main(String[] args) throws Exception {
        NumberFormat doubleParser = NumberFormat.getNumberInstance(Locale.forLanguageTag("pt-BR"));
        List<Negociacao> lista = new ArrayList<>();
        File file = new File("extrato-original.csv");
        CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setDelimiter(';').build();
        for (CSVRecord record : csvFormat.parse(new FileReader(file))) {
            Negociacao neg = new Negociacao();
            neg.setDt_negociacao(record.get(0));
            neg.setConta(Integer.parseInt(record.get(1)));
            neg.setAtivo(record.get(2));
            neg.setPreco_cota(doubleParser.parse(record.get(3)).doubleValue());
            neg.setQtd_compra(Integer.parseInt(record.get(4)));
            neg.setQtd_venda(Integer.parseInt(record.get(5)));
            neg.setTotalCompra(doubleParser.parse(record.get(6)).doubleValue());
            neg.setTotalVenda(doubleParser.parse(record.get(7)).doubleValue());

            lista.add(neg);
        }

        // mostrar os dados da lista, etc
    }
}

Usei setDelimiter(';') para indicar que o ; é o separador (o default é a vírgula). A grande vantagem é que ele trata dos casos mencionados acima, quando há ponto-e-vírgula, aspas ou quebras de linha dentro do texto. Outra vantagem é que ele também já retorna o texto sem as aspas no início e fim, já que elas são delimitadores e não fazem parte do texto em si (ou seja, não precisa de replace).

E para fazer o parsing dos números, usei um NumberFormat, com o locale “pt-BR” (português brasileiro), que sabe tratar os separadores de milhares e decimais corretamente.

Além disso, a biblioteca também possui várias outras opções de configuração, como ignorar ou não linhas vazias, considerar se a primeira linha é o cabeçalho, dar nomes para as colunas, etc. Não deixe de ler a documentação (ou então pesquise por outras libs e encontre uma que se adapte melhor ao seu caso).


Só pra citar outro exemplo, o OpenCSV já faz as conversões numéricas automaticamente pra vc. No caso, usei a versão 5.7.1, bastando adicionar no Maven:

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.7.1</version>
</dependency>

E na classe Negociacao, adicione as annotations:

import com.opencsv.bean.CsvBindByPosition;

public class Negociacao {
    @CsvBindByPosition(position = 0)
    private String dt_negociacao;
    @CsvBindByPosition(position = 1)
    private int conta;
    @CsvBindByPosition(position = 2)
    private String ativo;
    @CsvBindByPosition(position = 3, locale = "pt-BR")
    private double preco_cota;
    @CsvBindByPosition(position = 4)
    private int qtd_compra;
    @CsvBindByPosition(position = 5)
    private int qtd_venda;
    @CsvBindByPosition(position = 6, locale = "pt-BR")
    private double totalCompra;
    @CsvBindByPosition(position = 7, locale = "pt-BR")
    private double totalVenda;

    // getters e setters, construtor, etc
}

Repare que o locale é setado na própria annotation, e o OpenCSV faz a conversão automaticamente para vc. A leitura fica mais simples ainda:

import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import java.io.File;
import java.io.FileReader;
import java.util.List;

public class Teste {
    public static void main(String[] args) throws Exception {
        File file = new File("extrato-original.csv");
        CsvToBean<Negociacao> cb = new CsvToBeanBuilder<Negociacao>(new FileReader(file))
                .withType(Negociacao.class)
                .withSeparator(';')
                .build();
        List<Negociacao> lista = cb.parse();

        // mostrar a lista, etc
    }
}

Lembrando que o OpenCSV também trata os casos já citados (ter ponto-e-vírgula, aspas ou quebras de linha no meio do texto), além de ter opções de configuração (mudar o separador, ignorar linhas em branco, considerar ou não a primeira linha como cabeçalho, etc).


Claro que para os casos mais simples, talvez até compense fazer na mão. Mas basta complicar um pouquinho o CSV para que uma lib já comece a valer a pena, já que tratar esses casos especiais na mão começa a ficar chato e propenso a erros.

Criado 15 de maio de 2023
Ultima resposta 16 de mai. de 2023
Respostas 10
Participantes 4