Java入門|コマンドライン引数でデータを受け取る

実行するときに値を渡せるようになると、プログラムはぐっと使いやすくなる

これまでのプログラムでは、読み込むファイル名をコードの中にあらかじめ決めて書いていました。
この方法は学習の最初には分かりやすいのですが、実際に使うことを考えると少し不便です。
なぜなら、別のファイルを読みたいときにも、毎回コードを書き換えなければならないからです。

そこで役立つのが、コマンドライン引数です。
コマンドライン引数を使うと、プログラムを実行するその場で、ユーザーが値を渡せるようになります。
このしくみを使えば、ファイル名や文字列などを実行時に自由に指定できるので、同じプログラムをもっと柔軟に使えるようになります。

ドラゴンボールの世界でたとえるなら、修行装置を起動するたびに、装置の中身を書き換えるのではなく、
「今日は重力50倍」
「今日は修行記録ファイルAを読む」
のように、起動するときに指示を渡せるようになるイメージです。
装置そのものは同じでも、渡す指示によって動き方を変えられるので、とても便利ですよね。

ここでは、コマンドライン引数とは何か、main メソッドの String[] args が何を意味しているのか、そして実際にファイル名を受け取ってファイルを読み込む流れを、ドラゴンボールの世界観に置き換えながらやさしく整理していきます。

コマンドライン引数とは何か

コマンドライン引数とは、プログラムを実行するときに外から渡す値のことです。

Javaでは、main メソッドが次のように定義されています。

public static void main(String[] args)
{
    ...
}

この args が、コマンドラインから渡された文字列を受け取るための入れ物です。
型は String[] なので、文字列の配列として受け取ることになります。

つまり、実行時にスペース区切りで入力した値が、順番に配列 args の中へ入っていくわけです。

ドラゴンボール風にいうと、修行装置を起動するときに外から渡す「指示カード」が args です。
カードが1枚なら指示は1つ、複数枚なら順番に複数の指示を受け取れます。

なぜコマンドライン引数が便利なのか

コードの中にファイル名を固定してしまうと、たとえば test3.txt を読むプログラムは、ずっと test3.txt しか読めません。
でも、コマンドライン引数を使えば、実行時に別のファイル名を渡せます。

たとえば、同じプログラムでも

java Sample10 training3.txt

java Sample10 message.txt

のように、渡す引数を変えるだけで読む対象を変えられます。

方法使い勝手
コードにファイル名を固定する別のファイルを読むたびにコード修正が必要
コマンドライン引数で渡す実行時に自由に切り替えられる

これはとても大きな違いです。
同じプログラムを何度も活用しやすくなるからです。

args は配列として扱う

コマンドライン引数は、配列 args に入ります。
そのため、次のように添字で取り出します。

書き方意味
args[0]1番目に渡した文字列
args[1]2番目に渡した文字列
args[2]3番目に渡した文字列

ここで少し注意したいのは、配列なので番号は 0 から始まることです。
1番目なのに args[1] ではなく、args[0] になるのがポイントです。

ドラゴンボール風に考えるなら、修行指示の一覧表があって、先頭の指示が0番に入るイメージです。

args.length で個数を調べる

配列には length という性質があり、要素の個数を調べられます。
コマンドライン引数でもこれを使うことで、正しく入力されたかを確認できます。

たとえば今回のように、ファイル名を1つだけ受け取るプログラムなら、引数の個数は 1 であるべきです。
そこで、次のように確認します。

if(args.length != 1){
    System.out.println("ファイル名を正しく指定してください。");
    System.exit(1);
}

このコードは、

  • 引数の個数が1個ではない
  • つまり使い方が正しくない

と判断したときに、メッセージを表示してプログラムを終了しています。

これはとても大切です。
入力が正しい前提で進めてしまうと、あとで配列の範囲外アクセスなど別の問題が起きやすくなるからです。

サンプルプログラムで見てみよう

ここでは、実行時に渡したファイル名を使って、そのファイルの内容を1行ずつ読み込んで表示する例を見ていきます。

まず、同じフォルダに次のようなファイルを用意しておくイメージです。

training3.txt

悟空は朝から修行をしていた
ベジータは静かに気を高めていた

このファイルを、プログラム実行時にファイル名として渡します。

ファイル名:Sample10.java

import java.io.*;

class Sample10
{
    public static void main(String[] args)
    {
        if(args.length != 1){
            System.out.println("読むファイル名を正しく指定してください。");
            System.exit(1);
        }

        try{
            BufferedReader br =
                new BufferedReader(new FileReader(args[0]));

            String str;
            while((str = br.readLine()) != null){
                System.out.println(str);
            }

            br.close();
        }
        catch(IOException e){
            System.out.println("ファイルの入出力で問題が発生しました。");
        }
    }
}

このプログラムの流れ

このプログラムの動きを順番に整理すると、次のようになります。

手順内容
1引数の個数が1個かどうか確認する
2args[0] をファイル名として使う
3FileReader と BufferedReader で読み込み準備をする
4readLine で1行ずつ読み込む
5読み込んだ行を画面へ表示する
6ファイルを閉じる

この流れを見ると、コマンドライン引数は「ファイル名を固定しないための入口」として働いていることが分かります。

実行方法

このプログラムは、次のように実行します。

java Sample10 training3.txt

ここで、

  • Sample10 はクラス名
  • training3.txt はコマンドライン引数

です。

このとき、training3.txt という文字列が args[0] に入ります。

実行結果のイメージはこうです。

悟空は朝から修行をしていた
ベジータは静かに気を高めていた

つまり、プログラムの中ではファイル名を直接書いていないのに、実行時に指定したファイルをちゃんと読めているわけです。

args[0] がファイル名になる仕組み

この部分が、コマンドライン引数の中心です。

new FileReader(args[0])

args[0] は、ユーザーが実行時に入力した最初の文字列です。
今回なら training3.txt が入ります。

そのため、

new FileReader(args[0])

は、実質的には

new FileReader("training3.txt")

と同じ意味になります。

ただし、コードの中にファイル名を固定していないので、実行時に別の名前を渡せるところが大きな違いです。

while 文で最後まで読み込む

このコードの中で、とても大事なのが次の部分です。

while((str = br.readLine()) != null){
    System.out.println(str);
}

ここでは、while の条件の中で1行読み込み、その結果を str に代入しています。
そして、その値が null でないかどうかを調べています。

この書き方を分解すると、意味はこうです。

部分意味
str = br.readLine()1行読み込んで str に入れる
!= nullまだファイルの終わりではないか確認する

readLine は、ファイルを最後まで読み終わると null を返します。
つまり、

  • 行が読めた間は繰り返す
  • 読めなくなったら終わる

という流れになります。

これによって、ファイルの中身を最後の行まで順番に読み出せるのです。

null とは何か

ここで出てくる null は、何も参照していない状態値がない状態を表す特別な値です。
今回の readLine では、「もうこれ以上読める行がありません」という合図として使われています。

ドラゴンボール風にいえば、記録庫の巻物を最後まで読み終わったあとに、
「次のページはありません」
と告げられるようなものです。
その合図が null です。

引数が間違っていたときの処理

このプログラムでは、最初に引数の個数をチェックしています。

if(args.length != 1){
    System.out.println("読むファイル名を正しく指定してください。");
    System.exit(1);
}

これはとても親切な作りです。
たとえば、ユーザーが引数を入れ忘れたり、逆に余計な引数を渡したりした場合、そのまま進むと意図しない動きになります。

そこで、

  • 使い方が間違っていたら
  • 先にメッセージを出して
  • プログラムを終了する

という流れにしています。

このように、最初に入力チェックをしておくと、トラブルが分かりやすくなります。

System.exit(1) の意味

System.exit(1) は、プログラムを終了するための命令です。
ここでは「正常な終了ではなく、入力ミスなどの理由で終了した」という意味合いで使われています。

学習段階では、
条件が正しくないので、ここで処理を終える命令
と理解しておけば十分です。

コマンドライン引数は複数渡すこともできる

今回はファイル名1つだけを受け取っていますが、コマンドライン引数は2つ以上渡すこともできます。

たとえば、

java Sample10 training3.txt Hello GoodBye

のように実行すると、配列の中は次のようになります。

要素内容
args[0]training3.txt
args[1]Hello
args[2]GoodBye

つまり、プログラムを実行するときに、複数の情報をまとめて渡せるわけです。
この仕組みを使えば、ファイル名だけでなく、検索文字列や設定値なども受け取れるようになります。

図でコマンドライン引数の流れを整理する

コマンドライン引数の基本は、図にするとかなり分かりやすいです。

この図は、実行時に入力した文字列が args 配列に入り、その値がファイル名として利用される流れを表しています。
プログラムの外から渡したデータが、main メソッドの引数として受け取られ、そのまま処理に使われます。

図で while と null の流れを整理する

ファイルを最後まで読む部分も、図にすると理解しやすいです。

この図は、readLine が1行ずつ文字列を返し、最後まで読むと null を返すことで while 文が終了する流れを表しています。
条件式の中で読み込みと判定を同時に行っていることが分ります。

コマンドライン引数を使うと何がうれしいのか

コマンドライン引数を使う一番のよさは、プログラムを作り直さなくても使い方を変えられることです。

固定値だけのプログラムコマンドライン引数を使うプログラム
ファイル名が決め打ち実行時に好きなファイル名を指定できる
柔軟性が低い使い回ししやすい
変更のたびにソース修正実行コマンドを変えるだけで対応できる

これは、実用的なプログラムを作るうえでとても大きな利点です。
学習用のサンプルから、実際に使える道具へ一歩近づく感覚です。

ドラゴンボールの世界でも、修行装置が毎回同じ設定しか受け付けないより、起動時に指示を変えられるほうがずっと使いやすいですよね。
Javaのコマンドライン引数も、それと同じようにプログラムの柔軟性を高めてくれます。

ここで押さえておきたいポイント

最後に、このテーマで大事な点を整理しておきます。

ポイント内容
コマンドライン引数実行時にプログラムへ渡す文字列
String[] args渡された文字列を受け取る配列
args[0]最初に渡した文字列
args.length渡された文字列の個数
引数チェック使い方の誤りを早めに見つけるために重要
while と readLineファイルの最後まで1行ずつ読むための基本形
nullこれ以上読める行がないことを表す合図

コマンドライン引数を理解すると、プログラムは「決められた通りにしか動かないもの」から、「外からの指示に応じて動きを変えられるもの」へ変わっていきます。
これはとても大きな進歩です。

ドラゴンボールの世界でいえば、修行装置が固定メニューしか持たない状態から、その日の目的に応じて指示を受け取って動けるようになる感じです。
Javaでも同じで、コマンドライン引数を使えるようになると、プログラムがぐっと実用的で扱いやすくなります。