lfcbvb
lfcbvb

Reputation: 1

Using the Scanner class to read a scanner string

I have a problem creating a student class which contains a constructor which takes a Scanner string of a format "Brookes 00918 X12 X14 X16 X21". The conditions should be that there should be a student name and student number and the course codes should start with an "X". I have thrown IncorrectFormatExceptions in the case that they are not satisfied. However when I create a test class and enter a string and press enter , for example "abc 123" it doesn't produce an output which is usually the case.

Update: I've changed the code to use a String array tokens however now with the toString() method using "123 abc X12" it gives a Null Pointer Exception. It works when I put "123 abc" in the constructor

Update:Seems to work now forgot to initialize the arrayList

    import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;


public class Student extends UniversityPerson{
    private String studentNumber="";
    private List<String> courses=new ArrayList<String>();
    private String studentName="";

    public int checkNoofletters(char[] chararray){
        int noofletters=0;
        for (char c:chararray){
            if (Character.isLetter(c)){
                noofletters++;
            }
        }
        return noofletters;
    }
    public String courseListinStr(){
        String stringo="";
        for (String c:courses){
            stringo+=c;
            stringo+=" ";
        }
        return stringo;
    }

    public Student(Scanner scanner) throws IncorrectFormatException{
        int studentNumberCount=0;
        int studentNameCount=0;

        Scanner s=scanner;
        String input=s.nextLine();
        String[] tokens=input.split("\\s");

        for (int i=0; i<tokens.length; i++){
            char[] chars=tokens[i].toCharArray();

            if (checkNoofletters(chars)==chars.length){//if the number of letters is equal to the character length
                if (studentNameCount==1){throw new IncorrectFormatException("Can only have 1 student name");}
                studentNameCount++;
                this.studentName=tokens[i];
                continue;
            }
            if (tokens[i].matches("[0-9]+")){//add to the studentNumbers list
                if (studentNumberCount==1){throw new IncorrectFormatException("Can only have 1 student number");}
                studentNumberCount++;
                this.studentNumber=tokens[i];

                continue;
                }

            if (!tokens[i].startsWith("X")){
                throw new IncorrectFormatException("Course code must start with an 'X'");
            }

            System.out.println(tokens[i]);
            courses.add(tokens[i]);

        } 

        if (studentNumber=="" || studentName==""){
            throw new IncorrectFormatException("Must have 1 student Number and Student Name");
        }

    }


    @Override
    public String toString() {
        //return String.format("%s %s", studentName,courseListinStr());
        return String.format("Student: %s %s", studentName,studentNumber);
    }

    @Override
    public boolean equals(Object o) {
        // TODO Auto-generated method stub
        return false;
    }



}

Upvotes: 0

Views: 131

Answers (3)

Raffaele
Raffaele

Reputation: 20885

Your program is full of errors and I'll list some of them after answering why it doesn't print anything: if you dump all threads you'll see that the main thread is stuck at next(), which blocks until next token is available, and effectively never leaves the constructor of the first student

if (s.hasNextInt()){
  studentNumbers.add(s.nextInt());
  s.next();
  continue;  // <--------- this line
}

I think this is not the only error in your program, so maybe you'd better throw the entire parsing away and restart from scratch.

  1. You should create exactly one Scanner object for each input stream, not one for parsed object
  2. You should not pass the scanner to the Student constructor
  3. You should not put any algorithm in a constructor: make a separate object instead

To simplify your program introduce a Parser class

public class Parser {
  public Parser(Reader in) {}
  public boolean hasNext() {}
  public Student next() {}
}

and inside next() make the parser deal with entire lines (Scanner.hasNextLine and Scanner.nextLine()) instead of individual tokens, otherwise you'll have to define a custom protocol to mean EOR (end of record)

Dealing with lines is easier to think about, program and test. Once you have the full record, you can further tokenize it with a simple String.split(), or directly use regular expressions.

Upvotes: 1

Theo
Theo

Reputation: 1193

The best way would be to do something like this:

Scanner s=scanner;
String input = s.nextLine();
String[] tokens=input.split("\\s");

Now you can test all your conditions:

if (tokens.size() < yourNumber) throw new Exception("add here");
if (tokens[2].charAt(0)!='X') throw new Exception("add here");

and so on; it should be rather easy to create your Student Object based on your requirements.

Upvotes: 1

Ravi
Ravi

Reputation: 31407

I didn't go through, your whole code. But, I would suggest you to use StringTokenizer or split function and store it in temp array. Then, traverse through your temp array and validate the data.

Upvotes: 0

Related Questions