序
簡単にcsvを読むというだけのことをします。
使用するライブラリ
簡単になので、既存のライブラリを使用します。今回試してみたのは以下の2つです。
使用するデータ
あまりオススメされてないようですが、有名な郵便番号のCSVを使用します。
ビルド用の設定
今回はmavenで用意しました。JREは17想定です。8でもちょっと修正すれば動くかも。
pox.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example-hoge</groupId>
<artifactId>csv-reader-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>csv-reader-test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.9</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.11.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>example_hoge.csv_reader_test.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>example_hoge.csv_reader_test.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
コード
src/main/java/example_hoge/csv_reader_test/App.java
package example_hoge.csv_reader_test;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;
import com.opencsv.bean.CsvBindByPosition;
import com.opencsv.bean.CsvToBeanBuilder;
public class App {
public static class PostalCode {
// 全国地方公共団体コード
@CsvBindByPosition(position = 0)
private String nationalLocalGovernmentCode;
// (旧)郵便番号
@CsvBindByPosition(position = 1)
private String oldPostalCode;
// 郵便番号
@CsvBindByPosition(position = 2)
private String postalCode;
// 都道府県名(全角カタカナ)
@CsvBindByPosition(position = 3)
private String prefectureNameKana;
// 市区町村名(全角カタカナ)
@CsvBindByPosition(position = 4)
private String cityNameKana;
// 町域名(全角カタカナ)
@CsvBindByPosition(position = 5)
private String townAreaNameKana;
// 都道府県名
@CsvBindByPosition(position = 6)
private String prefectureName;
// 市区町村名
@CsvBindByPosition(position = 7)
private String cityName;
// 町域名
@CsvBindByPosition(position = 8)
private String townAreaName;
// 一町域が二以上の郵便番号で表される場合の表示
@CsvBindByPosition(position = 9)
private boolean multiPostalCodesInTownArea;
// 小字毎に番地が起番されている町域の表示
@CsvBindByPosition(position = 10)
private boolean numberedForEachKoaza;
// 丁目を有する町域の場合の表示
@CsvBindByPosition(position = 11)
private boolean townAreaWithChome;
// 一つの郵便番号で二以上の町域を表す場合の表示
@CsvBindByPosition(position = 12)
private boolean multiTownAreasWithPostalCode;
// 更新の表示
@CsvBindByPosition(position = 13)
private boolean updated;
// 変更理由
@CsvBindByPosition(position = 14)
private int reasonForUpdate;
}
public static enum Headers {
// 全国地方公共団体コード
NationalLocalGovernmentCode,
// (旧)郵便番号
OldPostalCode,
// 郵便番号
PostalCode,
// 都道府県名(全角カタカナ)
PrefectureNameKana,
// 市区町村名(全角カタカナ)
CityNameKana,
// 町域名(全角カタカナ)
TownAreaNameKana,
// 都道府県名
PrefectureName,
// 市区町村名
CityName,
// 町域名
TownAreaName,
// 一町域が二以上の郵便番号で表される場合の表示
MultiPostalCodesInTownArea,
// 小字毎に番地が起番されている町域の表示
NumberedForEachKoaza,
// 丁目を有する町域の場合の表示
TownAreaWithChome,
// 一つの郵便番号で二以上の町域を表す場合の表示
MultiTownAreasWithPostalCode,
// 更新の表示
Updated,
// 変更理由
ReasonForUpdate
}
private static void test_commonscsv(Reader in, Writer out) {
try {
Iterable<CSVRecord> records = CSVFormat.RFC4180.builder().setHeader(Headers.class).build().parse(in);
for (CSVRecord record : records) {
out.write(record.get(Headers.PostalCode));
out.write(":");
out.write(record.get(Headers.PrefectureName));
out.write(" ");
out.write(record.get(Headers.CityName));
out.write(" ");
out.write(record.get(Headers.TownAreaName));
out.write("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void test_opencsv(Reader in, Writer out) {
try {
var parser = new CsvToBeanBuilder<PostalCode>(in).withType(PostalCode.class).build();
var postalCodes = parser.parse();
for (var postalcode: postalCodes) {
out.write(postalcode.postalCode);
out.write(':');
out.write(postalcode.prefectureName);
out.write(' ');
out.write(postalcode.cityName);
out.write(' ');
out.write(postalcode.townAreaName);
out.write('\n');
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Reader in = new InputStreamReader(System.in); // wont close for standard input
Writer out = new OutputStreamWriter(System.out); // wont close for standard output
boolean use_commonscsv = true;
if (args.length > 0) {
use_commonscsv = ! "opencsv".equals(args[0]);
}
if (use_commonscsv) {
test_commonscsv(in, out);
} else {
test_opencsv(in, out);
}
try {
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
動かし方
$ mvn package # ビルド
$ java -jar target/csv-reader-test-0.0.1-SNAPSHOT-jar-with-dependencies.jar <utf_ken_all.csv
...
9071544:沖縄県 八重山郡竹富町 鳩間
9071800:沖縄県 八重山郡与那国町 以下に掲載がない場合
9071801:沖縄県 八重山郡与那国町 与那国
$
普通に動かすとcommons csvを使います。opencsvを使う場合は引数にopencsvを入れて下さい。
$ java -jar target/csv-reader-test-0.0.1-SNAPSHOT-jar-with-dependencies.jar opencsv <utf_ken_all.csv
...
9071544:沖縄県 八重山郡竹富町 鳩間
9071800:沖縄県 八重山郡与那国町 以下に掲載がない場合
9071801:沖縄県 八重山郡与那国町 与那国
$
補足
opencsvではbeansを使用する方式にしてるので、割と遅いかもしれません。とはいえ、18MBのテキストを読むのに起動処理込み3.2秒かかっていました。
ヘッダの設定はどちらも本来不要ですが、やはり読みやすさが違うので、今回は定義しています。
まとめ
beanを使った読み込みに興味があり、少し動かしてみただけです。ちゃんと調べてないので結論はなし。