Excelなどの表計算ソフトなどで以下のような表を作成したことがあるだろう。

このデータをプログラムで扱いたい。そんな時に便利なのがCSV形式である。
CSVとはComma-Separated Valuesの略でデータをカンマ区切りで表す汎用データフォーマットの一つだ。

例外なくすべての表計算ソフトでCSV形式で出力することができる。実際に先ほどのデータをCSV形式で保存したものが以下だ。
●sample.csv

年度,製品A,製品B
2016,210,1014
2017,220,990

行ごとに改行され、項目がカンマで区切られているのがわかる。
一般的に拡張子には.csvが用いられる。

このような汎用データフォーマットにはこのほかにもTSV、XML、JSONなど様々なものがあるがまずはこのもっともポピュラーなCSVデータをプログラムで扱ってみよう。

今回はJavaで扱ってみる。このように汎用データフォーマットをプログラムで扱えるようにすることを「パースする」などとも言うので覚えておくと良い。今回はjavaによるCSVパースだ。

1.まずは以下から先ほど作成したCSVファイルをダウンロードする。(文字コードはUTF-8)

2.ダウンロードしたファイルをJavaのプロジェクトルートに配置する。

3.以下のソースコードを打ち込む
●Sales.java

public class Sales{
	private String year;
	private int salesA;
	private int salesB;
	public Sales(String year,int salesA,int salesB){
		this.year=year;
		this.salesA=salesA;
		this.salesB=salesB;
	}
	@Override
	public String toString() {	
		return String.format("%s年度の製品Aの売上高%d、製品Bの売上高%d", this.year,this.salesA,this.salesB);
	}	
}

●CSVParseLesson.java


import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

public class CSVParseLesson {
	public static void main(String[] args) {
		//今回はcsvデータをパースしてSalesクラスインスタンスを生成し、それをlistに格納する
		List<Sales> list=new ArrayList<>();
		//ファイルをバッファリングして読み込むBufferedReader型の変数を宣言
		BufferedReader br=null;
		try {
			//csvファイルを指定し、fisを生成する
			FileInputStream fis=new FileInputStream("sample.csv");
			//InputStreamReaderでラッピング(csvファイルの文字コードを指定する)
			InputStreamReader isr=new InputStreamReader(fis,"UTF-8");
			//BufferdReaderクラスのインスタンスをnew
			br=new BufferedReader(isr);
			//ファイルを一行一行読み込みながらファイルの終わりまで処理をするときの定型
			String line;
			while((line=br.readLine()) != null){
				//最初の一行は見出しが入っている行なのでスキップする
				if(line.startsWith("年度")){
					continue;
				}
				//文字列.split(",")で文字列をカンマによって分解し、配列を生成できる。
				//2016,210,1014=>data[0]は"2016",data[1]は"210",data[2]は"1014"
				String[] data=line.split(",");
				//インスタンスを生成し、リストに格納する。salesAとsalesBはintなのでintに変換する
				list.add(new Sales(data[0],Integer.parseInt(data[1]),Integer.parseInt(data[2])));	
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(br != null){
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		//csvパースによって作成されたlistを出力する
		for(Sales s:list){
			System.out.println(s);
		}
		
	}

}

4. 実行すると以下のように出力されることを確認する。

2016年度の製品Aの売上高210、製品Bの売上高1014
2017年度の製品Aの売上高220、製品Bの売上高990

ファイルの読み込みとStringクラスに用意されているsplitメソッドを使うと簡単にCSVファイルをパースできることが確認できた。

CSVデータの扱いを理解したところで、以下の問題に挑戦してみよう。

Q1
先ほどの例題において今度は2018〜2025年度のデータをJavaプログラムによって作成し、sample2.csvファイルに書き込め。
各年の製品Aの売り上げは180~250のランダム、製品Bの売り上げは800〜1300のランダムとする。
なお、ファイル文字コードはUTF-8で書き込むこと。

[作成されたsample2.csvファイルの一例]

年度,製品A,製品B
2018,184,894
2019,214,996
2020,221,1296
2021,187,1053
2022,240,821
2023,211,877
2024,222,1038
2025,206,1174

●Sales.javaに追記

class Sales{
	private String year;
	private int salesA;
	private int salesB;
	public Sales(String year,int salesA,int salesB){
		this.year=year;
		this.salesA=salesA;
		this.salesB=salesB;
	}
	@Override
	public String toString() {	
		return String.format("%s年度の製品Aの売上高%d、製品Bの売上高%d", this.year,this.salesA,this.salesB);
	}
public String toCSV(){
return String.format("%s,%d,%d",this.year,this.salesA,this.salesB);	
}

●CSVFileWriteLesson.java


import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class CSVFileWriteLesson {

	public static void main(String[] args) {
		Random r=new Random();
		List<Sales> list=new ArrayList<>();
		for(int i=2018;i<=2025;i++){
			int salesA=r.nextInt(250-180+1)+180;
			int salesB=r.nextInt(1300-800+1)+800;
			list.add(new Sales(String.valueOf(i),salesA,salesB));
		}
		BufferedWriter bw=null;
		try {
			FileOutputStream fos=new FileOutputStream("sample2.csv");
			OutputStreamWriter osw=new OutputStreamWriter(fos,"UTF-8");
			bw=new BufferedWriter(osw);
			bw.append("年度,製品A,製品B");
			bw.newLine();
			for(Sales s:list){
				bw.append(s.toCSV());
				bw.newLine();
			}
			bw.flush();
			System.out.println("書き込み完了");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(bw != null){
				try {
					bw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}