JavaBeginner
JavaBeginner

Reputation: 63

Getting integer data from txt file in Java

I want to read a text file which has String and a few integers related to that string.

This is the class that I have to write my program in:

public List<Integer> Data(String name) throws IOException {
    return null;
}

I have to read the .txt file and find the name in that file, with its data. And save it in an ArrayList.

My question is how do I save it in the ArrayList<Integer> when I have Strings in the List.
This is what I think I would do:

Scanner s = new Scanner(new File(filename));
ArrayList<Integer> data = new ArrayList<Integer>();

while (s.hasNextLine()) {
    data.add(s.nextInt());
}
s.close();

Upvotes: 6

Views: 1309

Answers (2)

slartidan
slartidan

Reputation: 21576

If you want to use java8 you could use something like this.

Input.txt (has to be in classpath):

text1;4711;4712
text2;42;43

The code:

public class Main {

    public static void main(String[] args) throws IOException, URISyntaxException {

        // find file in classpath
        Path path = Paths.get(ClassLoader.getSystemResource("input.txt").toURI());

        // find the matching line
        findLineData(path, "text2")

                // print each value as line to the console output
                .forEach(System.out::println);
    }

    /** searches for a line in a textfile and returns the line's data */
    private static IntStream findLineData(Path path, String searchText) throws IOException {

        // securely open the file in a "try" block and read all lines as stream
        try (Stream<String> lines = Files.lines(path)) {
            return lines

                    // split each line by a separator pattern (semicolon in this example)
                    .map(line -> line.split(";"))

                    // find the line, whiches first element matches the search criteria
                    .filter(data -> searchText.equals(data[0]))

                    // foreach match make a stream of all of the items
                    .map(data -> Arrays.stream(data)

                            // skip the first one (the string name)
                            .skip(1)

                            // parse all values from String to int
                            .mapToInt(Integer::parseInt))

                    // return one match
                    .findAny().get();
        }
    }
}

The output:

42
43

Upvotes: 0

Elliott Frisch
Elliott Frisch

Reputation: 201439

I would define the file as a field (in addition to the filename, and I suggest reading it from the user's home folder) file

private File file = new File(System.getProperty("user.home"), filename);

Then you can use the diamond operator <> when you define your List. You can use a try-with-resources to close your Scanner. You want to read by lines. And you can split your line. Then you test if your first column matches the name. If it does, iterate the other columns are parse them to int. Something like

public List<Integer> loadDataFor(String name) throws IOException {
    List<Integer> data = new ArrayList<>();
    try (Scanner s = new Scanner(file)) {
        while (s.hasNextLine()) {
            String[] row = s.nextLine().split("\\s+");
            if (row[0].equalsIgnoreCase(name)) {
                for (int i = 1; i < row.length; i++) {
                    data.add(Integer.parseInt(row[i]));
                }
            }
        }
    }
    return data;
}

It might be signifanctly more efficient to scan the file once and store the names and fields as a Map<String, List<Integer>> like

public static Map<String, List<Integer>> readFile(String filename) {
    Map<String, List<Integer>> map = new HashMap<>();
    File file = new File(System.getProperty("user.home"), filename);
    try (Scanner s = new Scanner(file)) {
        while (s.hasNextLine()) {
            String[] row = s.nextLine().split("\\s+");
            List<Integer> al = new ArrayList<>();
            for (int i = 1; i < row.length; i++) {
                al.add(Integer.parseInt(row[i]));
            }
            map.put(row[0], al);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return map;
}

Then store that as fileContents like

private Map<String, List<Integer>> fileContents = readFile(filename);

And then implement your loadDataFor(String) method with fileContents like

public List<Integer> loadDataFor(String name) throws IOException {
    return fileContents.get(name);
}

If your usage pattern reads the File for many names then the second is likely to be much faster.

Upvotes: 3

Related Questions