Como resolver problema com multiple points na leitura de txt?

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

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.

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

PROBLEMA RESOLVIDO

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(",", "."));

Ótima solução tb

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?

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?

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());

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

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

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.

1 curtida