Introduction to Java: input, output, file operations

I wrote a simple console application to show you how to read from and write to text files in Java. Basically it generates two HTML files (exam results based on a data file) in two formats. There is a version for students (results sorted by student id) and a version for a teacher (sorted by result). For Java programming I recommend IntelliJ IDEA.

A data file looks like this. First name then surname, student system id and number of points achieved. An example data file:

Chandler Bing 987622345 10
Joey Tribbiani 983455234 1
Phoebe Buffay 345678434 21
Rachel Green 345634456 4

We want to store these lines in an array of objects so there is a class with variables needed, getters and setters and some more methods I will describe later. What’s important I use comparators (needed for sorting). I need data sorted by two keys so I use two comparators.

/**
 * Created by Krzysztof Grabowski on 04.03.15.
 */

import java.util.Comparator;


public class Student {
//public class Student implements Comparable<Student> {
// Needed to use:  @Override public int compareTo()

    private String studentname;
    private String studentsurname;
    private int systemid;
    private int points;

    public Student(String studentname, String studentsurname, int systemid, int points) {
        this.studentname = studentname;
        this.studentsurname = studentsurname;
        this.systemid = systemid;
        this.points = points;
    }

    public String getStudentname() {
        return studentname;
    }
    public void setStudentname(String studentname) {
        this.studentname = studentname;
    }
    public String getStudentsurname() {
        return studentsurname;
    }
    public void setStudentsurname(String studentsurname) {
        this.studentsurname = studentsurname;
    }
    public int getSystemid() {
        return systemid;
    }
    public void setSystemid(int systemid) {
        this.systemid = systemid;
    }
    public int getPoints() {
        return points;
    }
    public void setPoints(int points) {
        this.points = points;
    }

    /*@Override
    public int compareTo(Student comparesto) {
        int comparepoints=((Student)comparesto).getPoints();
        *//* For Ascending order*//*
        //return this.points-comparepoints;

        *//* For Descending order do like this *//*
        return comparepoints-this.points;
    }*/


    // Comparator for sorting the list by systemid
    public static Comparator<Student> StuSystemid = new Comparator<Student>() {

        public int compare(Student s1, Student s2) {

            int systemid1 = s1.getSystemid();
            int systemid2 = s2.getSystemid();

	   /*For ascending order*/
            //return systemid1-systemid2;

	   /*For descending order*/
            return systemid2-systemid1;
        }
    };

    // Comparator for sorting the list by points
    public static Comparator<Student> StuPoints = new Comparator<Student>() {

        public int compare(Student s1, Student s2) {

            int points1 = s1.getPoints();
            int points2 = s2.getPoints();

	   /*For ascending order*/
            //return points1-points2;

	   /*For descending order*/
            return points2-points1;
        }
    };


    @Override
    public String toString() {
        return studentname + " " + studentsurname + " " + systemid + " " + points;
    }

}

As you can see there is a few lines commented. If you need only one way of sorting you can uncomment them and comment those two comparators instead and use the following code somewhere in your StudentApp to do the sorting.

Collections.sort(arraylist);

Implementing Comparable and overriding compareTo() is just great for one sorting pattern. I needed two ways of doing it so finally I used two comparators instead.

What’s next? What actually makes the application work – a class called StudentApp. I’m creating an array list because I’m not sure how many students are in the data file. If the number was constant and known you could just use a declared array of objects. However in that case the file can be filled with any number of entries so there is need to use an array list. It’s also a structure which makes sorting really easy.

/**
 * Created by Krzysztof Grabowski on 04.03.15.
 */

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


public class StudentApp {

    private static ArrayList<Student> arraylist = new ArrayList<Student>();

    private static void readFile(String fileName) {
        try {
            Scanner scanner = new Scanner(new File(fileName));
            scanner.useDelimiter(System.getProperty("line.separator"));
            while (scanner.hasNext()) {
                parseLine(scanner.next());
            }
            scanner.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static void parseLine(String line) {
        Scanner lineScanner = new Scanner(line);
        lineScanner.useDelimiter("\\s+");
        String name = lineScanner.next();
        String surname = lineScanner.next();
        int systemid = lineScanner.nextInt();
        int points = lineScanner.nextInt();
        arraylist.add(new Student(name, surname, systemid, points));
    }

    private static void generateForStudents() {
        Collections.sort(arraylist, Student.StuSystemid);
        try {
            BufferedWriter outputWriter = new BufferedWriter(new FileWriter("student.html"));
            outputWriter.write("<html>\n<body>\n<p>Results for students</p>\n<ol>\n");
            for(Student str: arraylist) {
                outputWriter.write("<li>" + str.getSystemid() + " (" + str.getPoints() + ")</li>");
                outputWriter.newLine();
            }
            outputWriter.write("</ol>\n</body>\n</html>");
            outputWriter.flush();
            outputWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void generateForTeacher() {
        Collections.sort(arraylist, Student.StuPoints);
        try {
            BufferedWriter outputWriter = new BufferedWriter(new FileWriter("teacher.html"));
            outputWriter.write("<html>\n<body>\n<p>Results for teachers</p>\n<ol>\n");
            for(Student str: arraylist) {
                outputWriter.write("<li>" + str.getStudentname() + " " + str.getStudentsurname() + " (" + str.getPoints() + ")</li>");
                outputWriter.newLine();
            }
            outputWriter.write("</ol>\n</body>\n</html>");
            outputWriter.flush();
            outputWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]) {

        Scanner userInputScanner = new Scanner(System.in);
        System.out.println("Specify a data file ( example - \'students\' ): \nFile format: student entries in new lines, values separated by spaces");
        String filename = userInputScanner.nextLine();

        readFile(filename);
        generateForStudents();
        generateForTeacher();

        System.out.println("Sorted data saved to student.html & teacher.html\nHere is a quick overview\n");
        for(Student str: arraylist){
            System.out.println(str);
        }

    }
}

The method readFile() loads all the data to memory. It’s not a great solution when a data file is extremely big but in that case it’s ok. The second method, parseLine() extracts the values from a whole line, process them to certain variable types and finally adds objects to our array list using the class constructor.

Methods for generating files are actually easy to understand. First we sort data (that’s why we needed comparators before!) and then we create a file and write all the sorted entries there. As a result HTML files with exam results are created. It’s important to fully understand how to write to files. We need to create new BufferedWriter and we use it to write data line by line.

Last but not least – main() method where we ask for a data filename, call the methods and show what’s actually happened by printing strings in a loop thanks to overridden method toString(). That really makes displaying the lines so easy.