D. Wisor
D. Wisor

Reputation: 43

How do I use a Java method to process an input file line-by-line from within a loop?

I am using Java to process a file by calling multiple methods for each line of input from within a loop. The problem is that methods only read the first line of input for every iteration of the loop. For example, this input:

Jones 90 90 90
Brown 80 80 80
Smith 70 70 70

Should result in output of:

Jones 90 A
Brown 80 B
Smith 70 C

But instead I end up with:

Jones 90 A
Brown 90 A
Smith 90 A

Is it possible to get the studentAverage and determineGrade methods in the code below to process the second and third lines of input as in the above example, or do I need to reconsider the entire loop/method structure?

import java.io.*;
import java.util.Scanner;

public class Lab07Part01
{
    public static void main(String[] args) throws IOException
    {
        File infile = new File("Grades.txt");
        Scanner myfile = new Scanner(infile);
        Scanner counter = new Scanner(infile);

        String studentName = "";
        int testQty = 0;
        int studentQty = -1;
        double studentAvg = 0.0;
        double classSum = 0.0;
        double classAvg = 0.0;
        char letterGrade = 'X';

        while(counter.hasNextLine()) {
            studentQty++;
            counter.nextLine();
        }

        testQty = myfile.nextInt();

        System.out.println("Name   Average   Letter Grade");
        for(int i = 0; i < studentQty; i++) {
            studentName = myfile.next();
            studentAvg = studentAverage(testQty);
            classAvg += studentAvg;
            letterGrade = determineGrade(studentAvg);
            System.out.println(studentName + " " + studentAvg + " " + letterGrade);
            myfile.nextLine();
        }

        classAvg = overallAverage(studentQty, classSum);

        System.out.println("The average test grade is: " + classAvg);

    }

    public static double studentAverage(int testQty) throws IOException
    {
        File infile = new File("Grades.txt");
        Scanner scanavg = new Scanner(infile);
        double studentSum = 0;

        scanavg.nextLine();
        scanavg.next();

        for(int i = 1; i <= testQty; i++) {
            studentSum += scanavg.nextDouble();
        }

        double studentAvg = studentSum / testQty;
        return studentAvg;
    }

    public static char determineGrade(double studentAvg) throws IOException
    {
        char letterGrade = 'X';

        if(studentAvg >= 90.0)
            letterGrade = 'A';
        else if(studentAvg >= 80.0)
            letterGrade = 'B';
        else if(studentAvg >= 70.0)
            letterGrade = 'C';
        else if(studentAvg >= 60.0)
            letterGrade = 'D';
        else if(studentAvg < 60.0)
            letterGrade = 'F';

        return letterGrade;
    }

    public static double overallAverage(int studentQty, double classSum) throws IOException
    {
        double classAvg = classSum / studentQty;
        return classSum;
    }
}

Upvotes: 1

Views: 875

Answers (3)

Ryotsu
Ryotsu

Reputation: 806

The reason why your code prints the average marks repeateadly is because of this method here

public static double studentAverage(int testQty) throws IOException
    {
        File infile = new File("Grades.txt");
        Scanner scanavg = new Scanner(infile);
        double studentSum = 0;

        scanavg.nextLine();
        scanavg.next();

        for(int i = 1; i <= testQty; i++) {
            studentSum += scanavg.nextDouble();
        }

        double studentAvg = studentSum / testQty;
        return studentAvg;
    }

The problem is that although you think you're reading consecutive lines, you are actually just reading the first line of the file whenever this methods executed and hence you end up with the first students marks.

A Solution

If you're comfortable with Streams and java.nio, you could do it a little more elegantly and functionally using Files.lines()

public static void main(String... args) throws IOException {

        // The following line reads each line from your data file and does some
        // with it.
        Files.lines(Paths.get("/path/to/your/file")).forEach(ThisClass::doSomethingWithLine);

        System.out.println("clas average is "+ (classMarks / totalStuds));
    }

    static double classMarks=0; //total marks of the class
    static int totalStuds=0; // total students read until now


    static void doSomethingWithLine(String line) {
        Scanner sc = new Scanner(line);

        // Parse the tokens
        // student name (assuming only first name according to your
        //  example) otherwise use pattern to parse multi part names
        String name = sc.next();

        // parse student marks
        // (you could create a method for this to make things cleaner)
        int subA = sc.nextInt();
        int subB = sc.nextInt();
        int subC = sc.nextInt();
        //calculate average
        double avg = (subA + subB + subC) / 3;

        // calculate class average
        classMarks = classMarks + avg
        totalStuds++;

        //determine grade
        char grade = determineGrade(avg);
        System.out.println(name + " " + avg + " " + grade);
    }

Upvotes: 2

markusw
markusw

Reputation: 2065

you could use a BufferedReader like in the following example. But you need to keep track of the file's end yourself.

try (BufferedReader br = new BufferedReader(new FileReader("<path to your file>")));

  for (Item item : list) {

    String line = br.readLine();
    if (line == null) {
      break; // no more lines to read
    }
    // do something with line...

  }
}

Upvotes: 2

Tim Biegeleisen
Tim Biegeleisen

Reputation: 522817

I would use the scanner by reading in an entire line at a time. Then, we can process that entire line in one logical step:

File infile = new File("Grades.txt");
Scanner myfile = new Scanner(infile);

while (myfile.hasNextLine()) {
    String line = myfile.nextLine();
    String[] parts = line.split("\\s+");
    double average = 0.0d;
    for (int i=1; i < parts.length; ++i) {
        average += Double.parseDouble(parts[i]);
    }
    average /= parts.length - 1;
    char letterGrade = determineGrade(average);

    System.out.println(parts[0] + " " + average + " " + letterGrade);
}

Upvotes: 2

Related Questions