Solution
You can achieve what you want by fully consuming the first line before you enter your loop, just call
in.nextLine();
before and the first line is consumed.
Splitting
However, I would approach this differently, parsing line by line and then splitting on |, that way it is easier to work with the data given per line.
in.nextLine();
while (in.hasNextLine()) {
String line = in.nextLine();
String[] data = line.split("\\|");
String name = data[0];
int[] testResults = new int[data.length - 1];
for (int i = 0; i < testResults.length; i++) {
testResults[i] = Integer.parseInt(data[i + 1]);
}
...
}
Proper OOP
Ideally you would add some OOP to that, create a class Student with fields like
public class Student {
private final String name;
private final int[] testResults;
// constructor, getter, ...
}
and then give it a parseLine method like:
public static Student parseLine(String line) {
String[] data = line.split("\\|");
String name = data[0];
int[] testResults = new int[data.length - 1];
for (int i = 0; i < testResults.length; i++) {
testResults[i] = Integer.parseInt(data[i + 1]);
}
return new Student(name, testResults);
}
Then your parsing simplifies heavily to just:
List<Student> students = new ArrayList<>();
in.nextLine();
while (in.hasNextLine()) {
students.add(Student.parseLine(in.nextLine());
}
Streams and NIO
Or if you like streams, just read the file using NIO:
List<Student> students = Files.lines(Path.of("myFile.txt"))
.skip(1)
.map(Student::parseLine)
.collect(Collectors.toList());
very clear, compact and readable.
Average score
My goal is to be able to get each student's average test score, as well as the average score per test (column) and the average score overall.
With the proper OOP structure, as shown, this is fairly simple. First, a students average score, just add a method to the Student class:
public double getAverageScore() {
double total = 0.0;
for (int testResult : testResults) {
total += testResult;
}
return total / testResults.length;
}
Alternative stream solution:
return IntStream.of(testResults).average().orElseThrow();
Next, the average score per column:
public static double averageTestScore(List<Student> students, int testId) {
double total = 0.0;
for (Student student : students) {
total += student.getTestScores()[testId];
}
return total / students.size();
}
And the stream solution:
return students.stream()
.mapToInt(student -> student.getTestScores[testId])
.average().orElseThrow();
And finally the average score overall, which can be computed by taking the average of each students average score:
public static double averageTestScore(List<Student> students) {
double total = 0.0;
for (Student student : students) {
total += student.getAverageScore();
}
return total / students.size();
}
and the stream variant:
return students.stream()
.mapToDouble(Student::getAverageScore)
.average().orElseThrow();