Reading a file -- pairing a String and int value -- with multiple split lines

247 views Asked by At

I am working on an exercise with the following criteria:

"The input consists of pairs of tokens where each pair begins with the type of ticket that the person bought ("coach", "firstclass", or "discount", case-sensitively) and is followed by the number of miles of the flight."

The list can be paired -- coach 1500 firstclass 2000 discount 900 coach 3500 -- and this currently works great. However, when the String and int value are split like so:

firstclass        5000  coach         1500         coach
100 firstclass

2000    discount 300

it breaks entirely. I am almost certain that it has something to do with me using this format (not full)

while(fileScanner.hasNextLine())
{
    StringTokenizer token = new StringTokenizer(fileScanner.nextLine(), " ")
    while(token.hasMoreTokens())
    {
        String ticketClass = token.nextToken().toLowerCase();
        int count = Integer.parseInt(token.nextToken());
        ...
    }
}

because it will always read the first value as a String and the second value as an integer. I am very lost on how to keep track of one or the other while going to read the next line. Any help is truly appreciated.

Similar (I think) problems:

3

There are 3 answers

3
bui On BEST ANSWER

If you can afford to read the text file in all at once as a very long String, simply use the built-in String.split() with the regex \\s+, like so

String[] tokens = fileAsString.split("\\s+"); 

This will split the input file into tokens, assuming the tokens are separated by one or more whitespace characters (a whitespace character covers newline, space, tab, and carriage return). Even and odd tokens are ticket types and mile counts, respectively.

If you absolutely have to read in line-by-line and use StringTokenizer, a solution is to count number of tokens in the last line. If this number is odd, the first token in the current line would be of a different type of the first token in the last line. Once knowing the starting type of the current line, simply alternating types from there.

int tokenCount = 0;
boolean startingType = true; // true for String, false for integer
boolean currentType;
while(fileScanner.hasNextLine())
{
    StringTokenizer token = new StringTokenizer(fileScanner.nextLine(), " ");
    startingType = startingType ^ (tokenCount % 2 == 1); // if tokenCount is odd, the XOR ^ operator will flip the starting type of this line
    tokenCount = 0;
    while(token.hasMoreTokens())
    {
        tokenCount++;
        currentType = startingType ^ (tokenCount % 2 == 0); // alternating between types in current line
        if (currentType) {
            String ticketClass = token.nextToken().toLowerCase();
            // do something with ticketClass here
        } else {
            int mileCount = Integer.parseInt(token.nextToken());
            // do something with mileCount here
        }
        ...
    }
}
3
Kemper Lee On

I found another way to do this problem without using either the StringTokenizer or the regex...admittedly I had trouble with the regular expressions haha.

I declare these outside of the try-catch block because I want to use them in both my finally statement and return the points:

int points = 0;
ArrayList<String> classNames = new ArrayList<>();
ArrayList<Integer> classTickets = new ArrayList<>();

Then inside my try-statement, I declare the index variable because I won't need that outside of this block. That variable increases each time a new element is read. Odd elements are read as ticket classes and even elements are read as ticket prices:

try
{
    int index = 0;
    // read till the file is empty
    while(fileScanner.hasNext())
    {
        // first entry is the ticket type
        if(index % 2 == 0)
            classNames.add(fileScanner.next());
        // second entry is the number of points
        else
            classTickets.add(Integer.parseInt(fileScanner.next()));
        index++;
    }
}

You can either catch it here like this or use throws NoSuchElementException in your method declaration -- As long as you catch it on your method call

catch(NoSuchElementException noElement)
{
    System.out.println("<###-NoSuchElementException-###>");
}

Then down here, loop through the number of elements. See which flight class it is and multiply the ticket count respectively and return the points outside of the block:

finally
{
    for(int i = 0; i < classNames.size(); i++)
    {
        switch(classNames.get(i).toLowerCase())
        {
            case "firstclass":    // 2 points for first
                points += 2 * classTickets.get(i);
                break;
            case "coach":         // 1 point for coach
                points += classTickets.get(i);
                break;
            default:
                // budget gets nothing
        }
    }
}
return points;

The regex seems like the most convenient way, but this was more intuitive to me for some reason. Either way, I hope the variety will help out.

2
Kemper Lee On

simply use the built-in String.split() - @bui

I was finally able to wrap my head around regular expressions, but \s+ was not being recognized for some reason. It kept giving me this error message:

Invalid escape sequence (valid ones are \b \t \n \f \r " ' \ )Java(1610612990)

So when I went through with those characters instead, I was able to write this:

int points = 0, multiplier = 0, tracker = 0;
while(fileScanner.hasNext())
{
    String read = fileScanner.next().split(
            "[\b  \t  \n  \f  \r  \"  \'  \\ ]")[0];
    if(tracker % 2 == 0)
    {
        if(read.toLowerCase().equals("firstclass"))
            multiplier = 2;
        else if(read.toLowerCase().equals("coach"))
            multiplier = 1;
        else
            multiplier = 0;
    }else
    {
        points += multiplier * Integer.parseInt(read);
    }
    tracker++;
}

This code goes one entry at a time instead of reading a whole array void of whitespace as a work-around for that error message I was getting. If you could show me what the code would look like with String[] tokens = fileAsString.split("\s+"); instead I would really appreciate it :)

you need to add another "\" before "\s" to escape the slash before "s" itself – @bui