2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

OpenCSVの使用方法について

Posted at

初めに

本記事は、JavaのOpenCSVの使用方法について記載しています。Gradleを使用したJavaの開発環境があれば問題ないと思います。わからない方は以下の参考にしてください。

OpenCSVを導入

以下からライブラリを追加してください。

以下のようにbuild.gradleを記述してください。

build.gradle
plugins {
    id 'application'
    // 外部jarを読み込めるようにする
    id 'com.github.johnrengelman.shadow' version '7.1.2'
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'junit:junit:4.13.2'

    implementation 'com.google.guava:guava:30.1.1-jre'
    // 追加 opencsv
    implementation 'com.opencsv:opencsv:5.6'
    // 追加 lombok
    implementation 'org.projectlombok:lombok:1.18.24'
}

application {
    mainClass = 'app.App'
}

// jar追加
jar {
    manifest {
        attributes 'Main-Class': 'app.App'
    }
}

OpenCSVの使用方法

OpenCSVの使用方法について記載します。本記事では、単純なCSVファイルの出力方法とCSVファイルの読み込み方法、JavaBeansを使用したCSVファイルの出力方法とCSVファイルの読み込みについて記述します。

単純なCSVファイルの出力方法

List形式のデータをCSV出力する方法です。配列が1行のデータ(カンマ区切りの数)を指し、リストが行数を指します。

Pattern1WriteCSVUtil.java
package app.pattern1.util;

import java.io.File;
import java.io.FileWriter;
import java.util.List;

import com.opencsv.CSVWriter;

public class Pattern1WriteCSVUtil {
    /**
     * CSVファイルを出力する
     * @param path CSVファイルのパス
     * @param dataList 書き込むデータリスト
     * @throws Exception
     */
    public static void writeCSV(String path, List<String[]> dataList) throws Exception{
        File file = new File(path);
        try (CSVWriter csvWriter = new CSVWriter(new FileWriter(file))) {
            csvWriter.writeAll(dataList);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        }
    }
}

単純なCSVファイルの読み込み方法

CSVデータをList形式で読み込む方法です。配列が1行のデータ(カンマ区切りの数)を指し、リストが行数を指します。

Pattern1ReadCSVUtil.java
package app.pattern1.util;

import java.io.File;
import java.io.FileReader;
import java.util.List;

import com.opencsv.CSVReader;

public class Pattern1ReadCSVUtil {
    /**
     * CSVファイルを読み込む
     * @param path CSVファイルのパス
     * @return 読み込んだCSVデータ
     * @throws Exception
     */
    public static List<String[]> readCSV(String path) throws Exception{
        File file = new File(path);
        try (CSVReader csvReader = new CSVReader(new FileReader(file))) {            
            return csvReader.readAll();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        }
    }   
}

JavaBeansを使用したCSVファイルの出力方法

JavaBeansを使用したCSVファイルの出力方法です。例えば、「ID、名前、年齢、所属」の情報をもつ「 UserInfoBean.java 」というJavaBeansのクラスがあるとします。この時、 単純なCSVファイルの読み込み方法の方法だと毎回、データをString型に変換しないといけないので面倒ですね。「 UserInfoBean.java 」のまま出力させられたら便利ですね。今回はその方法についてです。

JavaBeansの修正

以下のような「 UserInfoBean.java 」を想定します。

UserInfoBean.java(変更前)
package app.pattern2.bean;

import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;

import org.apache.commons.lang3.builder.ToStringBuilder;

import lombok.Data;

@Data
public class UserInfoBean {
    private int id;

    private String name;

    private int age;

    private String belong;

    public UserInfoBean(int id, String name, int age, String belong){
        this.id = id;
        this.name = name;
        this.age = age;
        this.belong = belong;
    }
    
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

これを以下のように変更してください。

UserInfoBean.java(変更後)
package app.pattern2.bean;

import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;

import org.apache.commons.lang3.builder.ToStringBuilder;

import lombok.Data;

@Data
public class UserInfoBean {
    @CsvBindByPosition(position = 0)
    @CsvBindByName(column = "ID", required = true)
    private int id;

    @CsvBindByPosition(position = 1)
    @CsvBindByName(column = "名前", required = true)
    private String name;

    @CsvBindByPosition(position = 2)
    @CsvBindByName(column = "年齢", required = true)
    private int age;

    @CsvBindByPosition(position = 3)
    @CsvBindByName(column = "所属", required = true)
    private String belong;

    public UserInfoBean(int id, String name, int age, String belong){
        this.id = id;
        this.name = name;
        this.age = age;
        this.belong = belong;
    }
    
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

2つの@(アノテーション)を追加しました。それぞれ、「@CsvBindByPosition」はCSVファイルを出力する際のヘッダーの順番を指定します。次に、「@CsvBindByName」はヘッダーの名前と必須チェックの有無を指定しています。

JavaBeans形式でCSV出力する方法

以下の公式サイトを以下のように実装しました。

公式サイトのBeanを読み込む方法
@Test 
public void test() throws Exception{
    // csv読み込み(Bean)
    String path = "./csv/result.csv";
    UserInfoBean beans = new UserInfoBean(1,"Tanaka",20,"チームB");
    Writer writer = new FileWriter(path);
    StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).build();
    beanToCsv.write(beans);
    writer.close();       
}  

すると以下のような結果になってしいます。ヘッダー名が設定されていませんね。
上記の実装だと2つのアノテーションのうちどちらか片方しか、機能しません。

# CSVの結果
"1","Tanaka","20","チームB"

2つのアノテーションの両方が機能するようにします。

まずは、「ColumnPositionMappingStrategy」を継承した「CustomMappingStrategy」を作成します。パッケージ以外はコピペでOKです。よくわからないかと思いますが、「extractHeaderName」や「columnHeaderName」とあるのでなんとなくJavaBeansに設定した「@CsvBindByName」から良い感じにヘッダーの名前を取得しようとしていると思ってください。

CustomMappingStrategy.java
package app.pattern2.util;

import com.opencsv.bean.BeanField;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvBindByName;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;

import org.apache.commons.lang3.StringUtils;

public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T>{
    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
        final int numColumns = getFieldMap().values().size();
        super.generateHeader(bean);

        String[] header = new String[numColumns];

        BeanField beanField;
        for (int i = 0; i < numColumns; i++) {
            beanField = findField(i);
            String columnHeaderName = extractHeaderName(beanField);
            header[i] = columnHeaderName;
        }
        return header;
    }

    private String extractHeaderName(final BeanField beanField) {
        if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(
                CsvBindByName.class).length == 0) {
            return StringUtils.EMPTY;
        }

        final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
        return bindByNameAnnotation.column();
    }
}

任意のJavaBeansを読み込めるCSV出力機能の実装

Pattern2WriteCSVUtil.java
package app.pattern2.util;

import java.io.File;
import java.io.FileWriter;
import java.util.List;

import com.opencsv.CSVWriter;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;

public class Pattern2WriteCSVUtil<T>{
    /**
     * CSVファイルを出力する
     * @param beanList 書き込むデータリスト
     * @param bean JavaBeansのクラス
     * @param path CSVファイルのパス
     * @throws Exception
     */
    public void writeCSV(List<T> beanList, Class<T> bean, String path) throws Exception {
        File file = new File(path); 
        try (CSVWriter csvWriter = new CSVWriter(new FileWriter(file))) {
            CustomMappingStrategy<T> mappingStrategy = new CustomMappingStrategy<>();
            mappingStrategy.setType(bean);
            StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(csvWriter)
            .withSeparator(CSVWriter.DEFAULT_SEPARATOR)        
            .withMappingStrategy(mappingStrategy)
            .build();
            beanToCsv.write(beanList);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        }     
    }
}

使い方は以下のように行います。

Pattern2WriteCSVUtil<使用したいJavaBeans> 任意の変数名 = new Pattern2WriteCSVUtil<使用したいJavaBeans>();
任意の変数名.writeCSV();

実際に以下のように呼び出してみます。

@Test 
public void test() throws Exception{
   String path = "./csv/result.csv";
   List<UserInfoBean> beanList = new ArrayList<>();
   UserInfoBean bean = new UserInfoBean(1,"Tanaka",20,"チームB");
   beanList.add(bean);
   Pattern2WriteCSVUtil<UserInfoBean> util  = new Pattern2WriteCSVUtil<UserInfoBean>();  
   util.writeCSV(beanList, UserInfoBean.class, path); 
}

上記の出力結果です。header名も設定されていますね。

# CSVの結果
"ID","名前","年齢","所属"
"1","Tanaka","20","チームB"

CSVの出力はこれで終わりです。

JavaBeans形式でCSV読み込みする方法

CSV読み込みは書き込みほど面倒ではありません。ほぼ公式サイトと同じ実装です。

Pattern2ReadCSVUtil.java
package app.pattern2.util;

import java.io.File;
import java.io.FileReader;
import java.util.List;

import com.opencsv.CSVReader;
import com.opencsv.bean.CsvToBeanBuilder;

public class Pattern2ReadCSVUtil<T> {
    public  List<T> readCSV(Class<T> bean, String path) throws Exception{
        File file = new File(path);
        try (CSVReader csvReader = new CSVReader(new FileReader(file))) {
            CsvToBeanBuilder<T> builder = new CsvToBeanBuilder<T>(csvReader);
            builder.withType(bean);                      
            return builder.build().parse();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        }
    }
}

ここで注意して欲しいことがあります。「UserInfoBean」は idageint型 になっています。しかし、CSVファイルから読み込む際はString型の扱いになるので「UserInfoBean」で読み込むとエラーになります。
よって以下のように新たに「UserInfoBean2」を作成します。

UserInfoBean2.java
package app.pattern2.bean;

import com.opencsv.bean.CsvBindByName;
import com.opencsv.bean.CsvBindByPosition;

import org.apache.commons.lang3.builder.ToStringBuilder;

import lombok.Data;

@Data
public class UserInfoBean2 {
    @CsvBindByPosition(position = 0)
    @CsvBindByName(column = "ID", required = true)
    private String id;

    @CsvBindByPosition(position = 1)
    @CsvBindByName(column = "名前", required = true)
    private String name;

    @CsvBindByPosition(position = 2)
    @CsvBindByName(column = "年齢", required = true)
    private String age;

    @CsvBindByPosition(position = 3)
    @CsvBindByName(column = "所属", required = true)
    private String belong;

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

先ほど出力したCSVを読み込んでみましょう。

@Test 
public void test6() throws Exception{
   String path = "./csv/result.csv";           
   Pattern2ReadCSVUtil<UserInfoBean2> csvUtil = new Pattern2ReadCSVUtil<UserInfoBean2>();
   List<UserInfoBean2> dataList = csvUtil.readCSV(UserInfoBean2.class, path);
   for(UserInfoBean2 info : dataList){
       System.out.println(info);
   }      
}

上記の実行結果です。ちゃんと変数と値が紐づいてますね。

# 実行結果
app.pattern2.bean.UserInfoBean2@6d7b4f4c[age=年齢,belong=所属,id=ID,name=名前]
app.pattern2.bean.UserInfoBean2@eec5a4a[age=20,belong=チームB,id=1,name=Tanaka]

最後に

ここまでご覧いただきありがとうございます。以下は投稿者が作成した今回のOpenCSVの記事で使用した4パターンの読み書きの機能を実装したJavaプロジェクトです。記事では省いている部分もありますのでご参考までに・・・。

参考

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?