diff --git a/README.md b/README.md index 1098214..d7fe7af 100644 --- a/README.md +++ b/README.md @@ -163,18 +163,19 @@ val tsvReader = csvReader { } ``` -| Option | default value | description | -|--------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| logger | _no-op_ | Logger instance for logging debug information at runtime. | -| charset | `UTF-8` | Charset encoding. The value must be supported by [java.nio.charset.Charset](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html). | -| quoteChar | `"` | Character used to quote fields. | -| delimiter | `,` | Character used as delimiter between each field.
Use `"\t"` if reading TSV file. | -| escapeChar | `"` | Character to escape quote inside field string.
Normally, you don't have to change this option.
See detail comment on [ICsvReaderContext](src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt). | -| skipEmptyLine | `false` | Whether to skip or error out on empty lines. | -| autoRenameDuplicateHeaders | `false` | Whether to auto rename duplicate headers or throw an exception. | -| ~~skipMissMatchedRow~~ | `false` | Deprecated. Replace with appropriate values in `excessFieldsRowBehaviour` and `insufficientFieldsRowBehaviour`, e.g. both set to `IGNORE`. ~~Whether to skip an invalid row. If `ignoreExcessCols` is true, only rows with less than the expected number of columns will be skipped.~~ | -| excessFieldsRowBehaviour | `ERROR` | Behaviour to use when a row has more fields (columns) than expected. `ERROR` (default), `IGNORE` (skip the row) or `TRIM` (remove the excess fields at the end of the row to match the expected number of fields). | -| insufficientFieldsRowBehaviour | `ERROR` | Behaviour to use when a row has fewer fields (columns) than expected. `ERROR` (default), `IGNORE` (skip the row) or `EMPTY_STRING` (replace missing fields with an empty string). | +| Option | default value | description | +|--------------------------------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| logger | _no-op_ | Logger instance for logging debug information at runtime. | +| charset | `UTF-8` | Charset encoding. The value must be supported by [java.nio.charset.Charset](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html). | +| quoteChar | `"` | Character used to quote fields. | +| delimiter | `,` | Character used as delimiter between each field.
Use `"\t"` if reading TSV file. | +| escapeChar | `"` | Character to escape quote inside field string.
Normally, you don't have to change this option.
See detail comment on [ICsvReaderContext](src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt). | +| skipEmptyLine | `false` | Whether to skip or error out on empty lines. | +| autoRenameDuplicateHeaders | `false` | Whether to auto rename duplicate headers or throw an exception. | +| ~~skipMissMatchedRow~~ | `false` | Deprecated. Replace with appropriate values in `excessFieldsRowBehaviour` and `insufficientFieldsRowBehaviour`, e.g. both set to `IGNORE`. ~~Whether to skip an invalid row. If `ignoreExcessCols` is true, only rows with less than the expected number of columns will be skipped.~~ | +| excessFieldsRowBehaviour | `ERROR` | Behaviour to use when a row has more fields (columns) than expected. `ERROR` (default), `IGNORE` (skip the row) or `TRIM` (remove the excess fields at the end of the row to match the expected number of fields). | +| insufficientFieldsRowBehaviour | `ERROR` | Behaviour to use when a row has fewer fields (columns) than expected. `ERROR` (default), `IGNORE` (skip the row) or `EMPTY_STRING` (replace missing fields with an empty string). | +| withFieldAsNull | `NEITHER` | Behaviour to handle two empty separators or quotes as null. `NEITHER` (default, two sequential separators or quotes are handled as empty string), `EMPTY_SEPARATORS` (two sequential separators are null), `EMPTY_QUOTES` (two sequential quotes are null) or `BOTH` (two sequential separators and two sequential quotes are null). | ### CSV Write examples diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileReader.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileReader.kt index 3577ab0..1bc273f 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileReader.kt +++ b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileReader.kt @@ -1,13 +1,15 @@ package com.github.doyaaaaaken.kotlincsv.client +import com.github.doyaaaaaken.kotlincsv.dsl.context.CSVReaderNullFieldIndicator import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext import com.github.doyaaaaaken.kotlincsv.dsl.context.ExcessFieldsRowBehaviour import com.github.doyaaaaaken.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour import com.github.doyaaaaaken.kotlincsv.parser.CsvParser +import com.github.doyaaaaaken.kotlincsv.parser.ParserNullFieldIndicator import com.github.doyaaaaaken.kotlincsv.util.CSVAutoRenameFailedException import com.github.doyaaaaaken.kotlincsv.util.CSVFieldNumDifferentException -import com.github.doyaaaaaken.kotlincsv.util.logger.Logger import com.github.doyaaaaaken.kotlincsv.util.MalformedCSVException +import com.github.doyaaaaaken.kotlincsv.util.logger.Logger /** * CSV Reader class, which controls file I/O flow. @@ -23,7 +25,7 @@ class CsvFileReader internal constructor( private val reader = BufferedLineReader(reader) private var rowNum = 0L - private val parser = CsvParser(ctx.quoteChar, ctx.delimiter, ctx.escapeChar) + private val parser = CsvParser(ctx.quoteChar, ctx.delimiter, ctx.escapeChar, ctx.withFieldAsNull.toParserNullFieldIndicator()) /** * read next csv row @@ -33,14 +35,14 @@ class CsvFileReader internal constructor( * or return null, if all line are already read. */ @Deprecated("We are considering making it a private method. If you have feedback, please comment on Issue #100.") - fun readNext(): List? { + fun readNext(): List? { return readUntilNextCsvRow("") } /** * read all csv rows as Sequence */ - fun readAllAsSequence(fieldsNum: Int? = null): Sequence> { + fun readAllAsSequence(fieldsNum: Int? = null): Sequence> { var expectedNumFieldsInRow: Int? = fieldsNum return generateSequence { @Suppress("DEPRECATION") readNext() @@ -76,7 +78,7 @@ class CsvFileReader internal constructor( private fun skipMismatchedRow( idx: Int, - row: List, + row: List, numFieldsInRow: Int ): Nothing? { logger.info("skip miss matched row. [csv row num = ${idx + 1}, fields num = ${row.size}, fields num of first row = $numFieldsInRow]") @@ -86,9 +88,9 @@ class CsvFileReader internal constructor( /** * read all csv rows as Sequence with header information */ - fun readAllWithHeaderAsSequence(): Sequence> { + fun readAllWithHeaderAsSequence(): Sequence> { @Suppress("DEPRECATION") - var headers = readNext() ?: return emptySequence() + var headers = readNext()?.map { it ?: "" } ?: return emptySequence() if (ctx.autoRenameDuplicateHeaders) { headers = deduplicateHeaders(headers) } else { @@ -108,7 +110,7 @@ class CsvFileReader internal constructor( * @return return fields in row as List. * or return null, if all line are already read. */ - private tailrec fun readUntilNextCsvRow(leftOver: String = ""): List? { + private tailrec fun readUntilNextCsvRow(leftOver: String = ""): List? { val nextLine = reader.readLineWithTerminator() rowNum++ return if (nextLine == null) { @@ -160,4 +162,11 @@ class CsvFileReader internal constructor( if (results.size != results.distinct().size) throw CSVAutoRenameFailedException() } } + + private fun CSVReaderNullFieldIndicator.toParserNullFieldIndicator() = when(this) { + CSVReaderNullFieldIndicator.EMPTY_SEPARATORS -> ParserNullFieldIndicator.EMPTY_SEPARATORS + CSVReaderNullFieldIndicator.EMPTY_QUOTES -> ParserNullFieldIndicator.EMPTY_QUOTES + CSVReaderNullFieldIndicator.BOTH -> ParserNullFieldIndicator.BOTH + CSVReaderNullFieldIndicator.NEITHER -> ParserNullFieldIndicator.NEITHER + } } diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt index 5e31eb5..6a6e85c 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt +++ b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt @@ -11,12 +11,12 @@ expect class CsvReader( ctx: CsvReaderContext = CsvReaderContext() ) { /** - * read csv data as String, and convert into List> + * read csv data as String, and convert into List> */ - fun readAll(data: String): List> + fun readAll(data: String): List> /** - * read csv data with header, and convert into List> + * read csv data with header, and convert into List> */ - fun readAllWithHeader(data: String): List> + fun readAllWithHeader(data: String): List> } diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt index ca520a0..9e2005f 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt +++ b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt @@ -88,6 +88,11 @@ interface ICsvReaderContext { * If a row exceeds have the expected number of fields (columns), how, and if, the reader should proceed */ val excessFieldsRowBehaviour: ExcessFieldsRowBehaviour + + /** + * Configures which field values should be handled as null value by the reader. + */ + val withFieldAsNull: CSVReaderNullFieldIndicator } enum class InsufficientFieldsRowBehaviour { @@ -125,6 +130,29 @@ enum class ExcessFieldsRowBehaviour { TRIM } +enum class CSVReaderNullFieldIndicator { + + /** + * Two sequential separators are null. + */ + EMPTY_SEPARATORS, + + /** + * Two sequential quotes are null. + */ + EMPTY_QUOTES, + + /** + * Two sequential separators and two sequential quotes are null. + */ + BOTH, + + /** + * Default. Both are considered empty string. + */ + NEITHER +} + /** * CSV Reader settings used in `csvReader` DSL method. * @@ -142,4 +170,5 @@ class CsvReaderContext : ICsvReaderContext { override var autoRenameDuplicateHeaders: Boolean = false override var insufficientFieldsRowBehaviour: InsufficientFieldsRowBehaviour = InsufficientFieldsRowBehaviour.ERROR override var excessFieldsRowBehaviour: ExcessFieldsRowBehaviour = ExcessFieldsRowBehaviour.ERROR + override var withFieldAsNull: CSVReaderNullFieldIndicator = CSVReaderNullFieldIndicator.NEITHER } diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParser.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParser.kt index 47cfe65..29d1635 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParser.kt +++ b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParser.kt @@ -8,7 +8,8 @@ package com.github.doyaaaaaken.kotlincsv.parser internal class CsvParser( private val quoteChar: Char, private val delimiter: Char, - private val escapeChar: Char + private val escapeChar: Char, + private val withFieldAsNull: ParserNullFieldIndicator ) { /** @@ -18,8 +19,8 @@ internal class CsvParser( * @return return parsed row fields * return null, if passed line string is on the way of csv row. */ - fun parseRow(line: String, rowNum: Long = 1): List? { - val stateMachine = ParseStateMachine(quoteChar, delimiter, escapeChar) + fun parseRow(line: String, rowNum: Long = 1): List? { + val stateMachine = ParseStateMachine(quoteChar, delimiter, escapeChar, withFieldAsNull) var lastCh: Char? = line.firstOrNull() var skipCount = 0L line.zipWithNext { ch, nextCh -> diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/ParseStateMachine.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/ParseStateMachine.kt index 7dd2989..9805aba 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/ParseStateMachine.kt +++ b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/ParseStateMachine.kt @@ -9,15 +9,18 @@ import com.github.doyaaaaaken.kotlincsv.util.Const internal class ParseStateMachine( private val quoteChar: Char, private val delimiter: Char, - private val escapeChar: Char + private val escapeChar: Char, + private val withFieldAsNull: ParserNullFieldIndicator ) { private var state = ParseState.START - private val fields = ArrayList() + private val fields = ArrayList() private var field = StringBuilder() + private var handleFieldAsNull = false + private var pos = 0L /** @@ -33,6 +36,7 @@ internal class ParseStateMachine( Const.BOM -> Unit quoteChar -> state = ParseState.QUOTE_START delimiter -> { + handleEmptySeparators() flushField() state = ParseState.DELIMITER } @@ -89,6 +93,7 @@ internal class ParseStateMachine( when (ch) { quoteChar -> state = ParseState.QUOTE_START delimiter -> { + handleEmptySeparators() flushField() state = ParseState.DELIMITER } @@ -126,6 +131,7 @@ internal class ParseStateMachine( state = ParseState.QUOTED_FIELD pos += 1 } else { + handleEmptyQuotes() state = ParseState.QUOTE_END } } else { @@ -167,10 +173,15 @@ internal class ParseStateMachine( * @return return parsed CSV Fields. * return null, if current position is on the way of csv row. */ - fun getResult(): List? { + fun getResult(): List? { return when (state) { ParseState.DELIMITER -> { - fields.add("") + val value = when(withFieldAsNull) { + ParserNullFieldIndicator.EMPTY_SEPARATORS -> null + ParserNullFieldIndicator.BOTH -> null + else -> "" + } + fields.add(value) fields.toList() } ParseState.QUOTED_FIELD -> null @@ -183,8 +194,27 @@ internal class ParseStateMachine( } private fun flushField() { - fields.add(field.toString()) + val value = if (handleFieldAsNull) null else field.toString() + + fields.add(value) field.clear() + handleFieldAsNull = false + } + + private fun handleEmptySeparators() { + handleFieldAsNull = when(withFieldAsNull) { + ParserNullFieldIndicator.EMPTY_SEPARATORS -> true + ParserNullFieldIndicator.BOTH -> true + else -> false + } + } + + private fun handleEmptyQuotes() { + handleFieldAsNull = when(withFieldAsNull) { + ParserNullFieldIndicator.EMPTY_QUOTES -> field.isEmpty() + ParserNullFieldIndicator.BOTH -> field.isEmpty() + else -> false + } } } @@ -197,3 +227,10 @@ private enum class ParseState { QUOTE_END, QUOTED_FIELD } + +internal enum class ParserNullFieldIndicator { + EMPTY_SEPARATORS, + EMPTY_QUOTES, + BOTH, + NEITHER +} diff --git a/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt b/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt index 7792b44..46154a0 100644 --- a/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt +++ b/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt @@ -13,16 +13,16 @@ actual class CsvReader actual constructor( ) : ICsvReaderContext by ctx { /** - * read csv data as String, and convert into List> + * read csv data as String, and convert into List> */ - actual fun readAll(data: String): List> { + actual fun readAll(data: String): List> { return CsvFileReader(ctx, StringReaderImpl(data), logger).readAllAsSequence().toList() } /** - * read csv data with header, and convert into List> + * read csv data with header, and convert into List> */ - actual fun readAllWithHeader(data: String): List> { + actual fun readAllWithHeader(data: String): List> { return CsvFileReader(ctx, StringReaderImpl(data), logger).readAllWithHeaderAsSequence().toList() } } diff --git a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt b/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt index a02256e..d78a9a1 100644 --- a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt +++ b/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt @@ -18,61 +18,61 @@ actual class CsvReader actual constructor( private val charsetCode = Charset.forName(charset) /** - * read csv data as String, and convert into List> + * read csv data as String, and convert into List> * * No need to close InputStream when calling this method. */ - actual fun readAll(data: String): List> { + actual fun readAll(data: String): List> { val br = data.byteInputStream(charsetCode).bufferedReader(charsetCode) return open(br) { readAllAsSequence().toList() } } /** - * read csv data as File, and convert into List> + * read csv data as File, and convert into List> * * No need to close InputStream when calling this method. */ - fun readAll(file: File): List> { + fun readAll(file: File): List> { val br = file.inputStream().bufferedReader(charsetCode) return open(br) { readAllAsSequence().toList() } } /** - * read csv data as InputStream, and convert into List> + * read csv data as InputStream, and convert into List> * * No need to close InputStream when calling this method. */ - fun readAll(ips: InputStream): List> { + fun readAll(ips: InputStream): List> { val br = ips.bufferedReader(charsetCode) return open(br) { readAllAsSequence().toList() } } /** - * read csv data with header, and convert into List> + * read csv data with header, and convert into List> * * No need to close InputStream when calling this method. */ - actual fun readAllWithHeader(data: String): List> { + actual fun readAllWithHeader(data: String): List> { val br = data.byteInputStream(charsetCode).bufferedReader(charsetCode) return open(br) { readAllWithHeaderAsSequence().toList() } } /** - * read csv data with header, and convert into List> + * read csv data with header, and convert into List> * * No need to close InputStream when calling this method. */ - fun readAllWithHeader(file: File): List> { + fun readAllWithHeader(file: File): List> { val br = file.inputStream().bufferedReader(charsetCode) return open(br) { readAllWithHeaderAsSequence().toList() } } /** - * read csv data with header, and convert into List> + * read csv data with header, and convert into List> * * No need to close InputStream when calling this method. */ - fun readAllWithHeader(ips: InputStream): List> { + fun readAllWithHeader(ips: InputStream): List> { val br = ips.bufferedReader(charsetCode) return open(br) { readAllWithHeaderAsSequence().toList() } } diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReaderTest.kt b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReaderTest.kt index df92f47..47c522c 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReaderTest.kt +++ b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReaderTest.kt @@ -1,5 +1,6 @@ package com.github.doyaaaaaken.kotlincsv.client +import com.github.doyaaaaaken.kotlincsv.dsl.context.CSVReaderNullFieldIndicator import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext import com.github.doyaaaaaken.kotlincsv.dsl.context.ExcessFieldsRowBehaviour import com.github.doyaaaaaken.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour @@ -21,6 +22,7 @@ class CsvReaderTest : WordSpec({ "be created with no argument" { val reader = CsvReader() reader.charset shouldBe Const.defaultCharset + reader.withFieldAsNull shouldBe CSVReaderNullFieldIndicator.NEITHER } "be created with CsvReaderContext argument" { val context = CsvReaderContext().apply { @@ -29,6 +31,7 @@ class CsvReaderTest : WordSpec({ delimiter = '\t' escapeChar = '"' skipEmptyLine = true + withFieldAsNull = CSVReaderNullFieldIndicator.EMPTY_SEPARATORS } val reader = CsvReader(context) assertSoftly { @@ -37,6 +40,7 @@ class CsvReaderTest : WordSpec({ reader.delimiter shouldBe '\t' reader.escapeChar shouldBe '"' reader.skipEmptyLine shouldBe true + reader.withFieldAsNull shouldBe CSVReaderNullFieldIndicator.EMPTY_SEPARATORS } } } @@ -312,6 +316,39 @@ class CsvReaderTest : WordSpec({ expected shouldBe actual } } + "should handle two sequential separators as null with EMPTY_SEPARATORS null field indicator" { + val result = csvReader{ + withFieldAsNull = CSVReaderNullFieldIndicator.EMPTY_SEPARATORS + }.readAll( + """,,"a","",""" + ) + result shouldBe listOf(listOf(null, null, "a", "", null)) + } + "should handle two sequential quotes as null with EMPTY_QUOTES null field indicator" { + val result = csvReader{ + withFieldAsNull = CSVReaderNullFieldIndicator.EMPTY_QUOTES + }.readAll( + """,,"a","",""" + ) + result shouldBe listOf(listOf("", "", "a", null, "")) + } + "should handle two sequential separators and quotes as null with BOTH null field indicator" { + val result = csvReader{ + withFieldAsNull = CSVReaderNullFieldIndicator.BOTH + }.readAll( + """,,"a","",""" + ) + result shouldBe listOf(listOf(null, null, "a", null, null)) + } + "should handle two sequential separators and quotes not as null with NEITHER null field indicator" { + val result = csvReader{ + withFieldAsNull = CSVReaderNullFieldIndicator.NEITHER + }.readAll( + """,,"a","",""" + ) + result shouldBe listOf(listOf("", "", "a", "", "")) + } + } "readAllWithHeader method" should { @@ -380,6 +417,42 @@ class CsvReaderTest : WordSpec({ } exception.fieldNum shouldBe 3 } + "should handle two sequential separators in header as empty string" { + + val indicators = listOf( + CSVReaderNullFieldIndicator.EMPTY_SEPARATORS, + CSVReaderNullFieldIndicator.BOTH + ) + + indicators.forEach { indicator -> + val result = csvReader{ + withFieldAsNull = indicator + }.readAllWithHeader( + """a,,c + |a,,c + """.trimMargin() + ) + result shouldBe listOf(mapOf("a" to "a", "" to null, "c" to "c")) + } + } + "should handle two sequential quotes in header as empty string" { + + val indicators = listOf( + CSVReaderNullFieldIndicator.EMPTY_QUOTES, + CSVReaderNullFieldIndicator.BOTH + ) + + indicators.forEach { indicator -> + val result = csvReader{ + withFieldAsNull = indicator + }.readAllWithHeader( + """a,"",c + |a,"",c + """.trimMargin() + ) + result shouldBe listOf(mapOf("a" to "a", "" to null, "c" to "c")) + } + } } "open method (with fileName argument)" should { @@ -429,7 +502,7 @@ class CsvReaderTest : WordSpec({ } "validate test as flow" { val fileStream = readTestDataFile("simple.csv").inputStream() - val rows = mutableListOf>() + val rows = mutableListOf>() csvReader().openAsync(fileStream) { readAllAsSequence().asFlow().collect { rows.add(it) diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/StringReaderTest.kt b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/StringReaderTest.kt index 93f55da..305fe46 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/StringReaderTest.kt +++ b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/StringReaderTest.kt @@ -147,15 +147,15 @@ private fun readTestDataFile(fileName: String): String { } /** - * read csv data as String, and convert into List> + * read csv data as String, and convert into List> */ -private fun readAll(data: String): List> { +private fun readAll(data: String): List> { return CsvFileReader(CsvReaderContext(), StringReaderImpl(data), LoggerNop).readAllAsSequence().toList() } /** - * read csv data with header, and convert into List> + * read csv data with header, and convert into List> */ -private fun readAllWithHeader(data: String): List> { +private fun readAllWithHeader(data: String): List> { return CsvFileReader(CsvReaderContext(), StringReaderImpl(data), LoggerNop).readAllWithHeaderAsSequence().toList() } diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParserTest.kt b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParserTest.kt index 4ea8f0e..2c07fe2 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParserTest.kt +++ b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParserTest.kt @@ -7,7 +7,7 @@ import io.kotest.core.spec.style.WordSpec import io.kotest.matchers.shouldBe class CsvParserTest : WordSpec({ - val parser = CsvParser('"', ',', '"') + val parser = CsvParser('"', ',', '"', ParserNullFieldIndicator.NEITHER) val lineTerminators = listOf("\n", "\u2028", "\u2029", "\u0085", "\r", "\r\n") "CsvParser.parseRow" should {