Merge pull request #11 from yaslab/feature/new-row-struct

Enumerate the Row struct instead of a [String]
This commit is contained in:
Yasuhiro Hatta 2016-11-04 19:19:07 +09:00 committed by GitHub
commit f93b83c26b
16 changed files with 697 additions and 392 deletions

4
.swiftlint.yml Normal file
View File

@ -0,0 +1,4 @@
disabled_rules:
- force_cast
- force_try
- variable_name

View File

@ -17,6 +17,14 @@
0E3CE3701DB529D700FA45CF /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */; }; 0E3CE3701DB529D700FA45CF /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */; };
0E3CE3711DB529D700FA45CF /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */; }; 0E3CE3711DB529D700FA45CF /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */; };
0E3CE3721DB529D700FA45CF /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */; }; 0E3CE3721DB529D700FA45CF /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */; };
0E47EEB91DBB268000EBF783 /* CSVConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEB81DBB268000EBF783 /* CSVConfiguration.swift */; };
0E47EEBA1DBB268000EBF783 /* CSVConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEB81DBB268000EBF783 /* CSVConfiguration.swift */; };
0E47EEBB1DBB268000EBF783 /* CSVConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEB81DBB268000EBF783 /* CSVConfiguration.swift */; };
0E47EEBC1DBB268000EBF783 /* CSVConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEB81DBB268000EBF783 /* CSVConfiguration.swift */; };
0E47EEC11DBCDB1800EBF783 /* CSV+iterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEC01DBCDB1800EBF783 /* CSV+iterator.swift */; };
0E47EEC21DBCDB1800EBF783 /* CSV+iterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEC01DBCDB1800EBF783 /* CSV+iterator.swift */; };
0E47EEC31DBCDB1800EBF783 /* CSV+iterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEC01DBCDB1800EBF783 /* CSV+iterator.swift */; };
0E47EEC41DBCDB1800EBF783 /* CSV+iterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E47EEC01DBCDB1800EBF783 /* CSV+iterator.swift */; };
0E7E8C8C1D0BC7BB0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */; }; 0E7E8C8C1D0BC7BB0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */; };
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; }; 0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; }; 0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; };
@ -42,10 +50,6 @@
0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; }; 0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; };
0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; }; 0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; };
0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; }; 0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; };
0E9317D91D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
0E9317DA1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
0E9317DB1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
0E9317DC1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
0E9317DE1D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; }; 0E9317DE1D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; };
0E9317DF1D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; }; 0E9317DF1D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; };
0E9317E01D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; }; 0E9317E01D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; };
@ -87,6 +91,8 @@
0E0F160D1D197DB800C92580 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endian.swift; sourceTree = "<group>"; }; 0E0F160D1D197DB800C92580 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endian.swift; sourceTree = "<group>"; };
0E3CE36B1DB5281E00FA45CF /* UnicodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeTests.swift; sourceTree = "<group>"; }; 0E3CE36B1DB5281E00FA45CF /* UnicodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeTests.swift; sourceTree = "<group>"; };
0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrimFieldsTests.swift; sourceTree = "<group>"; }; 0E3CE36F1DB529D700FA45CF /* TrimFieldsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrimFieldsTests.swift; sourceTree = "<group>"; };
0E47EEB81DBB268000EBF783 /* CSVConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVConfiguration.swift; sourceTree = SOURCE_ROOT; };
0E47EEC01DBCDB1800EBF783 /* CSV+iterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+iterator.swift"; sourceTree = "<group>"; };
0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = "<group>"; }; 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = "<group>"; };
@ -102,7 +108,6 @@
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+init.swift"; sourceTree = "<group>"; }; 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+init.swift"; sourceTree = "<group>"; };
0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+subscript.swift"; sourceTree = "<group>"; };
0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = "<group>"; }; 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = "<group>"; };
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = "<group>"; }; 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = "<group>"; };
0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeIterator.swift; sourceTree = "<group>"; }; 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeIterator.swift; sourceTree = "<group>"; };
@ -194,7 +199,8 @@
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */, 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */,
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */, 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */,
0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */, 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */,
0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */, 0E47EEC01DBCDB1800EBF783 /* CSV+iterator.swift */,
0E47EEB81DBB268000EBF783 /* CSVConfiguration.swift */,
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */, 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */,
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */, 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */,
0E0F160D1D197DB800C92580 /* Endian.swift */, 0E0F160D1D197DB800C92580 /* Endian.swift */,
@ -321,6 +327,7 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */; buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */;
buildPhases = ( buildPhases = (
0E47EEBF1DBBC05700EBF783 /* Run Script () */,
0E7E8CC11D0BCA2A0057A1C1 /* Sources */, 0E7E8CC11D0BCA2A0057A1C1 /* Sources */,
0E7E8CC21D0BCA2A0057A1C1 /* Frameworks */, 0E7E8CC21D0BCA2A0057A1C1 /* Frameworks */,
0E7E8CC31D0BCA2A0057A1C1 /* Headers */, 0E7E8CC31D0BCA2A0057A1C1 /* Headers */,
@ -504,6 +511,23 @@
}; };
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0E47EEBF1DBBC05700EBF783 /* Run Script () */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script ()";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint autocorrect\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
0E7E8C7C1D0BC7BB0057A1C1 /* Sources */ = { 0E7E8C7C1D0BC7BB0057A1C1 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
@ -511,9 +535,10 @@
files = ( files = (
0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0EA2AB821D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0EA2AB821D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317DA1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E47EEC21DBCDB1800EBF783 /* CSV+iterator.swift in Sources */,
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */, 0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */,
0E0F160F1D197DB800C92580 /* Endian.swift in Sources */, 0E0F160F1D197DB800C92580 /* Endian.swift in Sources */,
0E47EEBA1DBB268000EBF783 /* CSVConfiguration.swift in Sources */,
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */, 0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */,
0EA2AB7D1D183B45003EC967 /* BinaryReader.swift in Sources */, 0EA2AB7D1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );
@ -537,9 +562,10 @@
files = ( files = (
0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317DC1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E47EEC41DBCDB1800EBF783 /* CSV+iterator.swift in Sources */,
0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */, 0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */,
0E0F16111D197DB800C92580 /* Endian.swift in Sources */, 0E0F16111D197DB800C92580 /* Endian.swift in Sources */,
0E47EEBC1DBB268000EBF783 /* CSVConfiguration.swift in Sources */,
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */, 0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */,
0EA2AB7F1D183B45003EC967 /* BinaryReader.swift in Sources */, 0EA2AB7F1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );
@ -551,9 +577,10 @@
files = ( files = (
0E9317D41D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D41D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0EA2AB811D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0EA2AB811D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317D91D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E47EEC11DBCDB1800EBF783 /* CSV+iterator.swift in Sources */,
0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */, 0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */,
0E0F160E1D197DB800C92580 /* Endian.swift in Sources */, 0E0F160E1D197DB800C92580 /* Endian.swift in Sources */,
0E47EEB91DBB268000EBF783 /* CSVConfiguration.swift in Sources */,
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */, 0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */,
0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */, 0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );
@ -577,9 +604,10 @@
files = ( files = (
0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317DB1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E47EEC31DBCDB1800EBF783 /* CSV+iterator.swift in Sources */,
0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */, 0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */,
0E0F16101D197DB800C92580 /* Endian.swift in Sources */, 0E0F16101D197DB800C92580 /* Endian.swift in Sources */,
0E47EEBB1DBB268000EBF783 /* CSVConfiguration.swift in Sources */,
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */, 0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */,
0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */, 0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );

44
CSVConfiguration.swift Normal file
View File

@ -0,0 +1,44 @@
//
// CSVConfiguration.swift
// CSV
//
// Created by Yasuhiro Hatta on 2016/10/22.
// Copyright © 2016 yaslab. All rights reserved.
//
import Foundation
internal let defaultHasHeaderRow = false
internal let defaultTrimFields = false
internal let defaultDelimiter = UnicodeScalar(UInt8(0x2c)) // ","
internal let defaultWhitespaces = CharacterSet.whitespaces
/// No overview available.
public struct CSVConfiguration {
/// `true` if the CSV has a header row, otherwise `false`. Default: `false`.
public let hasHeaderRow: Bool
/// No overview available.
public let trimFields: Bool
/// Default: `","`.
public let delimiter: UnicodeScalar
/// No overview available.
public let whitespaces: CharacterSet
/// No overview available.
public init(
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter,
whitespaces: CharacterSet = defaultWhitespaces) {
self.hasHeaderRow = hasHeaderRow
self.trimFields = trimFields
self.delimiter = delimiter
var whitespaces = whitespaces
_ = whitespaces.remove(delimiter)
self.whitespaces = whitespaces
}
}

View File

@ -50,8 +50,12 @@ internal class BinaryReader {
private var tempBuffer = [UInt8](repeating: 0, count: 4) private var tempBuffer = [UInt8](repeating: 0, count: 4)
private let tempBufferSize = 4 private let tempBufferSize = 4
private var tempBufferOffset = 0 private var tempBufferOffset = 0
internal init(stream: InputStream, endian: Endian = .unknown, closeOnDeinit: Bool = true) throws { internal init(
stream: InputStream,
endian: Endian = .unknown,
closeOnDeinit: Bool = true) throws {
var endian = endian var endian = endian
if stream.streamStatus == .notOpen { if stream.streamStatus == .notOpen {
@ -74,7 +78,7 @@ internal class BinaryReader {
self.endian = endian self.endian = endian
self.closeOnDeinit = closeOnDeinit self.closeOnDeinit = closeOnDeinit
} }
deinit { deinit {
if closeOnDeinit && stream.streamStatus != .closed { if closeOnDeinit && stream.streamStatus != .closed {
stream.close() stream.close()
@ -101,7 +105,7 @@ internal class BinaryReader {
} }
return stream.read(buffer + i, maxLength: maxLength - i) return stream.read(buffer + i, maxLength: maxLength - i)
} }
internal func readUInt8() throws -> UInt8 { internal func readUInt8() throws -> UInt8 {
let bufferSize = 1 let bufferSize = 1
let length = try readStream(&buffer, maxLength: bufferSize) let length = try readStream(&buffer, maxLength: bufferSize)
@ -113,7 +117,7 @@ internal class BinaryReader {
} }
return buffer[0] return buffer[0]
} }
internal func readUInt16() throws -> UInt16 { internal func readUInt16() throws -> UInt16 {
let bufferSize = 2 let bufferSize = 2
let length = try readStream(&buffer, maxLength: bufferSize) let length = try readStream(&buffer, maxLength: bufferSize)
@ -134,14 +138,14 @@ internal class BinaryReader {
} }
} }
} }
internal func readUInt32() throws -> UInt32 { internal func readUInt32() throws -> UInt32 {
let bufferSize = 4 let bufferSize = 4
let length = try readStream(&buffer, maxLength: bufferSize) let length = try readStream(&buffer, maxLength: bufferSize)
if length < 0 { if length < 0 {
throw CSVError.streamErrorHasOccurred(error: stream.streamError!) throw CSVError.streamErrorHasOccurred(error: stream.streamError!)
} }
if length != 4 { if length != bufferSize {
throw CSVError.stringEncodingMismatch throw CSVError.stringEncodingMismatch
} }
return try UnsafePointer(buffer).withMemoryRebound(to: UInt32.self, capacity: 1) { return try UnsafePointer(buffer).withMemoryRebound(to: UInt32.self, capacity: 1) {
@ -155,33 +159,32 @@ internal class BinaryReader {
} }
} }
} }
} }
extension BinaryReader { extension BinaryReader {
internal struct UInt8Iterator: Sequence, IteratorProtocol { internal struct UInt8Iterator: Sequence, IteratorProtocol {
private let reader: BinaryReader private let reader: BinaryReader
fileprivate init(reader: BinaryReader) { fileprivate init(reader: BinaryReader) {
self.reader = reader self.reader = reader
} }
internal mutating func next() -> UInt8? { internal mutating func next() -> UInt8? {
if !reader.hasBytesAvailable { if !reader.hasBytesAvailable {
return nil return nil
} }
do { do {
return try reader.readUInt8() return try reader.readUInt8()
} } catch {
catch {
return nil return nil
} }
} }
} }
internal func makeUInt8Iterator() -> UInt8Iterator { internal func makeUInt8Iterator() -> UInt8Iterator {
return UInt8Iterator(reader: self) return UInt8Iterator(reader: self)
} }
@ -189,33 +192,32 @@ extension BinaryReader {
} }
extension BinaryReader { extension BinaryReader {
internal struct UInt16Iterator: Sequence, IteratorProtocol { internal struct UInt16Iterator: Sequence, IteratorProtocol {
private let reader: BinaryReader private let reader: BinaryReader
fileprivate init(reader: BinaryReader) { fileprivate init(reader: BinaryReader) {
self.reader = reader self.reader = reader
} }
internal mutating func next() -> UInt16? { internal mutating func next() -> UInt16? {
if !reader.hasBytesAvailable { if !reader.hasBytesAvailable {
return nil return nil
} }
do { do {
return try reader.readUInt16() return try reader.readUInt16()
} } catch {
catch {
return nil return nil
} }
} }
} }
internal func makeUInt16Iterator() -> UInt16Iterator { internal func makeUInt16Iterator() -> UInt16Iterator {
return UInt16Iterator(reader: self) return UInt16Iterator(reader: self)
} }
} }
extension BinaryReader { extension BinaryReader {
@ -234,8 +236,7 @@ extension BinaryReader {
} }
do { do {
return try reader.readUInt32() return try reader.readUInt32()
} } catch {
catch {
return nil return nil
} }
} }

View File

@ -9,34 +9,33 @@
import Foundation import Foundation
extension CSV { extension CSV {
// TODO: Documentation /// Create an instance with `InputStream`.
/// No overview available. ///
/// - parameter stream: An `InputStream` object. If the stream is not open,
/// initializer opens automatically.
/// - parameter config: CSV configuration.
public init( public init(
stream: InputStream, stream: InputStream,
hasHeaderRow: Bool = defaultHasHeaderRow, config: CSVConfiguration = CSVConfiguration()) throws {
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter) try self.init(stream: stream, codecType: UTF8.self, config: config)
throws
{
try self.init(stream: stream, codecType: UTF8.self, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter)
} }
} }
extension CSV { extension CSV {
// TODO: Documentation /// Create an instance with CSV string.
/// No overview available. ///
/// - parameter string: An CSV string.
/// - parameter config: CSV configuration.
public init( public init(
string: String, string: String,
hasHeaderRow: Bool = defaultHasHeaderRow, config: CSVConfiguration = CSVConfiguration()) throws {
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter)
throws
{
let iterator = string.unicodeScalars.makeIterator() let iterator = string.unicodeScalars.makeIterator()
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter) try self.init(iterator: iterator, config: config)
} }
} }

View File

@ -0,0 +1,89 @@
//
// CSV+iterator.swift
// CSV
//
// Created by Yasuhiro Hatta on 2016/10/23.
// Copyright © 2016 yaslab. All rights reserved.
//
extension CSV: IteratorProtocol, Sequence {
/// No overview available.
public mutating func next() -> Row? {
guard let row = readRow() else {
return nil
}
return Row(data: row, headerRow: headerRow)
}
}
extension CSV {
/// No overview available.
public struct Row: RandomAccessCollection {
private let data: [String]
private let headerRow: [String]?
internal init(data: [String], headerRow: [String]?) {
self.data = data
self.headerRow = headerRow
}
// MARK: - RandomAccessCollection
/// No overview available.
public var startIndex: Int {
return data.startIndex
}
/// No overview available.
public var endIndex: Int {
return data.endIndex
}
/// No overview available.
public func index(before i: Int) -> Int {
return data.index(before: i)
}
/// No overview available.
public func index(after i: Int) -> Int {
return data.index(after: i)
}
/// No overview available.
public subscript(index: Int) -> String {
return data[index]
}
// MARK: - Public method
/// No overview available.
public subscript(key: String) -> String? {
assert(headerRow != nil, "CSVConfiguration.hasHeaderRow must be true")
guard let index = headerRow!.index(of: key) else {
return nil
}
return data[index]
}
/// No overview available.
public func toArray() -> [String] {
return data
}
/// No overview available.
public func toDictionary() -> [String : String] {
assert(headerRow != nil, "CSVConfiguration.hasHeaderRow must be true")
var dictionary: [String : String] = [:]
for (key, value) in zip(headerRow!, data) {
dictionary[key] = value
}
return dictionary
}
}
}

View File

@ -1,28 +0,0 @@
//
// CSV+subscript.swift
// CSV
//
// Created by Yasuhiro Hatta on 2016/06/13.
// Copyright © 2016 yaslab. All rights reserved.
//
extension CSV {
// TODO: Documentation
/// No overview available.
public subscript(key: String) -> String? {
get {
guard let headerRow = headerRow, let currentRow = currentRow else {
return nil
}
guard let index = headerRow.index(of: key) else {
return nil
}
if index >= currentRow.count {
return nil
}
return currentRow[index]
}
}
}

View File

@ -8,193 +8,159 @@
import Foundation import Foundation
private let LF = UnicodeScalar("\n")! private let LF = UnicodeScalar(UInt8(0x0a)) // "\n"
private let CR = UnicodeScalar("\r")! private let CR = UnicodeScalar(UInt8(0x0d)) // "\r"
private let DQUOTE = UnicodeScalar("\"")! private let DQUOTE = UnicodeScalar(UInt8(0x22)) // "\""
internal let defaultHasHeaderRow = false
internal let defaultTrimFields = false
internal let defaultDelimiter = UnicodeScalar(",")!
extension CSV: Sequence { }
extension CSV: IteratorProtocol {
// TODO: Documentation
/// No overview available.
public mutating func next() -> [String]? {
return readRow()
}
}
// TODO: Documentation
/// No overview available. /// No overview available.
public struct CSV { public struct CSV {
private var iterator: AnyIterator<UnicodeScalar> private var iterator: AnyIterator<UnicodeScalar>
private let trimFields: Bool private let config: CSVConfiguration
private let delimiter: UnicodeScalar
private var back: UnicodeScalar? = nil private var back: UnicodeScalar? = nil
internal var currentRow: [String]? = nil /// CSV header row. To set a value for this property,
/// you set `true` to `hasHeaerRow` in initializer.
/// CSV header row. To set a value for this property, you set `true` to `hasHeaerRow` in initializer. public private(set) var headerRow: [String]? = nil
public var headerRow: [String]? { return _headerRow }
private var _headerRow: [String]? = nil
private let whitespaces: CharacterSet
internal init<T: IteratorProtocol>( internal init<T: IteratorProtocol>(
iterator: T, iterator: T,
hasHeaderRow: Bool, config: CSVConfiguration
trimFields: Bool, ) throws where T.Element == UnicodeScalar {
delimiter: UnicodeScalar)
throws
where T.Element == UnicodeScalar
{
self.iterator = AnyIterator(iterator)
self.trimFields = trimFields
self.delimiter = delimiter
var whitespaces = CharacterSet.whitespaces self.iterator = AnyIterator(iterator)
whitespaces.remove(delimiter) self.config = config
self.whitespaces = whitespaces
if config.hasHeaderRow {
if hasHeaderRow { guard let headerRow = readRow() else {
guard let headerRow = next() else {
throw CSVError.cannotReadHeaderRow throw CSVError.cannotReadHeaderRow
} }
_headerRow = headerRow self.headerRow = headerRow
} }
} }
/// Create an instance with `InputStream`. /// Create an instance with `InputStream`.
/// ///
/// - parameter stream: An `InputStream` object. If the stream is not open, initializer opens automatically. /// - parameter stream: An `InputStream` object. If the stream is not open,
/// initializer opens automatically.
/// - parameter codecType: A `UnicodeCodec` type for `stream`. /// - parameter codecType: A `UnicodeCodec` type for `stream`.
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`. /// - parameter config: CSV configuration.
/// - parameter delimiter: Default: `","`.
public init<T: UnicodeCodec>( public init<T: UnicodeCodec>(
stream: InputStream, stream: InputStream,
codecType: T.Type, codecType: T.Type,
hasHeaderRow: Bool = defaultHasHeaderRow, config: CSVConfiguration = CSVConfiguration()
trimFields: Bool = defaultTrimFields, ) throws where T.CodeUnit == UInt8 {
delimiter: UnicodeScalar = defaultDelimiter)
throws
where T.CodeUnit == UInt8
{
let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true) let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true)
let iterator = UnicodeIterator(input: reader.makeUInt8Iterator(), inputEncodingType: codecType) let iterator = UnicodeIterator(
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter) input: reader.makeUInt8Iterator(),
inputEncodingType: codecType
)
try self.init(iterator: iterator, config: config)
} }
/// Create an instance with `InputStream`. /// Create an instance with `InputStream`.
/// ///
/// - parameter stream: An `InputStream` object. If the stream is not open, initializer opens automatically. /// - parameter stream: An `InputStream` object. If the stream is not open,
/// initializer opens automatically.
/// - parameter codecType: A `UnicodeCodec` type for `stream`. /// - parameter codecType: A `UnicodeCodec` type for `stream`.
/// - parameter endian: Endian to use when reading a stream. Default: `.big`. /// - parameter endian: Endian to use when reading a stream. Default: `.big`.
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`. /// - parameter config: CSV configuration.
/// - parameter delimiter: Default: `","`.
public init<T: UnicodeCodec>( public init<T: UnicodeCodec>(
stream: InputStream, stream: InputStream,
codecType: T.Type, codecType: T.Type,
endian: Endian = .big, endian: Endian = .big,
hasHeaderRow: Bool = defaultHasHeaderRow, config: CSVConfiguration = CSVConfiguration()
trimFields: Bool = defaultTrimFields, ) throws where T.CodeUnit == UInt16 {
delimiter: UnicodeScalar = defaultDelimiter)
throws
where T.CodeUnit == UInt16
{
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true) let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
let iterator = UnicodeIterator(input: reader.makeUInt16Iterator(), inputEncodingType: codecType) let iterator = UnicodeIterator(
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter) input: reader.makeUInt16Iterator(),
inputEncodingType: codecType
)
try self.init(iterator: iterator, config: config)
} }
/// Create an instance with `InputStream`. /// Create an instance with `InputStream`.
/// ///
/// - parameter stream: An `InputStream` object. If the stream is not open, initializer opens automatically. /// - parameter stream: An `InputStream` object. If the stream is not open,
/// initializer opens automatically.
/// - parameter codecType: A `UnicodeCodec` type for `stream`. /// - parameter codecType: A `UnicodeCodec` type for `stream`.
/// - parameter endian: Endian to use when reading a stream. Default: `.big`. /// - parameter endian: Endian to use when reading a stream. Default: `.big`.
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`. /// - parameter config: CSV configuration.
/// - parameter delimiter: Default: `","`.
public init<T: UnicodeCodec>( public init<T: UnicodeCodec>(
stream: InputStream, stream: InputStream,
codecType: T.Type, codecType: T.Type,
endian: Endian = .big, endian: Endian = .big,
hasHeaderRow: Bool = defaultHasHeaderRow, config: CSVConfiguration = CSVConfiguration()
trimFields: Bool = defaultTrimFields, ) throws where T.CodeUnit == UInt32 {
delimiter: UnicodeScalar = defaultDelimiter)
throws
where T.CodeUnit == UInt32
{
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
let iterator = UnicodeIterator(input: reader.makeUInt32Iterator(), inputEncodingType: codecType)
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter)
}
fileprivate mutating func readRow() -> [String]? {
currentRow = nil
var next = moveNext() let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
if next == nil { let iterator = UnicodeIterator(
input: reader.makeUInt32Iterator(),
inputEncodingType: codecType
)
try self.init(iterator: iterator, config: config)
}
// MARK: - Parse CSV
internal mutating func readRow() -> [String]? {
var c = moveNext()
if c == nil {
return nil return nil
} }
var row = [String]() var row = [String]()
var field: String var field: String
var end: Bool var end: Bool
while true { while true {
if trimFields { if config.trimFields {
// Trim the leading spaces // Trim the leading spaces
while next != nil && whitespaces.contains(next!) { while c != nil && config.whitespaces.contains(c!) {
next = moveNext() c = moveNext()
} }
} }
if next == nil { if c == nil {
(field, end) = ("", true) (field, end) = ("", true)
} } else if c == DQUOTE {
else if next == DQUOTE {
(field, end) = readField(quoted: true) (field, end) = readField(quoted: true)
} } else {
else { back = c
back = next
(field, end) = readField(quoted: false) (field, end) = readField(quoted: false)
if trimFields { if config.trimFields {
// Trim the trailing spaces // Trim the trailing spaces
field = field.trimmingCharacters(in: whitespaces) field = field.trimmingCharacters(in: config.whitespaces)
} }
} }
row.append(field) row.append(field)
if end { if end {
break break
} }
next = moveNext() c = moveNext()
} }
currentRow = row
return row return row
} }
private mutating func readField(quoted: Bool) -> (String, Bool) { private mutating func readField(quoted: Bool) -> (String, Bool) {
var field = "" var field = ""
var next = moveNext() while let c = moveNext() {
while let c = next {
if quoted { if quoted {
if c == DQUOTE { if c == DQUOTE {
var cNext = moveNext() var cNext = moveNext()
if trimFields { if config.trimFields {
// Trim the trailing spaces // Trim the trailing spaces
while cNext != nil && whitespaces.contains(cNext!) { while cNext != nil && config.whitespaces.contains(cNext!) {
cNext = moveNext() cNext = moveNext()
} }
} }
if cNext == nil || cNext == CR || cNext == LF { if cNext == nil || cNext == CR || cNext == LF {
if cNext == CR { if cNext == CR {
let cNextNext = moveNext() let cNextNext = moveNext()
@ -204,25 +170,20 @@ public struct CSV {
} }
// END ROW // END ROW
return (field, true) return (field, true)
} } else if cNext == config.delimiter {
else if cNext == delimiter {
// END FIELD // END FIELD
return (field, false) return (field, false)
} } else if cNext == DQUOTE {
else if cNext == DQUOTE {
// ESC // ESC
field.append(String(DQUOTE)) field.append(String(DQUOTE))
} } else {
else {
// ERROR? // ERROR?
field.append(String(c)) field.append(String(c))
} }
} } else {
else {
field.append(String(c)) field.append(String(c))
} }
} } else {
else {
if c == CR || c == LF { if c == CR || c == LF {
if c == CR { if c == CR {
let cNext = moveNext() let cNext = moveNext()
@ -232,29 +193,141 @@ public struct CSV {
} }
// END ROW // END ROW
return (field, true) return (field, true)
} } else if c == config.delimiter {
else if c == delimiter {
// END FIELD // END FIELD
return (field, false) return (field, false)
} } else {
else {
field.append(String(c)) field.append(String(c))
} }
} }
next = moveNext()
} }
// END FILE // END FILE
return (field, true) return (field, true)
} }
private mutating func moveNext() -> UnicodeScalar? { private mutating func moveNext() -> UnicodeScalar? {
if back != nil { if back != nil {
defer { back = nil } defer {
back = nil
}
return back return back
} }
return iterator.next() return iterator.next()
} }
}
extension CSV {
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:codecType:config:) instead")
public init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter
) throws where T.CodeUnit == UInt8 {
let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true)
let iterator = UnicodeIterator(
input: reader.makeUInt8Iterator(),
inputEncodingType: codecType
)
let config = CSVConfiguration(
hasHeaderRow: hasHeaderRow,
trimFields: trimFields,
delimiter: delimiter
)
try self.init(iterator: iterator, config: config)
}
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:codecType:endian:config:) instead")
public init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
endian: Endian = .big,
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter
) throws where T.CodeUnit == UInt16 {
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
let iterator = UnicodeIterator(
input: reader.makeUInt16Iterator(),
inputEncodingType: codecType
)
let config = CSVConfiguration(
hasHeaderRow: hasHeaderRow,
trimFields: trimFields,
delimiter: delimiter
)
try self.init(iterator: iterator, config: config)
}
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:codecType:endian:config:) instead")
public init<T: UnicodeCodec>(
stream: InputStream,
codecType: T.Type,
endian: Endian = .big,
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter
) throws where T.CodeUnit == UInt32 {
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
let iterator = UnicodeIterator(
input: reader.makeUInt32Iterator(),
inputEncodingType: codecType
)
let config = CSVConfiguration(
hasHeaderRow: hasHeaderRow,
trimFields: trimFields,
delimiter: delimiter
)
try self.init(iterator: iterator, config: config)
}
/// Unavailable.
@available(*, unavailable, message: "Use init(stream:config:) instead")
public init(
stream: InputStream,
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter) throws {
let config = CSVConfiguration(
hasHeaderRow: hasHeaderRow,
trimFields: trimFields,
delimiter: delimiter
)
try self.init(stream: stream, codecType: UTF8.self, config: config)
}
/// Unavailable.
@available(*, unavailable, message: "Use init(string:config:) instead")
public init(
string: String,
hasHeaderRow: Bool = defaultHasHeaderRow,
trimFields: Bool = defaultTrimFields,
delimiter: UnicodeScalar = defaultDelimiter) throws {
let iterator = string.unicodeScalars.makeIterator()
let config = CSVConfiguration(
hasHeaderRow: hasHeaderRow,
trimFields: trimFields,
delimiter: delimiter
)
try self.init(iterator: iterator, config: config)
}
/// Unavailable
@available(*, unavailable, message: "Use CSV.Row.subscript(String) instead")
public subscript(key: String) -> String? {
return nil
}
} }

View File

@ -6,10 +6,9 @@
// Copyright © 2016 yaslab. All rights reserved. // Copyright © 2016 yaslab. All rights reserved.
// //
// TODO: Documentation
/// No overview available. /// No overview available.
public enum CSVError: Error { public enum CSVError: Error {
/// No overview available. /// No overview available.
case cannotOpenFile case cannotOpenFile
/// No overview available. /// No overview available.
@ -22,5 +21,5 @@ public enum CSVError: Error {
case stringEncodingMismatch case stringEncodingMismatch
/// No overview available. /// No overview available.
case stringEndianMismatch case stringEndianMismatch
} }

View File

@ -6,15 +6,14 @@
// Copyright © 2016 yaslab. All rights reserved. // Copyright © 2016 yaslab. All rights reserved.
// //
// TODO: Documentation
/// No overview available. /// No overview available.
public enum Endian { public enum Endian {
/// No overview available. /// No overview available.
case big case big
/// No overview available. /// No overview available.
case little case little
/// No overview available. /// No overview available.
case unknown case unknown
} }

View File

@ -11,15 +11,15 @@ internal struct UnicodeIterator<
InputEncoding: UnicodeCodec> InputEncoding: UnicodeCodec>
: IteratorProtocol : IteratorProtocol
where InputEncoding.CodeUnit == Input.Element { where InputEncoding.CodeUnit == Input.Element {
private var input: Input private var input: Input
private var inputEncoding: InputEncoding private var inputEncoding: InputEncoding
internal init(input: Input, inputEncodingType: InputEncoding.Type) { internal init(input: Input, inputEncodingType: InputEncoding.Type) {
self.input = input self.input = input
self.inputEncoding = inputEncodingType.init() self.inputEncoding = inputEncodingType.init()
} }
internal mutating func next() -> UnicodeScalar? { internal mutating func next() -> UnicodeScalar? {
switch inputEncoding.decode(&input) { switch inputEncoding.decode(&input) {
case .scalarValue(let c): return c case .scalarValue(let c): return c
@ -27,5 +27,5 @@ internal struct UnicodeIterator<
case .error: return nil case .error: return nil
} }
} }
} }

View File

@ -10,41 +10,41 @@ import XCTest
@testable import CSV @testable import CSV
class CSVTests: XCTestCase { class CSVTests: XCTestCase {
func testOneLine() { func testOneLine() {
let csv = "\"abc\",1,2" let csv = "\"abc\",1,2"
var i = 0 var i = 0
for row in try! CSV(string: csv) { for row in try! CSV(string: csv) {
switch i { switch i {
case 0: XCTAssertEqual(row, ["abc", "1", "2"]) case 0: XCTAssertEqual(row.toArray(), ["abc", "1", "2"])
default: break default: break
} }
i += 1 i += 1
} }
XCTAssertEqual(i, 1) XCTAssertEqual(i, 1)
} }
func testTwoLines() { func testTwoLines() {
let csv = "\"abc\",1,2\n\"cde\",3,4" let csv = "\"abc\",1,2\n\"cde\",3,4"
var i = 0 var i = 0
for row in try! CSV(string: csv) { for row in try! CSV(string: csv) {
switch i { switch i {
case 0: XCTAssertEqual(row, ["abc", "1", "2"]) case 0: XCTAssertEqual(row.toArray(), ["abc", "1", "2"])
case 1: XCTAssertEqual(row, ["cde", "3", "4"]) case 1: XCTAssertEqual(row.toArray(), ["cde", "3", "4"])
default: break default: break
} }
i += 1 i += 1
} }
XCTAssertEqual(i, 2) XCTAssertEqual(i, 2)
} }
func testLastLineIsEmpty() { func testLastLineIsEmpty() {
let csv = "\"abc\",1,2\n\"cde\",3,4\n" let csv = "\"abc\",1,2\n\"cde\",3,4\n"
var i = 0 var i = 0
for row in try! CSV(string: csv) { for row in try! CSV(string: csv) {
switch i { switch i {
case 0: XCTAssertEqual(row, ["abc", "1", "2"]) case 0: XCTAssertEqual(row.toArray(), ["abc", "1", "2"])
case 1: XCTAssertEqual(row, ["cde", "3", "4"]) case 1: XCTAssertEqual(row.toArray(), ["cde", "3", "4"])
default: break default: break
} }
i += 1 i += 1
@ -57,9 +57,9 @@ class CSVTests: XCTestCase {
var i = 0 var i = 0
for row in try! CSV(string: csv) { for row in try! CSV(string: csv) {
switch i { switch i {
case 0: XCTAssertEqual(row, ["abc", "1", "2"]) case 0: XCTAssertEqual(row.toArray(), ["abc", "1", "2"])
case 1: XCTAssertEqual(row, ["cde", "3", "4"]) case 1: XCTAssertEqual(row.toArray(), ["cde", "3", "4"])
case 2: XCTAssertEqual(row, [" "]) case 2: XCTAssertEqual(row.toArray(), [" "])
default: break default: break
} }
i += 1 i += 1
@ -72,58 +72,9 @@ class CSVTests: XCTestCase {
var i = 0 var i = 0
for row in try! CSV(string: csv) { for row in try! CSV(string: csv) {
switch i { switch i {
case 0: XCTAssertEqual(row, ["abc", "1", "2"]) case 0: XCTAssertEqual(row.toArray(), ["abc", "1", "2"])
case 1: XCTAssertEqual(row, [""]) case 1: XCTAssertEqual(row.toArray(), [""])
case 2: XCTAssertEqual(row, ["cde", "3", "4"]) case 2: XCTAssertEqual(row.toArray(), ["cde", "3", "4"])
default: break
}
i += 1
}
XCTAssertEqual(i, 3)
}
func testCommaInQuotationMarks() {
let csvString = "abab,\"cd,cd\",efef"
var csv = try! CSV(string: csvString)
let row = csv.next()!
XCTAssertEqual(row, ["abab", "cd,cd", "efef"])
}
func testEscapedQuotationMark1() {
let csvString = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer"
var csv = try! CSV(string: csvString)
var row = csv.next()!
XCTAssertEqual(row, ["abab", "\"cdcd", "efef"])
row = csv.next()!
XCTAssertEqual(row, ["zxcv", "asdf", "qwer"])
}
func testEscapedQuotationMark2() {
let csvString = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\""
var csv = try! CSV(string: csvString)
var row = csv.next()!
XCTAssertEqual(row, ["abab", "cdcd", "efef"])
row = csv.next()!
XCTAssertEqual(row, ["zxcv", "asdf", "qw\"er"])
}
func testEmptyField() {
let csvString = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
var csv = try! CSV(string: csvString)
var row = csv.next()!
XCTAssertEqual(row, ["abab", "", "cdcd", "efef"])
row = csv.next()!
XCTAssertEqual(row, ["zxcv", "asdf", "qw\"er", ""])
}
func testDoubleQuoteBeforeLineBreak() {
let csv = "\"abc\",1,\"2\"\n\n\"cde\",3,\"4\""
var i = 0
for row in try! CSV(string: csv) {
switch i {
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
case 1: XCTAssertEqual(row, [""])
case 2: XCTAssertEqual(row, ["cde", "3", "4"])
default: break default: break
} }
i += 1 i += 1
@ -131,38 +82,109 @@ class CSVTests: XCTestCase {
XCTAssertEqual(i, 3) XCTAssertEqual(i, 3)
} }
func testSubscript() { func testCommaInQuotationMarks() {
let csvString = "id,name\n001,hoge\n002,fuga" let csvString = "abab,\"cd,cd\",efef"
var csv = try! CSV(string: csvString, hasHeaderRow: true) var csv = try! CSV(string: csvString)
let row = csv.next()!
XCTAssertEqual(row.toArray(), ["abab", "cd,cd", "efef"])
}
func testEscapedQuotationMark1() {
let csvString = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer"
var csv = try! CSV(string: csvString)
var row = csv.next()!
XCTAssertEqual(row.toArray(), ["abab", "\"cdcd", "efef"])
row = csv.next()!
XCTAssertEqual(row.toArray(), ["zxcv", "asdf", "qwer"])
}
func testEscapedQuotationMark2() {
let csvString = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\""
var csv = try! CSV(string: csvString)
var row = csv.next()!
XCTAssertEqual(row.toArray(), ["abab", "cdcd", "efef"])
row = csv.next()!
XCTAssertEqual(row.toArray(), ["zxcv", "asdf", "qw\"er"])
}
func testEmptyField() {
let csvString = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
var csv = try! CSV(string: csvString)
var row = csv.next()!
XCTAssertEqual(row.toArray(), ["abab", "", "cdcd", "efef"])
row = csv.next()!
XCTAssertEqual(row.toArray(), ["zxcv", "asdf", "qw\"er", ""])
}
func testDoubleQuoteBeforeLineBreak() {
let csv = "\"abc\",1,\"2\"\n\n\"cde\",3,\"4\""
var i = 0 var i = 0
while csv.next() != nil { for row in try! CSV(string: csv) {
switch i { switch i {
case 0: case 0: XCTAssertEqual(row.toArray(), ["abc", "1", "2"])
XCTAssertEqual(csv["id"], "001") case 1: XCTAssertEqual(row.toArray(), [""])
XCTAssertEqual(csv["name"], "hoge") case 2: XCTAssertEqual(row.toArray(), ["cde", "3", "4"])
case 1: default: break
XCTAssertEqual(csv["id"], "002")
XCTAssertEqual(csv["name"], "fuga")
default:
break
} }
i += 1 i += 1
} }
XCTAssertEqual(i, 2) XCTAssertEqual(i, 3)
} }
func testCSVState1() { func testCSVState1() {
let it = "あ,い1,\"\",えお\n,,x,".unicodeScalars.makeIterator() let it = "あ,い1,\"\",えお\n,,x,".unicodeScalars.makeIterator()
var csv = try! CSV(iterator: it, hasHeaderRow: defaultHasHeaderRow, trimFields: defaultTrimFields, delimiter: defaultDelimiter) var csv = try! CSV(iterator: it, config: CSVConfiguration())
var rows = [[String]]() var rows = [[String]]()
while let row = csv.next() { while let row = csv.next() {
rows.append(row) rows.append(row.toArray())
} }
XCTAssertEqual(rows.count, 2) XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0], ["", "い1", "", "えお"]) XCTAssertEqual(rows[0], ["", "い1", "", "えお"])
XCTAssertEqual(rows[1], ["", "", "x", ""]) XCTAssertEqual(rows[1], ["", "", "x", ""])
} }
func testSubscriptInt() {
let csvString = "a,bb,ccc"
let csv = try! CSV(string: csvString)
for row in csv {
XCTAssertEqual(row[0], "a")
XCTAssertEqual(row[1], "bb")
XCTAssertEqual(row[2], "ccc")
}
}
func testSubscriptString() {
let csvString = "key1,key2\nvalue1,value2"
let config = CSVConfiguration(hasHeaderRow: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv {
XCTAssertEqual(row["key1"], "value1")
XCTAssertEqual(row["key2"], "value2")
XCTAssertNil(row["key9"])
}
}
func testToArray() {
let csvString = "1,2,3,4,5\n6,7,8,9,0"
let csv = try! CSV(string: csvString)
let rows = csv.map { $0.toArray() }
XCTAssertEqual(rows[0], ["1", "2", "3", "4", "5"])
XCTAssertEqual(rows[1], ["6", "7", "8", "9", "0"])
}
func testToDictionary() {
let csvString = "id,name\n1,name1\n2,name2"
let config = CSVConfiguration(hasHeaderRow: true)
let csv = try! CSV(string: csvString, config: config)
let rows = csv.map { $0.toDictionary() }
XCTAssertEqual(rows[0]["id"], "1")
XCTAssertEqual(rows[0]["name"], "name1")
XCTAssertNil(rows[0]["xxx"])
XCTAssertEqual(rows[1]["id"], "2")
XCTAssertEqual(rows[1]["name"], "name2")
XCTAssertNil(rows[1]["yyy"])
}
} }

View File

@ -64,7 +64,7 @@ class LineBreakTests: XCTestCase {
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], ["zxc", "rty"]) XCTAssertEqual(records[1], ["zxc", "rty"])
} }
func testLineBreakCR() { func testLineBreakCR() {
let csv = "qwe,asd\rzxc,rty" let csv = "qwe,asd\rzxc,rty"
let records = parse(csv: csv) let records = parse(csv: csv)
@ -72,7 +72,7 @@ class LineBreakTests: XCTestCase {
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], ["zxc", "rty"]) XCTAssertEqual(records[1], ["zxc", "rty"])
} }
func testLineBreakCRLF() { func testLineBreakCRLF() {
let csv = "qwe,asd\r\nzxc,rty" let csv = "qwe,asd\r\nzxc,rty"
let records = parse(csv: csv) let records = parse(csv: csv)
@ -80,7 +80,7 @@ class LineBreakTests: XCTestCase {
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], ["zxc", "rty"]) XCTAssertEqual(records[1], ["zxc", "rty"])
} }
func testLineBreakLFLF() { func testLineBreakLFLF() {
let csv = "qwe,asd\n\nzxc,rty" let csv = "qwe,asd\n\nzxc,rty"
let records = parse(csv: csv) let records = parse(csv: csv)
@ -112,7 +112,7 @@ class LineBreakTests: XCTestCase {
let reader = try! CSV(string: csv) let reader = try! CSV(string: csv)
var records = [[String]]() var records = [[String]]()
for row in reader { for row in reader {
records.append(row) records.append(row.toArray())
} }
return records return records
} }

View File

@ -10,53 +10,65 @@ import XCTest
@testable import CSV @testable import CSV
class ReadmeTests: XCTestCase { class ReadmeTests: XCTestCase {
func testFromCSVString() { func testFromCSVString() {
for row in try! CSV(string: "1,foo\n2,bar") { let csv = try! CSV(string: "1,foo\n2,bar")
print("\(row)")
// => ["1", "foo"]
// => ["2", "bar"]
}
}
func testFromFilePath() {
// let stream = InputStream(fileAtPath: "/path/to/file.csv")!
// for row in try! CSV(stream: stream) {
// print("\(row)")
// }
}
func testGettingTheHeaderRow() {
let csv = try! CSV(
string: "id,name\n1,foo\n2,bar",
hasHeaderRow: true) // default: false
let headerRow = csv.headerRow!
print("\(headerRow)") // => ["id", "name"]
for row in csv { for row in csv {
print("\(row)") print("\(row)")
// => ["1", "foo"] // => ["1", "foo"]
// => ["2", "bar"] // => ["2", "bar"]
} }
} }
func testGetTheFieldValueUsingSubscript() { func testFromFile() {
var csv = try! CSV( // let stream = InputStream(fileAtPath: "/path/to/file.csv")!
string: "id,name\n1,foo", // let csv = try! CSV(stream: stream)
hasHeaderRow: true) // It must be true. // for row in csv {
// print("\(row)")
while let _ = csv.next() { // }
print("\(csv["id"]!)") // => "1" }
print("\(csv["name"]!)") // => "foo"
func testGettingTheHeaderRow() {
let csvString = "id,name\n1,foo\n2,bar"
let config = CSVConfiguration(hasHeaderRow: true) // It must be true.
let csv = try! CSV(string: csvString, config: config)
let headerRow = csv.headerRow!
print("\(headerRow)") // => ["id", "name"]
for row in csv {
print("\(row)")
// => ["1", "foo"]
// => ["2", "bar"]
} }
} }
func testGetTheFieldValueUsingIndex() {
let csvString = "1,foo"
let csv = try! CSV(string: csvString)
for row in csv {
print("\(row[0])") // => "1"
print("\(row[1])") // => "foo"
}
}
func testGetTheFieldValueUsingKey() {
let csvString = "id,name\n1,foo"
let config = CSVConfiguration(hasHeaderRow: true) // It must be true.
let csv = try! CSV(string: csvString, config: config)
for row in csv {
print("\(row["id"]!)") // => "1"
print("\(row["name"]!)") // => "foo"
}
}
func testProvideTheCharacterEncoding() { func testProvideTheCharacterEncoding() {
// let csv = try! CSV( // let csv = try! CSV(
// stream: InputStream(fileAtPath: "/path/to/file.csv")!, // stream: InputStream(fileAtPath: "/path/to/file.csv")!,
// codecType: UTF16.self, // codecType: UTF16.self,
// endian: .big) // endian: .big)
} }
} }

View File

@ -13,110 +13,173 @@ class TrimFieldsTests: XCTestCase {
func testTrimFields1() { func testTrimFields1() {
let csvString = "abc,def,ghi" let csvString = "abc,def,ghi"
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields2() { func testTrimFields2() {
let csvString = " abc, def, ghi" let csvString = " abc, def, ghi"
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields3() { func testTrimFields3() {
let csvString = "abc ,def ,ghi " let csvString = "abc ,def ,ghi "
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields4() { func testTrimFields4() {
let csvString = " abc , def , ghi " let csvString = " abc , def , ghi "
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields5() { func testTrimFields5() {
let csvString = "\"abc\",\"def\",\"ghi\"" let csvString = "\"abc\",\"def\",\"ghi\""
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields6() { func testTrimFields6() {
let csvString = " \"abc\", \"def\", \"ghi\"" let csvString = " \"abc\", \"def\", \"ghi\""
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields7() { func testTrimFields7() {
let csvString = "\"abc\" ,\"def\" ,\"ghi\" " let csvString = "\"abc\" ,\"def\" ,\"ghi\" "
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields8() { func testTrimFields8() {
let csvString = " \"abc\" , \"def\" , \"ghi\" " let csvString = " \"abc\" , \"def\" , \"ghi\" "
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields9() { func testTrimFields9() {
let csvString = "\" abc \",\" def \",\" ghi \"" let csvString = "\" abc \",\" def \",\" ghi \""
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, [" abc ", " def ", " ghi "]) XCTAssertEqual(row.toArray(), [" abc ", " def ", " ghi "])
} }
} }
func testTrimFields10() { func testTrimFields10() {
let csvString = "\tabc,\t\tdef\t,ghi\t" let csvString = "\tabc,\t\tdef\t,ghi\t"
let csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "def", "ghi"])
} }
} }
func testTrimFields11() { func testTrimFields11() {
let csvString = " abc \n def " let csvString = " abc \n def "
var csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
var csv = try! CSV(string: csvString, config: config)
let row1 = csv.next()! let row1 = csv.next()!
XCTAssertEqual(row1, ["abc"]) XCTAssertEqual(row1.toArray(), ["abc"])
let row2 = csv.next()! let row2 = csv.next()!
XCTAssertEqual(row2, ["def"]) XCTAssertEqual(row2.toArray(), ["def"])
} }
func testTrimFields12() { func testTrimFields12() {
let csvString = " \"abc \" \n \" def\" " let csvString = " \"abc \" \n \" def\" "
var csv = try! CSV(string: csvString, trimFields: true) let config = CSVConfiguration(trimFields: true)
var csv = try! CSV(string: csvString, config: config)
let row1 = csv.next()! let row1 = csv.next()!
XCTAssertEqual(row1, ["abc "]) XCTAssertEqual(row1.toArray(), ["abc "])
let row2 = csv.next()! let row2 = csv.next()!
XCTAssertEqual(row2, [" def"]) XCTAssertEqual(row2.toArray(), [" def"])
} }
func testTrimFields13() { func testTrimFields13() {
let csvString = " abc \t\tdef\t ghi " let csvString = " abc \t\tdef\t ghi "
let csv = try! CSV(string: csvString, trimFields: true, delimiter: UnicodeScalar("\t")!) let config = CSVConfiguration(trimFields: true, delimiter: UnicodeScalar("\t")!)
let csv = try! CSV(string: csvString, config: config)
for row in csv { for row in csv {
XCTAssertEqual(row, ["abc", "", "def", "ghi"]) XCTAssertEqual(row.toArray(), ["abc", "", "def", "ghi"])
} }
} }
func testTrimFields14() {
let csvString = ""
let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
let rows = csv.map { $0.toArray() }
XCTAssertEqual(rows.count, 0)
}
func testTrimFields15() {
let csvString = " "
let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
let rows = csv.map { $0.toArray() }
XCTAssertEqual(rows.count, 1)
XCTAssertEqual(rows[0], [""])
}
func testTrimFields16() {
let csvString = " , "
let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
let rows = csv.map { $0.toArray() }
XCTAssertEqual(rows.count, 1)
XCTAssertEqual(rows[0], ["", ""])
}
func testTrimFields17() {
let csvString = " , \n"
let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
let rows = csv.map { $0.toArray() }
XCTAssertEqual(rows.count, 1)
XCTAssertEqual(rows[0], ["", ""])
}
func testTrimFields18() {
let csvString = " , \n "
let config = CSVConfiguration(trimFields: true)
let csv = try! CSV(string: csvString, config: config)
let rows = csv.map { $0.toArray() }
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0], ["", ""])
XCTAssertEqual(rows[1], [""])
}
} }

View File

@ -23,7 +23,7 @@ class UnicodeTests: XCTestCase {
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
func testUTF16WithNativeEndianBOM() { func testUTF16WithNativeEndianBOM() {
let csvString = "abab,,cdcd,efef\r\nzxcv,😆asdf,\"qw\"\"er\"," let csvString = "abab,,cdcd,efef\r\nzxcv,😆asdf,\"qw\"\"er\","
let encoding = String.Encoding.utf16 let encoding = String.Encoding.utf16
@ -35,7 +35,7 @@ class UnicodeTests: XCTestCase {
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "😆asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "😆asdf", "qw\"er", ""])
} }
func testUTF16WithBigEndianBOM() { func testUTF16WithBigEndianBOM() {
let csvString = "abab,,cdcd,efef\r\n😆zxcv,asdf,\"qw\"\"er\"," let csvString = "abab,,cdcd,efef\r\n😆zxcv,asdf,\"qw\"\"er\","
let encoding = String.Encoding.utf16BigEndian let encoding = String.Encoding.utf16BigEndian
@ -48,7 +48,7 @@ class UnicodeTests: XCTestCase {
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["😆zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["😆zxcv", "asdf", "qw\"er", ""])
} }
func testUTF16WithLittleEndianBOM() { func testUTF16WithLittleEndianBOM() {
let csvString = "abab,,cdcd,efef\r\nzxcv😆,asdf,\"qw\"\"er\"," let csvString = "abab,,cdcd,efef\r\nzxcv😆,asdf,\"qw\"\"er\","
let encoding = String.Encoding.utf16LittleEndian let encoding = String.Encoding.utf16LittleEndian
@ -61,7 +61,7 @@ class UnicodeTests: XCTestCase {
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv😆", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv😆", "asdf", "qw\"er", ""])
} }
func testUTF32WithNativeEndianBOM() { func testUTF32WithNativeEndianBOM() {
let csvString = "😆abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let csvString = "😆abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
let encoding = String.Encoding.utf32 let encoding = String.Encoding.utf32
@ -73,7 +73,7 @@ class UnicodeTests: XCTestCase {
XCTAssertEqual(records[0], ["😆abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["😆abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
func testUTF32WithBigEndianBOM() { func testUTF32WithBigEndianBOM() {
let csvString = "abab,,cd😆cd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let csvString = "abab,,cd😆cd,efef\r\nzxcv,asdf,\"qw\"\"er\","
let encoding = String.Encoding.utf32BigEndian let encoding = String.Encoding.utf32BigEndian
@ -86,7 +86,7 @@ class UnicodeTests: XCTestCase {
XCTAssertEqual(records[0], ["abab", "", "cd😆cd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cd😆cd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
func testUTF32WithLittleEndianBOM() { func testUTF32WithLittleEndianBOM() {
let csvString = "abab,,cdcd,ef😆ef\r\nzxcv,asdf,\"qw\"\"er\"," let csvString = "abab,,cdcd,ef😆ef\r\nzxcv,asdf,\"qw\"\"er\","
let encoding = String.Encoding.utf32LittleEndian let encoding = String.Encoding.utf32LittleEndian
@ -99,13 +99,13 @@ class UnicodeTests: XCTestCase {
XCTAssertEqual(records[0], ["abab", "", "cdcd", "ef😆ef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "ef😆ef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
private func getRecords(csv: CSV) -> [[String]] { private func getRecords(csv: CSV) -> [[String]] {
var records = [[String]]() var records = [[String]]()
for row in csv { for row in csv {
records.append(row) records.append(row.toArray())
} }
return records return records
} }
} }