Help us understand the problem. What is going on with this article?

クラス java.util.Scannerのおさらいメモ

More than 1 year has passed since last update.

概要

Java 1.5より導入されたjava.util.Scannerクラスのおさらいメモです。
Scannerクラスの使用例では標準入力(System.in)を使う例が数多く取り上げられていますが、この記事では標準入力は扱わずテキストファイルを読むおさらいが中心になります。またScannerクラスのすべてのAPIは取り上げません。

環境

  • Windows 10 Professional
  • OpenJDK 10.0.1

参考

インスタンスの生成方法

Fileから

signature
public Scanner​(File source) throws FileNotFoundException

public Scanner​(File source, String charsetName) throws FileNotFoundException

コード例

example
Path in = Paths.get("path/to/sample.in");
Scanner scanner = new Scanner(in.toFile());
try (scanner) {
  // ...省略...
}

InputStreamから

signature
public Scanner​(InputStream source)

public Scanner​(InputStream source, String charsetName)

コード例

example
InputStream in = Files.newInputStream(Paths.get("path/to/sample.in"));
Scanner scanner = new Scanner(in);
try (scanner) {
  // ...省略...
}

JavaDocに記載があるようにScannerの入力リソースがCloseableインタフェースを実装しているとScannerを閉じるときに、そのリソースも閉じられるためtry句に含める必要はありません。

Scannerが閉じられる場合、その入力ソースがCloseableインタフェースを実装していると、そのソースも閉じられます。

Pathから

signature
public Scanner​(Path source) throws IOException

public Scanner​(Path source, String charsetName) throws IOException

コード例

example
Path in = Paths.get("path", "to", "sample.in");
Scanner scanner = new Scanner(in);
try (scanner) {
  // ...省略...
}

Stringから

signature
public Scanner​(String source)

コード例

example
String in = "apple banana cherry durian elderberry";
Scanner scanner = new Scanner(in);
try (scanner) {
  // ...省略...
}

Readableから

signature
public Scanner​(Readable source)

コード例

example
Readable in = new FileReader(new File("path/to/sample.in"));
Scanner scanner = new Scanner(in);
try (scanner) {
  // ...省略...
}

テキストファイルを読み取る

サンプルファイル

日本郵便株式会社のサイトよりダウンロードできる郵便番号データを利用しました。
この記事のコード例ではこのカンマ区切りのテキストを扱いますが、通常はopencsvなどのライブラリを使用すると思います。

sample.in
32343,"69917","6991701","シマネケン","ニタグンオクイズモチョウ","カメダケ","島根県","仁多郡奥出雲町","亀嵩",0,0,0,0,0,0
32343,"69915","6991515","シマネケン","ニタグンオクイズモチョウ","カモクラ","島根県","仁多郡奥出雲町","鴨倉",0,0,0,0,0,0
32343,"69915","6991514","シマネケン","ニタグンオクイズモチョウ","カワチ","島根県","仁多郡奥出雲町","河内",0,0,0,0,0,0

トークンの区切り文字

デフォルトのトークンの区切り文字は空白文字です。ただしこの場合の空白文字とはJavaの基準に従った空白(Character.isWhitespaceがtrueを返す文字)になり、半角スペース以外にも例えば以下のようなものが区切り文字として認識されます。

出力結果

戻り値がtrueの文字はJava基準の空白。

output
Character.isWhitespace(' ');       // 半角スペース
// → true
Character.isWhitespace('\u0020');  // 半角スペース
// → true
Character.isWhitespace(' ');      // 全角スペース
// → true
Character.isWhitespace('\t');      // タブ
// → true
Character.isWhitespace('\n');      // 改行
// → true
Character.isWhitespace('\f');      // フォームフィード
// → true
Character.isWhitespace('\r');      // 復帰
// → true
Character.isWhitespace('\u001C');  // ファイル区切り文字
// → true
Character.isWhitespace('\u001D');  // グループ区切り文字
// → true
Character.isWhitespace('\u001E');  // レコード区切り文字
// → true
Character.isWhitespace('\u001F');  // ユニット区切り文字
// → true

Character.isWhitespace('\u00a0');  // ノーブレークスペース. いわゆる 
// → false
Character.isWhitespace('a');
// → false
Character.isWhitespace('あ');
// → false

区切り文字のマッチングに使用するPatternの確認

signature
public Pattern delimiter()

コード例

example
scanner.delimiter().pattern();

出力結果

output
\p{javaWhitespace}+

区切り文字のマッチングに使用するPatternを指定する

useDelimiterメソッドで区切り文字に任意のパターンを指定します。

signature
public Scanner useDelimiter​(Pattern pattern)

public Scanner useDelimiter​(String pattern)

コード例

example
String in = "apple :  banana   : cherry  : durian  :  elderberry";
Scanner scanner = new Scanner(in);
try (scanner) {
  scanner.useDelimiter("\\s*:\\s*");
  while (scanner.hasNext()) {
    System.out.println("[" + scanner.next() + "]");
  }
}

出力結果

output
[apple]
[banana]
[cherry]
[durian]
[elderberry]

テキストファイルから1行ずつ読む

hasNextLineメソッドとnextLineメソッドを使用します。
hasNextLineメソッドはスキャナにまだ入力行がある場合にtrueを返します。またnextLineメソッドはスキャナの現在の位置から行末までの内容を返し、スキャナの位置を次行の先頭に移動させます。

signature
public boolean hasNextLine()

public String nextLine()

コード例

example
File in = new File("path/to/sample.in");
Scanner scanner = new Scanner(in);
try (scanner) {
  int counter = 0;
  while (scanner.hasNextLine()) {
    System.out.println(String.format("%2d: %s", ++counter, scanner.nextLine()));
  }
}

出力結果

output
 1: 32343,"69917","6991701","シマネケン","ニタグンオクイズモチョウ","カメダケ","島根県","仁多郡奥出雲町","亀嵩",0,0,0,0,0,0
 2: 32343,"69915","6991515","シマネケン","ニタグンオクイズモチョウ","カモクラ","島根県","仁多郡奥出雲町","鴨倉",0,0,0,0,0,0
 3: 32343,"69915","6991514","シマネケン","ニタグンオクイズモチョウ","カワチ","島根県","仁多郡奥出雲町","河内",0,0,0,0,0,0

テキストファイルからトークン単位で読む

hasNextメソッドはスキャナの入力に別のトークンがある場合にtrueを返します。またnextメソッドはスキャナの現在の位置からのトークンを返し、スキャナの位置を次の区切り文字の位置に移動させます。

デフォルトのトークンの区切り文字のホワイトスペース(半角、全角)、タブ、改行コードなどですが、このサンプルデータではトークンの区切り文字はカンマなのでuseDelimiterメソッドで明示的に指定し、また改行コードもトークンの区切り文字として指定する必要があります。

signature
public boolean hasNext()

public String next()

コード例

example
File in = new File("path/to/sample.in");
Scanner scanner = new Scanner(in);
try (scanner) {
  scanner.useDelimiter(",|\n");
  int counter = 0;
  while (scanner.hasNext()) {
    System.out.println(String.format("%2d: %s", ++counter, scanner.next()));
  }
}

出力結果

output
 1: 32343
 2: "69917"
 3: "6991701"
 4: "シマネケン"
 5: "ニタグンオクイズモチョウ"
 6: "カメダケ"
 7: "島根県"
 8: "仁多郡奥出雲町"
 9: "亀嵩"
10: 0
11: 0
12: 0
13: 0
14: 0
15: 0
16: 32343
17: "69915"
18: "6991515"
19: "シマネケン"
20: "ニタグンオクイズモチョウ"
21: "カモクラ"
22: "島根県"
23: "仁多郡奥出雲町"
24: "鴨倉"
25: 0
26: 0
27: 0
28: 0
29: 0
30: 0
31: 32343
32: "69915"
33: "6991514"
34: "シマネケン"
35: "ニタグンオクイズモチョウ"
36: "カワチ"
37: "島根県"
38: "仁多郡奥出雲町"
39: "河内"
40: 0
41: 0
42: 0
43: 0
44: 0
45: 0

nextとnextLineの併用

nextメソッドで任意の位置のトークンを読み、nextLineメソッドで行末までのデータを読み飛ばすということもできます。

コード例

example
File in = new File("path/to/sample.in");
Scanner scanner = new Scanner(in);
try (scanner) {
  scanner.useDelimiter(",");
  int counter = 0;
  while (scanner.hasNextLine()) {
    int code = scanner.nextInt();        // 全国地方公共団体コード
    String zip5 = scanner.next();        // 郵便番号(5桁)
    String zip7 = scanner.next();        // 郵便番号(7桁)
    scanner.next();                      // skip 都道府県名 半角カタカナ
    scanner.next();                      // skip 市区町村名 半角カタカナ
    scanner.next();                      // skip 町域名 半角カタカナ
    String prefectures = scanner.next(); // 都道府県名
    String city = scanner.next();        // 市区町村名
    String townArea = scanner.next();    // 町域名
    System.out.println(String.format("%2d: %d %s %s %s %s %s", ++counter, code, zip5, zip7, prefectures, city, townArea));
    scanner.nextLine();                  // next line
  }
}

出力結果

output
 1: 32343 "69917" "6991701" "島根県" "仁多郡奥出雲町" "亀嵩"
 2: 32343 "69915" "6991515" "島根県" "仁多郡奥出雲町" "鴨倉"
 3: 32343 "69915" "6991514" "島根県" "仁多郡奥出雲町" "河内"

findInLineでパターン検索する

findInLineメソッドに指定した検索パターンに一致する文字列を、スキャナの現在位置から行末までの間で検索します。
パターンに一致する文字列が見つからなければnullを返します。

signature
public String findInLine​(String pattern)

public String findInLine​(Pattern pattern)

コード例

exmaple
File in = new File("path/to/sample.in");
Scanner scanner = new Scanner(in);
try (scanner) {
  int counter = 0;
  while (scanner.hasNextLine()) {
    String find = scanner.findInLine("69915[0-9]{2}");
    System.out.println(String.format("%2d: %s", ++counter, find));
    scanner.nextLine(); // next line
  }
}

出力結果

output
 1: null
 2: 6991515
 3: 6991514

Java 9で追加されたAPI

tokens

トークンのストリームを返します。

signature
public Stream<String> tokens()

コード例

example
String in = "apple banana cherry durian elderberry";
Scanner scanner = new Scanner(in);
try (scanner) {
  final List<String> fruits = scanner.tokens()
    .map(String::toUpperCase)
    .collect(Collectors.toUnmodifiableList());
  System.out.println(fruits);
}

出力結果

output
[APPLE, BANANA, CHERRY, DURIAN, ELDERBERRY]

findAll

スキャナからパターンマッチのストリームを返します。

signature
public Stream<MatchResult> findAll​(Pattern pattern)

public Stream<MatchResult> findAll​(String patString)

コード例

example
File in = new File("path/to/sample.in");
Scanner scanner = new Scanner(in);
try (scanner) {
  List<String> list = scanner.findAll("\"[0-9]{5,}\"")
    .map(MatchResult::group)
    .collect(Collectors.toUnmodifiableList());
  System.out.println(list);
}

出力結果

output
["69917", "6991701", "69915", "6991515", "69915", "6991514"]

Java 10で追加されたAPI

新しいコンストラクタ

第2引数にCharsetを取る新しいコンストラクタが追加されています。コード例は省略します。

signature
public Scanner​(InputStream source, Charset charset)

public Scanner​(File source, Charset charset) throws IOException

public Scanner​(Path source, Charset charset) throws IOException

public Scanner​(ReadableByteChannel source, Charset charset)

その他のおさらいメモ

rubytomato@github
今までJavaをメインにやってきましたが、JavaScript(Node.js)の習得に取り組み始めました。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした