Convert a .txt file with doubles to an ArrayList

Viewed 150

I have a .txt file with this content: "17.23;12.1;20.34;88.23523;". I want to read this file as doubles into an ArrayList. And eventually print the ArrayList (and eventually print the min. and max., but I don't think that will be a problem after solving this).

But I only get the output "[ ]".

What am I doing wrong? I've been struggling with this for embarrassing 15+ hours, browsed here, youtube, course books...

My code:

public static void main(String[] args) throws IOException {
      File myFile = new File("text.txt");
      Scanner scan = new Scanner(myFile);

      ArrayList<Double> aList = new ArrayList<>();
      
      while (scan.hasNextDouble()) {  
          double nextInput = scan.nextDouble();
          if (nextInput != 0) {
              break;  
          }  
        
          aList.add(nextInput);
      }

      System.out.println(alist);
}
4 Answers

You should configure your scanner so it will accept:

  1. ; as a delimiter
  2. , as a decimal separator

Working code is:

File myFile = new File("input.txt");

// Swedish locale uses ',' as a decimal separator
Scanner scan = new Scanner(myFile).useDelimiter(";").useLocale(Locale.forLanguageTag("sv-SE"));

ArrayList<Double> aList = new ArrayList<>();

while (scan.hasNextDouble()) {
    double nextInput = scan.nextDouble();
    aList.add(nextInput);
}

System.out.println(aList);

With output [17.23, 12.1, 20.34, 88.23523]

Scanner works by splitting the input into tokens, where tokens are separated by whitespaces (by default). Since there are no whitespaces in the text, the first/only token is the entire text, and since that text is not a valid double value, hasNextDouble() returns false.

Two ways to fix that:

  • Change the token separator to ;:

    scan.useDelimiter(";");
    
  • Read the file with BufferedReader and use split():

    String filename = "text.txt";
    try (BufferedReader in = Files.newBufferedReader(Paths.get(filename))) {
        for (String line; (line = in.readLine()) != null; ) {
            String[] tokens = line.split(";");
            // code here
        }
    }
    

That will now result in the following tokens: 17,23, 12,1, 20,34, 88,23523.

Unfortunately, none of those are valid double values, because they use locale-specific formatting, i.e. the decimal point is a ,, not a ..

Which means that if you kept using Scanner, you can't use hasNextDouble() and nextDouble(), and if you changed to use split(), you can't use Double.parseDouble().

You need to use a NumberFormat to parse locale-specific number formats. Since "Uppgift" looks Swedish, we can use NumberFormat.getInstance(Locale.forLanguageTag("sv-SE")), or simply NumberFormat.getInstance() if your default locale is Sweden.

String filename = "text.txt";
try (BufferedReader in = Files.newBufferedReader(Paths.get(filename))) {
    NumberFormat format = NumberFormat.getInstance(Locale.forLanguageTag("sv-SE"));
    
    for (String line; (line = in.readLine()) != null; ) {
        List<Double> aList = new ArrayList<>();
        
        for (String token : line.split(";")) {
            double value = format.parse(token).doubleValue();
            aList.add(value);
        }
        
        System.out.println(aList); // Prints: [17.23, 12.1, 20.34, 88.23523]
    }
}

Since your file has numbers separated by a semi-colon, you won't be able to read them using scan.hasNextDouble() by default. However, there are so many ways of doing it e.g.

  1. Override the default delimiter.
  2. Reading a line as string and process each number from it after splitting it on the semi-colon.

Option-1:

scan.useDelimiter(";")

Note that since your file has the comma instead of the dot as the decimal symbol, you can use a Locale in which it is the default.

scan.useLocale(Locale.FRANCE);

Also, the following code block in your code will cause the loop to be terminated after reading the first number itself as the first number in your file in not equal to zero. Simply remove these lines in order to get the desired result:

if (nextInput != 0) {
    break;  
}

Option-2:

Read a line, split it on a semi-colon, replace the comma with a dot, parse each element from the resulting array into Double and store the same into aList.

Demo:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        File myFile = new File("file.txt");
        Scanner scan = new Scanner(myFile);

        List<Double> aList = new ArrayList<>();
        while (scan.hasNextLine()) {
            String nextInput = scan.nextLine();
            String[] arr = nextInput.split(";");
            for (String s : arr) {
                aList.add(Double.valueOf(s.replace(",", ".")));
            }
        }
        System.out.println(aList);
    }
}

Output:

[17.23, 12.1, 20.34, 88.23523]

An alternative to replace comma with dot is to use NumberFormat as shown below:

import java.io.File;
import java.io.FileNotFoundException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws FileNotFoundException, ParseException {
        File myFile = new File("file.txt");
        Scanner scan = new Scanner(myFile);
        NumberFormat format = NumberFormat.getInstance(Locale.FRANCE);
        List<Double> aList = new ArrayList<>();
        while (scan.hasNextLine()) {
            String nextInput = scan.nextLine();
            String[] arr = nextInput.split(";");
            for (String s : arr) {
                aList.add(format.parse(s).doubleValue());
            }
        }
        System.out.println(aList);
    }
}

You can do it simply by changing the delimiter. This works by reading from a String but you can also do it from a file. It is up to you to put the values in some data structure. This presumes your default locale uses ',' as a decimal point.

String str = "17,23;12,1;20,34;88,23523;";

Scanner scan = new Scanner(str);
scan.useDelimiter("[;]+");
while(scan.hasNextDouble()) {
    System.out.println(scan.nextDouble());
}

prints

17.23
12.1
20.34
88.23523
Related