Tuesday, July 20, 2021

Java Tutorial: Scanner Class

Chapters

Scanner Class

Scanner class is a simple text scanner that can parse primitive types and string. In other words, scanner can break down primitive types and string into multiple parts called tokens. By default, scanner uses whitespace characters like "\n", "\t" and the good ol' whitespace(" ") as delimiter to separate primitive types and string.

Tokens in this topic means, parts of the input that are separated by delimiter.

Delimiter is a sequence of one or more characters for specifying the boundary between separate, independent regions in plain text, mathematical expressions or other data streams.

Check out Character.isWhiteSpace() to know the different kinds of whitespaces. Scanner can scan inputstreams, String or a text file. Let's use Scanner with Strings.

Scan String Input Using Scanner Class
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("A Big Brown Fox");
    
      //hasNext() returns true if the
      //scanner has another token in
      //its input
      while(scanner.hasNext()){
      
        //next() returns the next token
        //in this scanner as a string type.
        //To understand the function of this
        //method, imagine the tokens are
        //stored in an array, and next()
        //iterates through that array
        //until it reaches the last index
        //of the array.
        System.out.println(scanner.next());
      }
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
A
Big
Brown
Fox
Methods Similar To next()

If we have numbers as input, we can use the nextInt() if the numbers are integer, nextFloat() if the numbers are floating-point, etc.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
    
      scanner = new Scanner("10 100 1000 2000 50000");
     
      //hasNextInt() checks if the current token
      //matches an integer type
      //
      //nextInt() returns the current token as
      //integer type
      while(scanner.hasNextInt())
        System.out.println(scanner.nextInt());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
10
100
1000
2000
50000
Note: If we use nextXXX(); methods like nextInt() and a token doesn't match the type that the method is expecting, java will throw InputMismatchException.
import java.util.Scanner;
import java.util.InputMismatchException;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("10.50");
      while(scanner.hasNext())
        System.out.println(scanner.nextInt());
    }
    catch(InputMismatchException e){
      System.out.println("Token is not an"+
                         " integer type!");
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
Token is not an integer type!
Scanner can also read other number formats that are locale specific like "10,000". "Numbers with commas" format is supported by US locale. So, we need to set the locale of our scanner to US locale.
import java.util.Scanner;
import java.util.Locale;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("1,000,000");
      scanner.useLocale(Locale.US);
      
      while(scanner.hasNextInt())
        System.out.println(scanner.nextInt());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }

  }
}

Result
1000000
If we don't specify a locale then, the scanner uses the default locale of JVM. To get the default locale of our JVM, use the getDefault() static method of Locale class. Try changing the locale in the example to Locale.FRENCH and the scanner won't recognize "1,000,000" as an integer type.

Check out the Scanner class if you want to know more variations of the next() method in the Scanner class.

useDelimiter() Method

We can use useDelimiter() method to change the scanner's delimiter.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("Tomato, Potato, Falsetto");
      
      //useDelimiter changes scanner's delimiter
      //to the specified string
      scanner.useDelimiter(", ");
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result

Tomato
Potato
Falsetto
If we are knowledgeable about Regular Expressions then, we can use a pattern as a delimiter in our scanner.
import java.util.Scanner;
import java.util.regex.Pattern;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      scanner = new Scanner("-Tomato-0-Potato-1-Falsetto-");
      
      //useDelimiter changes scanner's delimiter
      //to the specified pattern
      scanner.useDelimiter(Pattern.compile("\\d"));
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Result
-Tomato-
-Potato-
-Falsetto-
Scan File Using Scanner Class

We can use the Scanner class to read files like text files.
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
public class SampleClass{

  public static void main(String[]args) throws IOException{
    Scanner scanner = null;
    
    try{
      //test.txt must exist in the folder where
      //this file is running
      scanner = new Scanner(new File("test.txt"));
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Assume the words are written in test.txt
like this: Big Brown Fox

Result
Big
Brown
Fox
Scan Console Input Using Scanner Class

We can put System.in of System class as argument in a scanner. Though, there's a problem. Take a look at this example.
Note: This example will ask for input forever. This program needs to be terminated forcefully. If you're using CMD on windows, just close the console or press CTRL+C to terminate the program.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      System.out.println("Enter Input: ");
      scanner = new Scanner(System.in);
      
      while(scanner.hasNext())
        System.out.println(scanner.next());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
     //closing the scanner also closes the stream
     //that is connected to the scanner. Always
     //close streams if they're not gonna be used
     //anymore to free up resources
     scanner.close();
    }
    
  }
}
First off, System.in is the "standard" input stream. This stream is already open and ready to supply input data. Typically this stream corresponds to keyboard input or another input source specified by the host environment or user.

The example above asks for input forever no matter how many inputs you type. The reason why the example is not stopping to ask for input is because System.in is a continuous stream. Thus, it doesn't give an EOL or End Of Line. So, the next() method can't verify if the stream ends. For example, we type "A B", after the program displays "B", next() still waits for an input 'cause it can't find an EOL.

There's a solution to bypass this problem. Since we're only inputting one line on the console, we can use the nextLine() method in the Scanner class.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    
    try{
      System.out.println("Enter Input: ");
      scanner = new Scanner(System.in);
      
      if(scanner.hasNextLine())
        System.out.println(scanner.nextLine());
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Input: Big Brown Fox
Result
Big Brown Fox
Notice that we use if statement when checking if the scanner has a line of strings. We use if statement and not loop statement to avoid the never ending input loop. Since we only use one line when typing an input on the console, it's alright if we just get a single line.

The problem here is that "Big Brown Fox" is not broken down into three parts 'cause we use nextLine() which skips an entire line. Well, we can use the split() method in the String class to break down the string into multiple parts.
import java.util.Scanner;
public class SampleClass{

  public static void main(String[]args){
    Scanner scanner = null;
    String str = "";
    
    try{
      System.out.println("Enter Input: ");
      scanner = new Scanner(System.in);
      
      if(scanner.hasNextLine())
        str = scanner.nextLine();
        
      String[] tokens = str.split(" ");
      
      for(String s : tokens)
        System.out.println(s);
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      //Close the scanner once
      //you're done using it
      if(scanner != null)
        scanner.close();
    }
    
  }
}

Input: Big Brown Fox
Result
Big
Brown
Fox
split(String regex) method in string splits strings based on a pattern which is a regular expression. That's right, the string that we put in this method as an argument is a regular expression.

There are characters that have special meanings in regular expression so, some characters that we put in split() might not behave as normal literal like the dot(.) character. If you wanna learn regular expressions then, visit my "Regular Expressions" blogpost.

No comments:

Post a Comment