【Java初心者用】CSVデータの合計を求める

Java

【Java初心者用】CSVデータの合計を求める

 

今回は、CSVデータから合計や平均、最大値、最小値を求めるプログラムについて解説します。

サンプルプログラムで解説する内容は、以下の3つのパターンです。

  1. 変数に初期値としてCSVデータを代入している
  2. キーボードから自由に1行CSVデータを入力する
  3. 既存のファイルから複数行のCSVデータを読み込んで処理する

テストの点数などは「,」(カンマ)で区切られた形式で保存されることがあります。この「,」で区切ったデータを「CSVデータ」と一般的に呼び、昔から頻繁に利用されてきました。

Javaで、このCSVデータから合計や平均、最大値、最小値を求めるプログラムの書き方について解説します。CSV形式を扱う場合、Stringクラスに用意されている「split()」メソッドを利用すると簡単に処理することができます。

 

まず、この記事は以下のような人を対象としています。

対象者・Javaの勉強をしている初心者

・JavaでCSV形式のデータの合計などを求めるプログラムの作り方を知りたい人

・Javaのsplit()メソッドの使い方を知りたい人

 

この記事を読むと、次のようなことが理解できるようになります。

この記事を読むとできること・CSV形式のデータの構造を知ることができる

・Javaのsplit()メソッドの使い方を理解することができる

・CSV形式のデータの各種処理をおこなう様々なプログラムを作成することできる

 

 

花子さん
花子さん

Excelなど他のソフトで作成したCSV形式のデータの合計や平均などを求めたいのですが、Javaではどのように処理すれば良いのですか?

続石講師
続石講師

CSV形式のデータは数字のデータであっても、文字列のデータです。
Javaに用意されている文字列操作メソッドを使えば処理できます。

花子さん
花子さん

文字列操作メソッドですか? 具体的にはどんなメソッドですか?

続石講師
続石講師

CSV形式のデータ専用ではないのですが、「split()」メソッドを利用すると処理できます。今回は、このメソッドの使い方を具体的に説明します。

 

CSV形式のデータ

CSVは「Comma-Separated Values」の略で、データを「,」で区切って表現するデータ形式です。あるプログラムで発生したデータをCSV形式で保存し、別のプログラムで読み込み処理することが簡単におこなえます。単純な構成なため、簡単に処理ができるので古くからプログラム間のデータ移行で利用されています。

例えば、テストの結果(国語50点、数学60点、英語70点)を表現する場合、「50,60,70」と記述されます。

記述されている点数は人間から見れば「数値」ですが、プログラムから見れば「数字(文字)」となるので、各種計算をおこなう場合は数字(文字)を数値に変換する必要があります。

 

その他のデータ形式

CSV形式のデータは単純な形式で記述されるため、プログラム側での処理は非常に簡単におこなえるのですが、各データが何を意味しているのかは全く理解できません。以下の例のように、1行目に各データのタイトルを記述する場合もありますが、各データには意味はないため1つのデータ(例えば、「50」)だけを見ると、プログラム側ではそのデータが何を意味しているのかは分かりません。

国語,数学,英語

50,60,70

そこで、プログラム側でも読み込んだデータの意味を理解できるように、データに意味を持たせるデータ形式が考え出されました。

例えば、CSV形式で都道府県名「大阪」で市の数が「33」を表現すると、「大阪,33」となるのですが、このデータだけを見て、都道府県名と市の数と理解できるでしょうか? 名前と年齢と判断されてしまうかも知れません。

XML形式

XMLは「eXtensible Markup Language」の略で「拡張可能なマークアップ言語」です。データに「<>」で表現されるタグをつけ、そのデータに意味を持たせます。プログラムでは、タグを読み込むことで、どんな意味があるのか判断することができます。

XMLは独自に定義することができるので、特定の業界内でやり取りされる独自のデータを表現する際に利用されています。また、Linuxの各種サーバ用プログラムの環境設定ファイルで古くから利用されています。ホームページを作成する際に利用されるHTMLもXMLの一種と言えます。

但し、タグを解析しなければならないので、プログラムの処理内容が複雑になってしまうという欠点があります。

CSV形式の「大阪,33」をXML形式で表現すると以下のようになります。

<prefecture>大阪</prefecture>

<city>33</city>

タグが付けられているので、大阪が「都道府県名」(prefecture)であることを理解することができます。

JSON形式

JSONは「JavaScript Object Notation」の略で「JavaScriptのオブジェクト記法によるデータ表現記法」です。データを「key(ラベル)、value(値)」形式で表現します。keyがデータの意味を示すので、何を意味しているのか判断することができます。

元々はJavaScriptで利用されていたのですが、データに意味を持たせ、なおかつ処理がXMLより簡単なので、インターネットでのデータ交換で良く利用されています。また、最近はソフトウェアの設定ファイルの表現方法としても利用されてます。エンジニアは是非、JSON形式のデータの取り扱い方法には慣れていた方が良いでしょう。

CSV形式の「大阪,33」をJSON形式で表現すると以下のようになります。

{

“prefecture”: “大阪”,

“city”: 33

}

keyが付けられているので、大阪が「都道府県名」(prefecture)であることを理解することができます。

 

split()メソッド

では、CSV形式のデータを操作する話に戻します。

CSV形式のデータは単純な文字列なので、処理する場合は各種文字列操作メソッドを利用しますCSV形式のデータは個々のデータが「,」で区切られているので、まずは「,」をもとに1つの文字列を個々のデータに分離する必要があります。

「split()」メソッドは「文字列内の特定の文字(記号)を区切り文字としてデータを分離する」メソッドです。

split()メソッドの定義内容は以下の通りです。

public  String[ ] split(String regex)

()内で指定した文字列(正規表現)で分割し、それぞれのデータ(文字列)を配列に格納します。

使用例は以下の通りです。

String  csvData = “大阪,33”;

String[ ] dataArray = csvData.split(“,”);

CSV形式のデータを文字列として変数csvDataに代入しておき、「,」を区切り文字として文字列を分割し、分割した個々のデータを文字列型配列dataArrayに代入しています。

この結果、dataArray[0]には”大阪”、dataArray[1]には”33″のデータがそれぞれ代入されています。注意することは「33」は数値(int型)ではなく、文字列(String型)であるということです。

CSV形式のデータ(変数)を処理するプログラム

では、実際のサンプルを以下に示します。説明を簡単にするためにこのサンプルでは、CSV形式のデータを変数であらかじめ代入しています。したがって、何度実行しても結果は同じです。

CSV形式のデータから、個数、合計、平均、最大値、最小値を求めてコンソールに結果を表示します。

実行結果は以下の通りです。

プログラムの解説

このプログラムのメイン部分では、具体的な処理ではなくメソッド名のみを記述しています。以前の記事でも説明していますが、main()メソッド内にはなるべく詳細なコードを記述せず、行いたい処理を定義しているメソッド名のみを記述した方が、プログラムの全体像を把握しやすくなります

また、変数にCSV形式のデータを初期値で与えておき、そのデータを利用して各種計算をするのですが、文字列のままではなく配列に分割してプログラム内で利用するようにしています(31行目から37行目のgetCsv()メソッド)。文字列のままだと、その都度分割しなければならず、手間が増えてしまうため、最初に分割し、自動的に代入された配列を利用しています。

プログラムを勉強し始めた初心者がやりがちな書き方は、合計も平均も最大値も最小値も1つのfor文でまとめておこなってしまうものです。確かに入力するコード量が減るため、良いプログラムのように見えますが、1つの処理内に複数の目的が記述されていると修正が必要となった時に、どこを修正すれば良いのか分かりずらくなってしまいます。したがって、2度手間のように思えるかもしれませんが、合計、最大、最小などは別々のメソッドに分けて記述しましょう。別々に分けることによって、最大値を求めるプログラムの修正が必要となった時は、最大値を求めるメソッド部分だけに注目すれば良いため、他の部分を間違えて修正したりする危険性を防ぐことができます。

プログラムを記述する時に必要なのは「メンテナンスがしやすい」プログラムを作成することです。

なお、プログラム内にコメントを記述していますので、各コードの説明はここでは省略します。

 

CSV形式のデータをキーボードから読み込み処理する

このサンプルでは、CSV形式のデータをキーボードから読み込み、個数、合計、平均、最大値、最小値を求めてコンソールに結果を表示します。なお、本来であればキーボードから正しい書式のCSV形式のデータが入力されているかチェックしなければなりませんが、説明を簡単にするためにチェック機能は実装していません。例えば、点数(数字)を入力しなければならないのに、英字や記号を入力すると例外が発生してしまいますが、対策はしてませんので注意して下さい。キーボードから入力できるCSV形式のデータは1行のみとします。

実行結果は以下の通りです。

プログラムの解説

このプログラムを見て、気が付いたでしょうか? 実は変数を使ったサンプルプログラムと比べると変更されている部分は、33行目から44行目までの「getCsv()」メソッド部分だけです。main()メソッド内の記述やそれ以外のメソッドの記述内容は前のサンプルと全く同じです。

このように、処理をメソッドに分けて定義しておくと、処理内容が変更になった場合は関連するメソッドだけを修正し、他は一切手を加えなくて済みます

 

では、キーボードからデータを読み込む「getCsv()」メソッドの内容について解説します。

35行目 Scanner stdin = new Scanner(System.in);

キーボードからデータを読み込む場合、Scannerクラスを利用するのが一番簡単です。コンストラクタの引数に、どこからデータを読み込むか、読み込み元を指定します。キーボード(正確には、標準入力)を示す用語は「System.in」となるので、それを引数に指定します。

37行目 System.out.println(“CSVデータを入力してください”);

Scannerクラスを利用してキーボードからデータを読み込む場合、そのままでは何も表示されずにいきなり入力待ち状態になります。したがって、ユーザに何をして欲しいのかメッセージを表示しています。このメッセージを表示しない場合、入力待ちでプログラムが一時停止になり、ユーザはプログラムが故障していると勘違いする危険性があります。ユーザの使いやすさを考えて、キーボードを利用する場合は、必ずメッセージを表示させましょう

40行目 String line = stdin.nextLine();

Scannerクラスにはキーボードからデータを読み込むメソッドが複数用意されています。その中で、入力された1行のデータをまとめて読み込む場合は「nextLine()」メソッドを利用します。読み込んだ1行の文字列を変数lineに代入しています。なお、int型として読み込む場合は「nextInt()」、double型として読み込む場合は「nextDouble()」をそれぞれ利用します。

43行目 return line.split(“,”);

キーボードから読み込んだ文字列を「,」を区切り文字として分割し、その配列を呼び出し元(main()内の10行目)に返しています。

 

その他の処理についてはコメント文を参考にして下さい。

CSV形式のデータをファイルから読み込み処理する

このサンプルでは、CSV形式のデータが記述されているファイルからデータを読み込み、個数、合計、平均、最大値、最小値を求めてコンソールに結果を表示します。ファイルには正しい形式でデータが記述されていることとします。また、CSV形式のデータは何行記述されていても良いこととします。

利用したCSVファイルの内容は以下の通りです。

実行結果は以下の通りです。

プログラムの解説

さすがに気が付いていると思いますが、このサンプルプログラムも「getCsv()メソッド」の定義内容だけが変更され、他のコードは一切変わっていません。

メソッドを利用する利点は、詳細な処理内容が変更されても他のコードに影響を与えない点にあります。もちろん、影響を与えないようなプログラムの設計が必要となります。初心者の人はすぐには自力で作成できないかもしれませんが、このように色々なパターンのプログラムを見て勉強することが、スキルアップの近道となります。

 

では、ファイルからデータを読み込む「getCsv()」メソッドの内容について解説します。

ファイルからデータを読み込む場合に注意することは、ファイルに何行のデータが書き込まれているかプログラムからは分からない、ということです。したがって、データが何行あっても大丈夫なようにプログラムを記述する必要があります。

一般的に、処理する回数(ファイルから読み込む行数)が分からない場合はwhile文を利用します。具体的には、ファイルから1行読み込む処理を、読み込む行がなくなるまで繰り返します。

 

38行目 String file = “score.csv”;

読み込むファイル名を文字列で指定しています。今回は「score.csv」というファイル名にします。

41行目 List<String> arrayList = new ArrayList<>();

ファイルから読み込んだCSVの個々のデータを保存する空のArrayListを用意します。読み込むデータの個数が不明なため、随時データを追加できるArrayList(可変長配列)を利用します。

44行目~45行目 try( BufferedReader br = new BufferedReader(new FileReader(file))) {

指定したファイル名をBufferedReaderクラスを利用してオープンしています。ファイルをオープンするだけであればFileReaderクラスで十分ですが、このクラスのみを利用するとファイルから行単位での読み込みができず、プログラムの処理が面倒になります。また、その都度データをファイルから読み込むので時間が無駄になります。そこで、BufferedReaderクラスをデコレート(装飾)し、行単位で読み込める機能とバッファリングしながら時間を有効活用する機能を追加しています。

また、try部分で()を使ってファイルをオープンしていますが、これは「try-with-resources文」と呼ばれ、この表現でオープンしたファイルは自動的にクローズ処理されるので、プログラム内で明示的にファイルをクローズする処理を記述する必要はありません

48行目 String line =”;

ファイルから読み込んだ1行を格納する変数の初期化をしています。

51行目 while( ( line = br.readLine() ) != null ) {

「line=br.readLine()」でファイルから読み込んだ1行を変数lineに代入します。「!=null」で読み込んだデータがnull(空)でないかどうかをチェックし、nullでない場合にwhile文を繰り返します。この記述はファイルからデータを読み込む際の定型的な記述内容です。ファイルから読み込むデータが無くなるまで繰り返し処理をおこないます。

54行目 String[] csvArray = line.split(“,”);

ファイルから読み込んだ1行のデータはCSV形式のデータなので、「,」で各データを分割し、String型の配列csvArrayに1個ずつ格納します。

56行目 for( String data : csvArray ) {

拡張for文を利用し、分割した個々のデータを1個ずつ取り出し、変数dataに代入します。取り出すデータが無くなるまで繰り返します。

57行目 arrayList.add( data );

配列から取り出したデータをadd()メソッドを使ってarrayListの末尾に追加します。配列csvArrayは読み込んだ1行のデータしか格納されていませんが、arrayListはファイル内の全てのデータを格納します。arrayListは可変長配列なので、add()メソッドを利用すれば何個でもデータを追加格納することができます。

61行目から63行目 } catch( IOException e) { System.out.println(“ファイル読み込みエラー”); }

44行目から60行目の間の処理を起こった際にファイル入出力に関する例外(IOException)が発生したら、エラーメッセージとして「ファイル読み込みエラー」を表示させます。

66行目 return arrayList.toArray( new String[ arrayList.size()  ] );

全てのデータが格納されたarrayListを配列に変換して、呼び出し元(main()の14行目)に返します。このメソッドでは、読み込んだデータを最終的にString型の配列(固定長配列)として返すように定義しています。しかし、ファイルから読み込んだ全てのデータを保存しているのはArrayList(可変長配列)です。ArrayListを配列として直接返すことはできないため、toArray()メソッドを利用してArrayListを固定長配列に変換しています。toArray()メソッドの引数は新規に作成する固定長配列の要素数を指定します。配列の要素数は「.length」プロパティで求められますが、ArrayListの場合はsize()メソッドを実行して求めます。

ファイルからデータを読み込んで、return文で配列を返すまでの処理を図にしました。

このように、最終的にarrayListには分割された全てのデータが文字列として格納されています。

 

それ以外の処理は全く変更点はありません。コメント文を参考に確認して下さい。

 

まとめ

今回は、split()メソッドを使ってCSV形式のデータを処理するプログラムについて説明しました。処理自体は「split()」メソッドを使えば簡単におこなうことができます。今回は、「変数に初期値として与えておく場合」、「キーボードからデータを読み込む場合」、「ファイルからデータを読み込む場合」の3パターンについて解説しました。

是非、CSV形式のデータを扱うプログラムとして参考にして下さい。

 

・文字列のCSV形式のデータはsplit()メソッドを使って分割する

・split()メソッドで分割されたデータは文字列のデータとして配列に格納される

・計算をおこなう場合はInteger.parseInt()メソッドなどを利用して数値に変換しなければならない

 

続石講師
続石講師

CSV形式のデータを扱うプログラムの書き方は理解できましたか?

花子さん
花子さん

split()メソッドが便利なメソッドだということが分かりました!!

3つのパターンのサンプルも説明してもらえたので参考にします!!

続石講師
続石講師

ファイルから読み込むパターンが一番難しいかも知れませんが、一番利用するはずです。ぜひ、パターンとして参考にして下さい!!

タイトルとURLをコピーしました