さてCSV。自分で実装するには意外と複雑なのがCSV。
データ内にダブルクォートを入れる場合はダブルクォートでエスケープするとか、ダブルクォートで囲ったら改行コードもデータにできるとか。
そういう意外と面倒なCSV、使いやすいライブラリはございませんでしょうかと、色々使ってみたところkenta.koyamaさんのkotlin-csvが凄く素敵で素晴らしかったというお話。
About
Pure Kotlin CSV Reader/Writer
Pure kotlin! ああ、なんと美しい響きでしょう。
ではbuild.gradleから
build.gradle
implementation 'com.github.doyaaaaaken:kotlin-csv-jvm:1.1.0'
今回のテストコードを動かすための依存関係
build.gradle
testImplementation "org.spekframework.spek2:spek-dsl-jvm:2.0.17"
testRuntimeOnly "org.spekframework.spek2:spek-runner-junit5:2.0.17"
testImplementation 'com.google.truth:truth:1.1.3'
testImplementation 'com.google.truth.extensions:truth-java8-extension:1.1.3'
testImplementation 'com.google.testing.compile:compile-testing:0.19'
実際のテストコード
CsvReadTest.kt
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
import com.google.common.truth.Truth
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
import java.nio.charset.Charset
import java.nio.file.Path
import kotlin.io.path.createTempDirectory
import kotlin.io.path.createTempFile
import kotlin.io.path.writeText
class CsvReadTest: Spek({
describe("kotlin-csvを使ったCSVファイル読み込み") {
// 取り敢えず文字列をパースして表示してみる
context("シンプルに読み込みと表示") {
val data = """
ナリタブライアン,マヤノトップガン,"カンマ,入り","改行
入り"
"キタサンブラック","コントレイル",,"ダブル""クォートいり"
""".trimIndent()
csvReader().readAll(data).forEach {
println(it.joinToString(";"))
}
}
// 文字コード指定でMS932のファイルを読み込んで見る
context("MS932ファイル読み込みテスト") {
temporaryDirectory { dir ->
csvReader {
charset = "MS932" // 文字コード指定を試すため今回は敢えてMS932で読み込み
}.readAll(
createTempFile(dir).apply {
writeText("""
ナリタブライアン,マヤノトップガン,"カンマ,入り","改行
入り"
"キタサンブラック","コントレイル",,"ダブル""クォートいり"
""".trimIndent(), Charset.forName("MS932"))
}.toFile()
).joinToString("[CRLF]") {
it.joinToString(";")
}.let {
// 検証
Truth.assertThat(it).isEqualTo("""
ナリタブライアン;マヤノトップガン;カンマ,入り;改行
入り[CRLF]キタサンブラック;コントレイル;;ダブル"クォートいり
""".trimIndent())
}
}
}
}
}) {
companion object {
// こういうの探せばありそうですけどね, JUnit使ってた時は@TempDirとか使ってましたね
fun temporaryDirectory(closure: (Path)->Unit) {
val dir = createTempDirectory()
runCatching {
closure(dir)
}.onFailure { throw it }
.also { dir.toFile().deleteRecursively() }
}
}
}
色々試した上で、こちらを使わせていただくことになりました。
Pure Kotlin! 世界の全てをkotlinで が合言葉の私の胸に、ズンズンズン、クリティカルヒット(やった!)でございしたとさ。