Osca
Osca

Reputation: 1694

Parse files with different length of lines and data types

I'm new to java and I'd like to parse a file and save tokens as variables.

The file looks like below. Lines are in different formats.

25 01 2021 400
48
Cape-Reinga 186 38  18.3    18.2    990.1   1015.1
Kaitaia     203 59  19.5    19.2    1005.4  1014.4
Kerikeri    216 63  18.6    17.3    996.5   1014.1

I tried

import java.util.*;
import java.nio.file.*;
import java.io.*;

    public void class(){
        String fromFile = UIFileChooser.open();
        try {
            List<String> lines = Files.readAllLines(Path.of(fromFile));
            for (String line : lines) {
                Scanner scan = new Scanner(line);           
                Double day = scan.nextDouble();
                Double month = scan.nextDouble();
                Double year = scan.nextDouble();
                Double time = scan.nextDouble();
                UI.println(day + month + year + time);
            }
        } catch(IOException e){UI.println("File reading failed" + e);}

I'm using BlueJ IDE. It printed out the value of UI.println(day + month + year + time) However I also got an error message A exception has been thrown java.util.NoSuchElementException at the same time. I guess it means it cannot find the next element.

I tried to use while(scan.hasNextDouble) inside the for loop to try avoid the error but it became an infinite loop.

For the expected result, I'd like to save tokens as variables. For example, save the first line's tokens as day month year time. The second line as count. The third line as station n1 n2 etc..

Upvotes: 1

Views: 224

Answers (2)

DevilsHnd - 退した
DevilsHnd - 退した

Reputation: 9192

The way I see it, it's best to create a class that can represent the file you want to parse. After all, when it gets to the actual file data it will be records you are reading. Here is an example class you might have to represent the file data example provided:

// Name the class whatever you want...Just remember to refactor.
public class SomeFileNameData {
    private String fileFullPath;
    private String fileName;
    private int fileDay;
    private int fileMonth;
    private int fileYear;
    private String fileDateString;
    private int fileTime;
    private String fileTimeString;
    private int recordsCount;
    java.util.List<Stations> stationsList = new java.util.ArrayList<>();
        
    // Constructor    
    public SomeFileNameData(java.io.File file) {
        if (!file.exists()) {
            throw new IllegalArgumentException("File Not Found! (" + fileFullPath + ")");
        }
        this.fileFullPath = file.getAbsolutePath();
        this.fileName = file.getName();
        readFile(file);
    }
    
    // Reads the data file supplied during instantiation of this class.
    private void readFile(java.io.File file) {
        java.util.List<String> linesList = null;
        try {
            linesList = java.nio.file.Files.readAllLines(file.toPath());
        }
        catch (java.io.IOException ex) {
            java.util.logging.Logger.getLogger(SomeFileNameData.class.getName())
                                .log(java.util.logging.Level.SEVERE, null, ex);
        }
        if (linesList == null || linesList.isEmpty()) {
            javax.swing.JOptionPane.showMessageDialog(null, "<htmL><center>The file named: "
                    + "<font color=blue>" + this.fileName + "</font> is empty!"
                    + "</center></html>", "Warning - File Is Empty", 
                    javax.swing.JOptionPane.WARNING_MESSAGE);
            return;
        }
        
        int lineCount = 0;
        for (int i = 0; i < linesList.size(); i++) {
            String line = linesList.get(i).trim();
            if (line.isEmpty()) { continue; }
            lineCount++;
            if (lineCount == 1) {
                String[] lineParts = line.split("\\s+");
                // Validate the file Date Line
                if (lineParts.length != 4 || !lineParts[0].matches("\\d+{2}") ||
                        !lineParts[1].matches("\\d+{2}") || !lineParts[2].matches("\\d+{4}") ||
                        !lineParts[3].matches("\\d+")) {
                    throw new RuntimeException("Invalid File Format! Date/Time line "
                            + "contains an Invalid number of arguments! (" + line + 
                            ") This line requires four arguments (dd mm yyyy time_value).");
                }
                
                this.fileDay = Integer.parseInt(lineParts[0]);
                this.fileMonth = Integer.parseInt(lineParts[1]);
                this.fileYear = Integer.parseInt(lineParts[2]);
                this.fileDateString = String.format("%04d-%02d-%02d", this.fileYear, this.fileMonth, this.fileDay);
                this.fileTime = Integer.parseInt(lineParts[3]);
                int hours = fileTime / 10000;
                int minutes = (fileTime % 10000) / 100;
                int seconds = fileTime % 100;
                this.fileTimeString = String.format("%02d:%02d:%02d", hours, minutes, seconds);
            }
            
            if (lineCount == 2) {
                // Validate the file Count line
                if (!line.matches("\\d+")) {
                    throw new RuntimeException("Invalid File Format! The Records Count line"
                            + "contains an Invalid value! (" + line + 
                            ") This line requires a numerical value.");
                }
                this.recordsCount = Integer.parseInt(line);
            }
            
            /* Read in Stations data and create an instance of each Station.
               These instances will be held within the List Interface named
               stationsList.                */ 
            if (lineCount > 2) {
                String[] lineParts = line.split("\\s+");
                stationsList.add(new Stations(lineParts[0], 
                        Double.valueOf(lineParts[1]),
                        Double.valueOf(lineParts[2]), 
                        Double.valueOf(lineParts[3]),
                        Double.valueOf(lineParts[4]), 
                        Double.valueOf(lineParts[5]),
                        Double.valueOf(lineParts[6])));
            }
        }
        linesList.clear();
    }

    // Getters
    public String getFileFullPath() {
        return fileFullPath;
    }

    public String getFileName() {
        return fileName;
    }

    public int getFileDay() {
        return fileDay;
    }

    public int getFileMonth() {
        return fileMonth;
    }

    public int getFileYear() {
        return fileYear;
    }

    public String getFileDateString() {
        return fileDateString;
    }

    public int getFileTime() {
        return fileTime;
    }

    public String getFileTimeString() {
        return fileTimeString;
    }

    public int getRecordsCount() {
        return recordsCount;
    }
    
    public java.util.List<Stations> getStationsList() {
        return stationsList;
    }
    
//==============================================================================    

    // Inner Class - Allows for instances of Station data.
    public class Stations {
        
        private String stationName;
        private double n1, n2, n3, n4, n5, n6; // Give these proper names.
        
        // Contructor #1
        public Stations() {    }
        
        // Contructor #2
        Stations (String stationName, double n1, double n2, double n3, double n4, 
                                      double n5, double n6) {
            this.stationName = stationName;
            this.n1 = n1;
            this.n2 = n2;
            this.n3 = n3;
            this.n4 = n4;
            this.n5 = n5;
            this.n6 = n6;
        }

        // Getters & Setters
        public String getStationName() {
            return stationName;
        }

        public void setStationName(String stationName) {
            this.stationName = stationName;
        }

        public double getN1() {
            return n1;
        }

        public void setN1(double n1) {
            this.n1 = n1;
        }

        public double getN2() {
            return n2;
        }

        public void setN2(double n2) {
            this.n2 = n2;
        }

        public double getN3() {
            return n3;
        }

        public void setN3(double n3) {
            this.n3 = n3;
        }

        public double getN4() {
            return n4;
        }

        public void setN4(double n4) {
            this.n4 = n4;
        }

        public double getN5() {
            return n5;
        }

        public void setN5(double n5) {
            this.n5 = n5;
        }

        public double getN6() {
            return n6;
        }

        public void setN6(double n6) {
            this.n6 = n6;
        }

        @Override
        public String toString() {
            return new StringBuilder(stationName).append(", ").append(n1).append(", ")
                    .append(n2).append(", ").append(n3).append(", ").append(n4)
                    .append(", ").append(n5).append(", ").append(n6).toString();
        }
    }
}

How you might use this above class:

File file = new File("D:/MyFiles/SomeFileData.txt");
if (!file.exists()) {
    System.err.println("File Not Found: " + file.getAbsolutePath());
    System.exit(0); // Close app or whatever you want to do in this case,
}
SomeFileNameData sfn = new SomeFileNameData(file);
List<SomeFileNameData.Stations> list = sfn.getStationsList();
        
System.out.println("File Date:          Day: " + sfn.getFileDay() + ", Month: " + sfn.getFileMonth() 
                   + ", Year: " + sfn.getFileYear());
System.out.println("File Date String:   " + sfn.getFileDateString() + " (yyyy-mm-dd)");
System.out.println("File Time Value:    " + sfn.getFileTime() + " which relates to: " + sfn.getFileTimeString());
System.out.println("File Count:         " + sfn.getRecordsCount());
System.out.println();
        
//Display Stations data...
System.out.println("Stations Data:");
System.out.println("==============");
for (SomeFileNameData.Stations station : list) {
    System.out.println(station.toString());
}
        
//Display the the names for each Station...
System.out.println();
StringBuilder stnNames = new StringBuilder("");
for (SomeFileNameData.Stations station : list) {
    if (!stnNames.toString().isEmpty()) {
        stnNames.append(", ");
    }
    stnNames.append(station.getStationName());
}
System.out.println("Station Names: " + stnNames.toString() );
        
// Display the average for literal column 3 station data
double sum = 0.0d;
for (SomeFileNameData.Stations station : list) {
    sum+= station.getN2();
}
System.out.println("Average for literal Column 3 of Station data is: " 
                  + String.format("%.03f",(sum / list.size())));

If the above code is used against the sample file data you provided, your console window should display:

File Date:          Day: 25, Month: 1, Year: 2021
File Date String:   2021-01-25 (yyyy-mm-dd)
File Time Value:    400 which relates to: 00:04:00
File Count:         48

Stations Data:
==============
Cape-Reinga, 186.0, 38.0, 18.3, 18.2, 990.1, 1015.1
Kaitaia, 203.0, 59.0, 19.5, 19.2, 1005.4, 1014.4
Kerikeri, 216.0, 63.0, 18.6, 17.3, 996.5, 1014.1

Station Names: Cape-Reinga, Kaitaia, Kerikeri
Average for literal Column 3 of Station data is: 53.333

Upvotes: 1

vsfDawg
vsfDawg

Reputation: 1527

The rows of your input file have different forms. Your handling within the loop only matches the form of the first row. Your error is probably occurring when it tries to parse the second row because it only has one value.

It appears that you have a date on the first line, some integer value on the second (number if data rows?), and then some number of data rows of a consistent form. You can use an Iterator over the lines so you can easily provide different behavior for the first and second lines versus the remainder of the lines.

public void parseFile(String filename) {
  Iterator<String> iterator = Files.readAllLines(Path.of(filename)).iterator();
  LocalDateTime timestamp = parseDate(iterator.next());
  int count = parseInt(iterator.next());
  ... do something with timestamp and count ...
  while (iterator.hasNext()) {
    DataModel data = parseDataRow(iterator.next());
    ... do something with row data ... 
  }
}

Note I am waving my hands at the parsing of a given line by just defining some methods to do this. You don't really need a parseInt method as you can just call Integer.parseInt(String).

private LocalDateTime parseDate(String line) {
  ... do stuff ...
}
private int parseInt(String line) {
  return Integer.parseInt(line);
}
private DataModel parseDataRow(String line) {
  ... do stuff ...
}

Upvotes: 0

Related Questions