最終課題

ステップ0

以下のステップを実行する前に,概要課題のデータから必要なデータをダウンロードしておいてください.

ステップ1

1-A. 問題説明

コマンドライン引数で与えられたデータ(複数可能)を読み,以下の分析結果を出力してください.

  • 公開年ごとの映画の本数.ただし,公開年の昇順で出力してください.

なお,データの各桁は,次の項目があり,各行には,各桁の値が記入されています. ただし,空欄の場合もありますので,注意してください.

  1. Release Year
    • 公開年
  2. Title
    • タイトル
  3. Origin/Ethnicity
    • 映画作成地域
  4. Director
    • 監督
  5. Cast
    • 出演者
  6. Genre
    • ジャンル
  7. Wiki Page
    • Wikipediaのページ
  8. Plot
    • Wikipediaの内容から推測されたあらすじ

1-B. 実行例

$ java MovieAnalyzer1 movie1.csv
1915,1
1931,2
# 途中省略
2015,1
2016,1
2017,2
$ java MovieAnalyzer1 movie2.csv
1910,1
1920,1
# 途中省略
2015,7
2016,6
2017,5
$ java MovieAnalyzer1 movie1.csv movie2.csv
1910,1
1915,1
# 途中省略
2015,8
2016,7
2017,7
$ java MovieAnalyzer1 # 何も出力されない.
  • 一つのファイルだけでなく,複数のファイルが指定されたとしても,正常に処理してください.
  • ファイルが何も指定されなかった場合は,何も出力されません.
  • 違うフォーマットのファイルが指定されることは考慮する必要はありません.
  • 公開年の昇順で出力するようにしてください.

1-C. ヒント

Mapのキーの昇順で表示する.

年と対応する作品数を記録するために典型的にはMapを利用します. Mapはその実装によりHashMapTreeMapに分けられます. HashMapは並び順は一切考慮されません. 一方,TreeMapはキーの辞書順で並べられます. そのため,HashMapではなく,TreeMapを使いましょう. 今まで HashMapと書いていた箇所を単純に TreeMapに置き換えるだけで良いです.

処理の流れ

次の流れで処理すれば良いでしょう.

  1. TreeMap<Integer, Integer>型の変数を宣言し,初期化する.
  2. コマンドライン引数で渡された文字列をファイルとして順に処理する.
    1. 1行ずつファイルを読む.
      1. 1行をコンマ(,)で区切る.
      2. 最初の要素が公開年であるため,String型をInteger型に変換する.
      3. TreeMap型の変数に公開年をキーに映画の本数を受け取る.
      4. 登録されていない場合は映画の本数を0に初期化する.
      5. 映画の本数を1増加させ,TreeMap型の変数に再登録する.
  3. TreeMap の要素を順に出力する.

1-D. 評価項目

  • 概要に示した評価項目
  • データを変更しても,例外なく実行結果を出力できるか.
  • 引数が与えられなかった場合でも,適切な処理が行えるか.

ステップ2

2-A. 問題説明

コマンドライン引数で何もデータが与えられなかった場合,標準入力からデータを受け取るようにしてください. 出力内容はステップ1と同様です.

2-B. 実行例

$ java MovieAnalyzer2 movie1.csv # ステップ1の実行結果と同じ.
1915,1
1931,2
# 途中省略
2015,1
2016,1
2017,2
$ head -1 movie1.csv | java MovieAnalyzer2 # 1行のみ
1915,1
$ java MovieAnalyzer2 # 入力をキーボードから与える.
1997,"Princess Mononoke",Japan,"Hayao Miyazaki",,Animation,https://en.wikipedia.org/wiki/Princess_Mononoke,
# この行はキーボードから1行で入力する(途中で改行せず,最後のコンマの後ろで改行する).
# ダブルクォート(&quot;)も省略せず入力する.
[Ctrl-D] # Control + D を入力し,入力終了(EOF (End Of File) を送る).
1997,1 # この行が出力結果.

2-C. ヒント

ファイルから読み取る時と標準入力から読み取る時の両方を BufferedReader として扱えると処理が重複せずに済みます. 次のようなフローチャートで考えてみると良いでしょう.

graph TB; A[開始] B{args.length > 0} C[System.inを開く] D["ループ開始 (i = 0)"] E[ループ終了] F{i < args.length} G["args[i]を開く"] H[分析処理] J[分析処理] I[表示処理] A --> B B -->|false| C B -->|true| D D --> F C --> H H --> I F -->|true| G G --> J J --> F F -->|false| E E --> I

ここで,分析処理と書かれている部分をBufferedReaderを引数として受け取るメソッドを定義し,そのメソッド内で分析を行うようにすると良いでしょう. そのために,System.inを開く,args[i]を開くという処理で,BufferedReader型の変数が得られるようにしましょう.

2-D. 評価項目

  • 概要に示した評価項目
  • データを変更しても,例外なく実行結果を出力できるか.
  • 引数が与えられなかった場合でも,適切な処理が行えるか.

ステップ3

3-A. 問題説明

コマンドライン引数,もしくは標準入力から与えられたデータ(複数可能)を読み,以下の分析結果を出力してください.

  • 出演者ごとの出演映画数.
  • 出演した映画のジャンル
  • 出演者の名前のアルファベット順に出力してください.

ただし,Castの項目には複数の出演者が記載されている場合があります. Genreの項目も同様に複数のジャンルが記載されている場合があります. そのため,読み込んだ各行を単純に line.split(",") とすると期待通りの結果が得られません.

そこで,CsvSplitter というプログラムを用意しましたMovieAnalyzer3.javaと同じディレクトリに置き,次のようなプログラムで期待通りの結果が得られます.

CsvSplitter splitter = new CsvSplitter();
// ...
ArrayList<String> items = splitter.split(line);
// lineが次の1行であった場合,
//      item1, "item2.1, item2.2", item3
// => 分割結果は次の通り.
// item1
// item2.1, item2.2
// item3

3-A-a. 満たすべき要件

ただし,以下の2つの要件を満たすようにプログラムを作成してください.

  • MovieAnalyzer3.java のフィールドが4つ以下になるようにしてください.
  • MovieAnalyzer3.java のそれぞれのメソッドにおいても,宣言する変数が4つ以下になるようにしてください.

3-B. 実行例

$ java MovieAnalyzer3 movie1.csv
A Jie,1,[drama, animation, fantasy]
ANR,1,[unknown]
Aaron Eckhart,1,[action, sci-fi]
Adam Pally,1,[comedy]
Adele Jergens,1,[comedy]
# ...途中省略
Yusuf Khan,1,[action]
Zach Galligan,1,[horror]
Zeenat Aman,1,[action]
Zhang Yuanyuan,1,[drama, animation, fantasy]
$ java MovieAnalyzer3 # 標準入力から受け取る.
1997,"Princess Mononoke",Japan,"Hayao Miyazaki",,Animation,https://en.wikipedia.org/wiki/Princess_Mononoke,
[Ctrl-D] # 入力終了.キャスト部分に何も入力されていないため,何も出力されない.
$ head -2 movie3.csv | java MovieAnalyzer3
Edmund Gwenn,1,[comedy]
Elizabeth Taylor,1,[comedy]
Irene Dunne,1,[comedy]
James Raglan,1,[comedy]
William Powell,1,[comedy]

3-C. ヒント

3-C-a. 満たすべき要件のために

  • 満たすべき要件を果たすため,役者名,出演数,ジャンルをまとめる型を導入すると良いでしょう.
    • Cast.javaを定義し,フィールドに name, count, genres を宣言します.
      • countInteger型で0で初期化しましょう.
      • genresArrayList<String>型であり,同じく初期化しておきましょう.
      • void update(String genreString)メソッドでは,ジャンルと出演数を更新します.
        • ただし,genreStringも複数のジャンルを含む可能性があります.
          • ジャンルはコンマ(,)もしくはスラッシュ(/)で区切られます.
          • そのため,genreString.split("[,/] *") で分割可能です.
    • MovieAnalyzer2 でデータを読むときには,Castupdateメソッドを呼び,必要な値を渡しましょう.
    • Castを出力するためのStringを返すstring()メソッドを用意しておくと良いでしょう.
public class Stats{
    // ... 途中省略
    String string(){
        return String.format("適切な文字列に置き換える");
    }
}

3-C-b. String.format メソッド.

printfと同じように利用でき,結果を文字列として受け取れるようになるメソッドです.

String name = "Tamada";
String hello = String.format("Hello, %s", name);
// => hello には,"Hello, Tamada" が代入される.

3-D. 評価項目

  • 概要に示した評価項目
  • データを変更しても,例外なく実行結果を出力できるか.
  • 独自の型を作成できているか.
  • 引数が与えられなかった場合でも,適切な結果が出力できるか.

ステップ4

4-A. 問題説明

与えられたデータを読み,与えられたオプションを元に適切な処理(主にフィルタリング)を行ってください.

$ java MovieAnalyzer4 --help
Usage: java MovieAnalyzer4 [OPTIONS] [Movie Files...]
    -h, --help:            このメッセージを表示する.
    -c, --cast <Cast名>:   ここで指定されたキャストでフィルタリングする(部分一致).
    -t, --title <タイトル>: ここで指定されたタイトルでフィルタリングする(部分一致).
  • -hもしくは,--helpが指定された場合は,上記のメッセージを表示してください.
  • -cもしくは,--castが指定された場合は,続いてキャスト名が指定されます. 指定された文字列を持つキャストでフィルタリングし,ステップ3と同じ結果を出力してください.
  • -tもしくは--titleが指定された場合は,続いてタイトルが指定されます. 指定された文字列を持つタイトルでフィルタリングし,ステップ3と同じ結果を出力してください.

4-B. 実行例

$ java MovieAnalyzer4 --help
Usage: java MovieAnalyzer4 [OPTIONS] [Movie Files...]
    -h, --help:            このメッセージを表示する.
    -c, --cast <Cast名>:   ここで指定されたキャストでフィルタリングする(部分一致).
    -t, --title <タイトル>: ここで指定されたタイトルでフィルタリングする(部分一致).
$ java MovieAnalyzer4 -h # --help が指定された場合と同じ結果となる.
Usage: java MovieAnalyzer4 [OPTIONS] [Movie Files...]
    -h, --help:            このメッセージを表示する.
    -c, --cast <Cast名>:   ここで指定されたキャストでフィルタリングする(部分一致).
    -t, --title <タイトル>: ここで指定されたタイトルでフィルタリングする(部分一致)
$ $ head -1 movie4.csv | java MovieAnalyzer4 # 何も指定されなければ標準入力から受け取る.
Saravanan,1,[unknown]
Suvarna Mathew,1,[unknown]
$ cat movie?.csv | java MovieAnalyzer4 -c 'Tom Cruise' # トムクルーズでフィルタリング.
Tom Cruise,3,[comedy, drama]
$ java MovieAnalyzer4 -t Pirates movie4.csv # タイトルにPiratesを含むもの.
Buster Crabbe,1,[serial]
Ian McShane,1,[action-adventure, family]
Johnny Depp,1,[action-adventure, family]
Kevin McNally and Geoffrey Rush,1,[action-adventure, family]
Lois Hall,1,[serial]
Penélope Cruz,1,[action-adventure, family]

4-C. ヒント

4-C-a. Arguments型

  • 指定されたオプションを格納するArguments型を作成しましょう.
    • 次のフィールドを宣言しましょう.
      • Boolean型の help
      • String型のcast
      • String型の title
      • ArrayList<String> 型の arguments
    • void parse(String[] args) メソッドを用意しましょう.
      • このメソッド内でArgumentsのフィールドに値を代入しましょう.
      • -c-tが指定された場合は,インデックスを1つ進めて,変数に代入しましょう.

4-C-b. parseメソッド内の処理の例

void parse(String[] args){
    for(Integer i = 0; i < args.length; i++){
        // 途中省略
        else if(/* -t もしくは --title が指定された場合の条件式 */){
            i++;
            this.title = args[i];
        }
        // 途中省略
    }
}

4-D. 評価項目

  • 概要に示した評価項目
  • オプションの解析を適切に行えているか.
  • オプションが指定された場合,指定されなかった場合の両方で,適切に処理できているか.

ステップ5

5-A. 問題説明

キャストごとに出演作品をリストアップしましょう. 例えば,movie4.csvには,Tom Cruiseが出演している映画が2作品リストアップされています. それをキャストの次の行にリストアップしてみましょう. 次のような出力にしてください.

$ java MovieAnalyzer5 --cast 'Tom Cruise' movie4.csv
Tom Cruise,2,[comedy, drama] # キャスト名,出演作品数,ジャンル一覧
	Losin' It [comedy] # [tab]タイトル [ジャンル]
	Magnolia [drama]   # [tab]タイトル [ジャンル]

5-B. 実行例

$ cat movie4.csv | java MovieAnalyzer5 -t Pirates
Buster Crabbe,1,[serial]
	Pirates of the High Seas [serial]
Ian McShane,1,[action-adventure, family]
	Pirates of the Caribbean: On Stranger Tides [action-adventure, family]
Johnny Depp,1,[action-adventure, family]
	Pirates of the Caribbean: On Stranger Tides [action-adventure, family]
Kevin McNally and Geoffrey Rush,1,[action-adventure, family]
	Pirates of the Caribbean: On Stranger Tides [action-adventure, family]
Lois Hall,1,[serial]
	Pirates of the High Seas [serial]
Penélope Cruz,1,[action-adventure, family]
	Pirates of the Caribbean: On Stranger Tides [action-adventure, family]
$ cat movie*.csv | java MovieAnalyzer5 -c 'Tommy Lee Jones'
Tommy Lee Jones,6,[comedy, drama, musical comedy, action, biography, science fiction, fantasy]
	Hope Springs [comedy]
	The Package [drama]
	A Prairie Home Companion [musical comedy]
	Black Moon Rising [action]
	Cobb [biography, drama]
	Small Soldiers [science fiction, fantasy, action]

5-C. ヒント

Cast.javaを修正すると,過去のプログラムの動作に影響が出ます. そのため,Cast.javaCast5.javaにコピーし,MovieAnalyzer5.javaではCast型の代わりにCast5型を利用するようにプログラムを修正しましょう.

次に,タイトルとジャンルを格納する型Movieを作成します. そして,Cast5では,Castが持つフィールドに加え,出演作品を表すArrayList<Movie>型の変数moviesを追加しましょう. また,updateメソッドでタイトルも受け取るようにし,moviesに作品を追加していきましょう. 最後に出力部分で,キャスト名を出力後に,出演タイトル一覧を出力するようにすれば良いです.

5-D. 評価項目

ステップ6

6-A. 問題説明

3つのオプション,--origin, --genre, --outputを追加してください.

Usage: java MovieAnalyzer4 [OPTIONS] [Movie Files...]
    -h, --help:            このメッセージを表示する.
    -c, --cast <Cast名>:   ここで指定されたキャストでフィルタリングする(部分一致).
    -t, --title <タイトル>: ここで指定されたタイトルでフィルタリングする(部分一致)
    -o, --origin <ORIGIN>: ここで指定された映画作成地域でフィルタリングする(部分一致,大文字小文字を区別しない).
    -g, --genre <GENRE>:   ここで指定されたジャンルでフィルタリングする(部分一致).
    -O, --output <DEST>:   ここで指定されたファイルに結果を出力する.

Cast5.javaCast6.javaに,Movie.javaMovie6.javaにコピーして始めましょう. また,Cast6.java内のMovieMovie6に変更し,Movie6String型のoriginを追加しましょう. また,Moivie6originに必要な値を代入できるよう,MovieAnalyzer6を修正しましょう.

6-B. 実行例

$ java MovieAnalyzer6 movie3.csv -o japanese
Ai Kayano,1,[drama]
	2016 Tamayura ~Sotsugyō Shashin~ Part 4: Ashita [drama] (Japanese)
Akemi Negishi,1,[war drama]
	1953 Anatahan [war drama] (Japanese)
# 途中省略....
Yutaka Matsushige,1,[drama]
	2000 Eureka [drama] (Japanese)
Yūko Kaida,1,[fantasy, drama, science fantasy]
	2017 The Night Is Short, Walk on Girl [fantasy, drama, science fantasy] (Japanese)
$ java MovieAnalyzer6 --help
Usage: java MovieAnalyzer4 [OPTIONS] [Movie Files...]
    -h, --help:            このメッセージを表示する.
    -c, --cast <Cast名>:   ここで指定されたキャストでフィルタリングする(部分一致).
    -t, --title <タイトル>: ここで指定されたタイトルでフィルタリングする(部分一致).
    -o, --origin <ORIGIN>: ここで指定された映画作成地域でフィルタリングする(部分一致,大文字小文字を区別しない).
    -g, --genre <GENRE>:   ここで指定されたジャンルでフィルタリングする(部分一致).
    -O, --output <DEST>:   ここで指定されたファイルに結果を出力する.
$ java MovieAnalyzer6 movie3.csv -o japanese --genre fantasy
Gen Hoshino,1,[fantasy, drama, science fantasy]
	2017 The Night Is Short, Walk on Girl [fantasy, drama, science fantasy] (Japanese)
Hiroshi Kamiya,1,[fantasy, drama, science fantasy]
	2017 The Night Is Short, Walk on Girl [fantasy, drama, science fantasy] (Japanese)
Kana Hanazawa,1,[fantasy, drama, science fantasy]
	2017 The Night Is Short, Walk on Girl [fantasy, drama, science fantasy] (Japanese)
Kazuya Nakai,1,[fantasy, drama, science fantasy]
	2017 The Night Is Short, Walk on Girl [fantasy, drama, science fantasy] (Japanese)
Ryuji Akiyama,1,[fantasy, drama, science fantasy]
	2017 The Night Is Short, Walk on Girl [fantasy, drama, science fantasy] (Japanese)
Yūko Kaida,1,[fantasy, drama, science fantasy]
	2017 The Night Is Short, Walk on Girl [fantasy, drama, science fantasy] (Japanese)

6-C. ヒント

映画作成地域でフィルタリングする(部分一致,大文字小文字を区別しない)

大文字小文字で区別せずに比較するには,一旦両方を大文字(小文字)に変換して比較を行いましょう. 文字列を大文字に変換するにはtoUpperCaseメソッド,小文字に変換するにはtoLowerCaseメソッドを利用します.

String string = "Some cAsE";
String upper = string.toUpperCase(); // => upper には "SOME CASE"が代入される.
String lower = string.toLowerCase(); // => lower には "some case"が代入さえる.

大文字小文字を統一してから比較を行うことで,大文字小文字を区別せずに比較が行えるようになります.

出力先を切り替える.

Arguments型に対して,以下の処理を行いましょう.

  • 出力先を格納するString型のoutputを宣言してください.
  • 出力先を表すPrintWriter型のoutを宣言してください.初期値は代入しなくて良いです.
  • オプション解析(Arguments型のparseメソッド)にて,出力先を Argumentsoutputに代入するようにしてください.
  • PrintWriterを返す writerメソッドを用意しましょう.
    • outに何も代入されていなければ(nullであれば),次の処理を行ってください.
      • outputに文字が代入されていれば,そのファイルを開き PrintWriter型でラップして,outに代入しましょう.
        • out = new PrintWriter(new FileWriter(output));
      • output に文字が代入されていなければ,System.outPrintWriter型でラップしてoutに代入しましょう.
        • out = new PrintWriter(System.out);
    • 最後に,outreturn してください.
  • closeメソッドを用意しましょう.引数,返り値はありません.
    • outに値が代入されていれば(nullでなければ),closeしてください.
  • 今まで,System.out.printfを呼び出していたところを,Argumentsの実体に対して,writerメソッドを呼び出してください.
    • System.out.println -> arguments.writer().println
    • 全ての処理が終われば,arguments.close() を呼び出して出力ストリームを閉じてください.

6-D. 評価項目