|
|
クラスライブラリの利用 (Scanner クラス) |
H.Kamifuji . |
テキスト入力を扱うためのScannerクラスについて見ていきます。標準入力からも読み込みが行え、System.inをそのまま使うよりも利用しやすく、またファイルからの入力も可能となっています。 クラス定義は下記のようになっています。 java.lang.Object | +-- java.util.Scanner public final class Scanner extends Object implements Iterator<String>使用する場合は「import java.util.Scanner;」を行って下さい。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
まずキーボードから入力された文字を読み取る方法について見ておきます。使用するコンストラクタは下記となります。 Scanner public Scanner(InputStream source)指定された入力ストリームからスキャンされた値を生成する Scanner を新しく作成します。ストリームから取得したバイトは、基になるプラットフォームのデフォルト文字セットを使用して文字に変換されます。 パラメータ: source - スキャン対象の入力ストリーム キーボードからの入力を受けつける場合はSystem.inを使って下記のように記述します。 Scanner scan = new Scanner(System.in);コンストラクタの引数で指定したところから値を読み込もうとします。ファイル名を指定した場合はファイルから、文字列などを指定した場合はその文字列を読み込みます。今回のように標準入力を指定した場合にはキーボードから読み込む対象のテキストを入力します。 読み込みを実際に行うと読み込み対象から値を読み込もうとします。標準入力からの場合はリターンキーを押すと入力が完了したと見なされ、入力されたテキストから値を読み込みます。一度に読み込む値は区切り文字までです。区切り文字のデフォルトは空白文字です。例えば下記のようにキーボードから入力を行った場合で考えてみます。 abc defこの場合、読み込みを行うと「abc」という値を読み込みます。この状態で次に読み込みを行うと、先ほどの残りのテキストである「def」がまだ残っているため、この値を読み込みます。さらに読み込みを行うと、読み込み対象のテキストが残っていないため、再度キーボードからの入力待ちとなります。 ではまず文字を入力してもらい読み込んでみます。文字を読み込むには「next」メソッドを使います。 next public String next()このスキャナから次の完全なトークンを検索して返します。完全なトークンの前後には、区切り文字パターンに一致する入力が配置されます。このメソッドは、前の hasNext() の呼び出しで true が返された場合でも、入力のスキャンの待機中にブロックを実行する可能性があります。 戻り値: 次のトークン 例外: NoSuchElementException - 利用可能なトークンがこれ以上存在しない場合 IllegalStateException - このスキャナがクローズしている場合 nextメソッドを実行すると、区切り文字までの文字を読み込みます。まだ対象としているテキストが残っていた場合には、次にnextメソッドを実行すると読み込む事ができます。 では実際に試してみましょう。下記では「abc def」など区切り文字で区切られた2つの文字を読み込む事を前提としています。 サンプルプログラム下記のサンプルを実行してみよう。import java.util.Scanner; class testScanner1{ public static void main(String args[]){ System.out.println("文字を入力して下さい。"); Scanner scan = new Scanner(System.in); String str = scan.next(); System.out.println("最初のトークンは: "+ str); str = scan.next(); System.out.println("次のトークンは : "+ str); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner1.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner1 文字を入力して下さい。上記のようにキーボードからの入力待ちとなります。ここで「abc def」と入力して下さい。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner1.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner1 文字を入力して下さい。 abc def上記の状態からリターンキーを押すと読み込み対象のテキストの入力を終了し解析を開始します。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner1.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner1 文字を入力して下さい。 abc def 最初のトークンは: abc 次のトークンは : def [xxxxxxxx@dddddddddd Scanner]$入力されたテキストをデフォルトの区切り文字である空白で分割し、それぞれのトークンを読み込んで出力しています。 では単に「abc」とだけ入力した場合も見てみます。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner1.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner1 文字を入力して下さい。 abc 最初のトークンは: abc最初のトークンの読み込みは終わっていますが、次のトークンが無い為、改めてキーボードから入力待ちとなります。そこで改めて「def」と入力してみて下さい。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner1.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner1 文字を入力して下さい。 abc 最初のトークンは: abc def 次のトークンは : def [xxxxxxxx@dddddddddd Scanner]$最後に「abc def ghi」と3つのトークンに区分されるように入力してみます。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner1.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner1 文字を入力して下さい。 abc def ghi 最初のトークンは: abc 次のトークンは : def [xxxxxxxx@dddddddddd Scanner]$この場合は1番目と2番目のトークンを読み込みで終了します。残っている「ghi」は捨てられます。 |
前の頁ではトークンを順番に文字列として読み取りました。ここでは入力された値を指定した型の値として受け取る方法を見ていきます。 nextInt public int nextInt()入力の次のトークンを int としてスキャンします。 nextInt() 形式のこの呼び出しは、nextInt(radix) の呼び出しと正確に同じ動作になります。ここで、radix はこのスキャナのデフォルト基数です。 戻り値: 入力からスキャンした int 例外: InputMismatchException - 次のトークンが Integer 正規表現に一致しないか、範囲外の場合 NoSuchElementException - スキャンする入力がなくなった場合 IllegalStateException - このスキャナがクローズしている場合 まずは「int型」の値を受け取る場合です。当然のことながら入力された値はint型に適合する値でなくてはなりません。int型で無いトークンをこのメソッドで読み込もうとすると例外のInputMismatchExceptionが発生します。 では実際に試してみましょう。 サンプルプログラム下記のサンプルを実行してみよう。import java.util.Scanner; class testScanner2{ public static void main(String args[]){ System.out.println("数値を入力して下さい。"); Scanner scan = new Scanner(System.in); int val = scan.nextInt(); System.out.println("最初の数値のトークンは: "+ val); val = scan.nextInt(); System.out.println("次の数値のトークンは : "+ val); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner2.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner2 数値を入力して下さい。上記のようにキーボードからの入力待ちとなります。ここで「10 25」と入力してからリータンキーを押すと下記のようになります。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner2.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner2 数値を入力して下さい。 10 25 最初の数値のトークンは: 10 次の数値のトークンは : 25 [xxxxxxxx@dddddddddd Scanner]$数値以外の値を入力した場合も試してみます。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner2.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner2 数値を入力して下さい。 10 test 最初の数値のトークンは: 10 Exception in thread "main" java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:864) at java.util.Scanner.next(Scanner.java:1485) at java.util.Scanner.nextInt(Scanner.java:2117) at java.util.Scanner.nextInt(Scanner.java:2076) at testScanner2.main(testScanner2.java:12) [xxxxxxxx@dddddddddd Scanner]$上記のように「Exception in thread "main" java.util.InputMismatchException」が発生します。例外を考慮に入れる場合は下記のように変更して下さい。 サンプルプログラム下記のサンプルを実行してみよう。import java.util.Scanner; import java.util.InputMismatchException; class testScanner3{ public static void main(String args[]){ System.out.println("数値を入力して下さい。"); Scanner scan = new Scanner(System.in); try{ int val = scan.nextInt(); System.out.println("最初の数値のトークンは: "+ val); val = scan.nextInt(); System.out.println("次の数値のトークンは : "+ val); }catch (InputMismatchException e){ System.out.println("型が違います:" + e); } } }先ほどと同じように数値以外の値を入れた場合は下記のようになります。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner3.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner3 数値を入力して下さい。 10 test 最初の数値のトークンは: 10 型が違います:java.util.InputMismatchException [xxxxxxxx@dddddddddd Scanner]$ 他の型のメソッド先ほどは例としてint型の値を受け取るメソッドを使いましたが、他の型用にも同じようなメソッドが用意されています。基本的に使い方は同じなのでメソッドの紹介だけしておきます(例外の説明は同じなので省いています)。nextByte: nextByte public byte nextByte()入力の次のトークンを byte としてスキャンします。 戻り値: 入力からスキャンした byte nextShort: nextShort public short nextByte()入力の次のトークンを short としてスキャンします。 戻り値: 入力からスキャンした short nextLong: nextLong public long nextLong()入力の次のトークンを long としてスキャンします。 戻り値: 入力からスキャンした long nextFloat: nextFloat public float nextFloat()入力の次のトークンを float としてスキャンします。 戻り値: 入力からスキャンした float nextDouble: nextDouble public double nextDouble()入力の次のトークンを double としてスキャンします。 戻り値: 入力からスキャンした double nextBoolean: nextBoolean public boolean nextBoolean()入力の次のトークンをブール値としてスキャンします。 戻り値: 入力からスキャンしたブール値 |
デフォルトではトークン毎の区切り文字として空白(実際には空白相当の値)が使われていますが、CSV形式のデータを取り扱う時のように区切り文字を例えばカンマ(,)などに変更することができます。 useDelimiter public Scanner useDelimiter(String pattern)このスキャナの区切り文字パターンを、指定された String から作成されたパターンに設定します。 このメソッドの useDelimiter(pattern) 形式の呼び出しの動作は、hasDelimiter(Pattern.compile(pattern)) の呼び出しとまったく同じになります。 パラメータ: pattern - 区切り文字パターンを指定する文字列 戻り値: 現在のスキャナ 例えば区切り文字をカンマ(,)に変更する場合は下記のようになります。 Scanner scan = new Scanner(System.in); scan.useDelimiter(",");指定できる区切り文字はPatternクラスで定義されている値を使いますが、例えば下記のようなものがあります。 x 文字 x \\ バックスラッシュ文字 \t タブ文字 ('\u0009') \n 改行文字 ('\u000A') \r キャリッジリターン文字 ('\u000D') [abc] a、b、または c (単純クラス) [^abc] a、b、c 以外の文字 (否定) [a-zA-Z] a 〜 z または A 〜 Z (範囲) [a-d[m-p]] a 〜 d、または m 〜 p: [a-dm-p] (結合) . 任意の文字 (行末記号とマッチする場合もある) \d 数字: [0-9] \D 数字以外: [^0-9] \s 空白文字: [ \t\n\x0B\f\r] \S 非空白文字: [^\s] \w 単語構成文字: [a-zA-Z_0-9] \W 非単語文字: [^\w] X? X、1 または 0 回 X* X、0 回以上 X+ X、1 回以上 X{n} X、n 回 X{n,} X、n 回以上 X{n,m} X、n 回以上、m 回以下例えば改行(Windowsの場合)を区切り文字にする場合は下記のようになります。 scan.useDelimiter("\\r\\n");※パターン文字列的には「\r\n」ですが、「\」文字はエスケープしないといけません。 カンマ区切りにして、カンマの前後に空白があっても無視する場合は下記のようになります。 scan.useDelimiter("\\s*,\\s*");※空白文字を表す「\s」をエスケープして「\\s」とし、それが0回以上続いてもいいように「\\s*」としています。そしてカンマ(,)が続き、同じように0回以上の空白文字が続くパターンです。よって「abc,def,ghi」も「abc, def, ghi」も「abc , def , ghi」も同じようにトークンを取り出せます。 では実際に試してみましょう。 サンプルプログラム下記のサンプルを実行してみよう。import java.util.Scanner; class testScanner4{ public static void main(String args[]){ System.out.println("文字をカンマ区切りで入力して下さい。"); Scanner scan = new Scanner(System.in); scan.useDelimiter("\\s*,\\s*|\n"); String str = scan.next(); System.out.println("最初のトークンは: "+ str); str = scan.next(); System.out.println("次のトークンは : "+ str); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner4.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner4 文字をカンマ区切りで入力して下さい。上記のようにキーボードからの入力待ちとなります。ここで「abc, def」と入力してからリータンキーを押すと下記のようになります。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner4.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner4 文字をカンマ区切りで入力して下さい。 abc, def 最初のトークンは: abc 次のトークンは : def [xxxxxxxx@dddddddddd Scanner]$ |
Scannerクラスはファイルの中身を読み込むこともできます。今まではキーボードから入力された値を読み取っていましたが、キーボードから入力してもらう代わりにファイルを指定し、そのファイルに書かれている内容を読み込むということになります。 ファイルからの入力を行う場合のコンストラクタは下記になります。 Scanner public Scanner(File source) throws FileNotFoundException指定されたファイルからスキャンされた値を生成する Scanner を新しく作成します。ファイルから取得したバイトは、基になるプラットフォームのデフォルト文字セットを使用して文字に変換されます。 パラメータ: source - スキャン対象のファイル 例外: FileNotFoundException - ソースが見つからない場合 引数に読み込む対象のファイルから作成したFileクラスのオブジェクトを指定します。例えば下記のような感じです。(例外処理も必要です)。 try{ File file = new File("targetfile.txt"); Scanner scan = new Scanner(file); }catch(FileNotFoundException e){ System.out.println(e); }Scannerクラスのオブジェクトを作成した後の使い方はキーボードから入力した場合と同じです。 ただファイルから読み込むと言う場合は、何個のトークンを読み込むと決まっているよりも、ファイルに書かれている文だけ読み込むという場合が多いと思います。このようにまだデータがあるなら読み込むといった処理を行うにはScannerクラスで用意されている"hasnext"メソッドを使います。 hasNext public boolean hasNext()このスキャナが入力内に別のトークンを保持する場合は true を返します。このメソッドは、入力のスキャンの待機中にブロックを実行する可能性があります。スキャナが、入力の先に進むことはありません。 戻り値: このスキャナが別のトークンを保持する場合にのみ true 例外: IllegalStateException - このスキャナがクローズしている場合 このメソッドを使う場合、"next"メソッドで読み込めるデータが残っているのかどうかを判別してくれます。 例えば区切り文字を改行に設定し、ファイルに含まれているデータを1行ずつ最後まで読み込む場合には下記のようになります。 try{ File file = new File("targetfile.txt"); Scanner scan = new Scanner(file); scan.useDelimiter("\\r\\n"); int line = 1; while(scan.hasNext()){ String str = scan.next(); System.out.println(line + ":" + str); line++; } }catch(FileNotFoundException e){ System.out.println(e); }上記では1行ずつ読み込み、読み込んだ値に行番号を付与して画面に出力しています。 また"next"メソッドではなく、"nextInt"メソッドなどで読み込む場合には、それに対応した"hasNextInt"メソッドなど型毎に類似のメソッドが用意されていますが、あまり使うことは無いのではと思います。 では実際に試してみましょう。 サンプルプログラム下記のサンプルを実行してみよう。
import java.util.Scanner;
import java.io.FileNotFoundException;
import java.io.File;
class testScanner5{
public static void main(String args[]){
try{
File file = new File("targetfile.txt");
// File file = new File( args[0] );
Scanner scan = new Scanner(file);
// scan.useDelimiter("\\r\\n"); // DOS Windows
scan.useDelimiter("\\n"); // UNIX Linux
int line = 1;
while(scan.hasNext()){
String str = scan.next();
System.out.println(line + ":" + str);
line++;
}
}catch(FileNotFoundException e){
System.out.println(e);
}
}
}
JDK-23 にて、見直し、青色部コード改修した。Windows / Linux で使い分けて下さい。上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Scanner]$ javac testScanner5.java [xxxxxxxx@dddddddddd Scanner]$ java testScanner5 1:ディープインパクト完勝 2:凱旋門賞へ大きな弾み 3:競馬・宝塚記念 [xxxxxxxx@dddddddddd Scanner]$ |
|