diff --git a/CSV.xcodeproj/project.pbxproj b/CSV.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index ddec1b3..cdd962d --- a/CSV.xcodeproj/project.pbxproj +++ b/CSV.xcodeproj/project.pbxproj @@ -7,26 +7,30 @@ objects = { /* Begin PBXBuildFile section */ + 0E0F16091D197D6000C92580 /* AnyIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F16081D197D6000C92580 /* AnyIterator.swift */; }; + 0E0F160A1D197D6000C92580 /* AnyIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F16081D197D6000C92580 /* AnyIterator.swift */; }; + 0E0F160B1D197D6000C92580 /* AnyIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F16081D197D6000C92580 /* AnyIterator.swift */; }; + 0E0F160C1D197D6000C92580 /* AnyIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F16081D197D6000C92580 /* AnyIterator.swift */; }; + 0E0F160E1D197DB800C92580 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F160D1D197DB800C92580 /* Endian.swift */; }; + 0E0F160F1D197DB800C92580 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F160D1D197DB800C92580 /* Endian.swift */; }; + 0E0F16101D197DB800C92580 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F160D1D197DB800C92580 /* Endian.swift */; }; + 0E0F16111D197DB800C92580 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F160D1D197DB800C92580 /* Endian.swift */; }; 0E7E8C8C1D0BC7BB0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */; }; - 0E7E8CA01D0BC7F10057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; }; 0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; }; 0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; }; 0E7E8CA31D0BC7F10057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0E7E8CA91D0BC8050057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; }; 0E7E8CAA1D0BC8050057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */; }; - 0E7E8CBD1D0BC9D70057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; }; 0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; }; 0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; }; 0E7E8CC01D0BC9D70057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0E7E8CD01D0BCA2A0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */; }; 0E7E8CDD1D0BCA840057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; }; 0E7E8CDE1D0BCA840057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.swift */; }; - 0E7E8CDF1D0BCA8E0057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; }; 0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; }; 0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; }; 0E7E8CE21D0BCA8E0057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0E7E8CF21D0BCD0B0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */; }; - 0E7E8CFF1D0BCDCF0057A1C1 /* ByteOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */; }; 0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; }; 0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; }; 0E7E8D021D0BCDCF0057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -43,6 +47,14 @@ 0E9317DE1D0DBCC500AC20A0 /* 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 */; }; + 0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */; }; + 0EA2AB7D1D183B45003EC967 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */; }; + 0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */; }; + 0EA2AB7F1D183B45003EC967 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */; }; + 0EA2AB811D183BA9003EC967 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */; }; + 0EA2AB821D183BA9003EC967 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */; }; + 0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */; }; + 0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -70,9 +82,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0E0F16081D197D6000C92580 /* AnyIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyIterator.swift; sourceTree = ""; }; + 0E0F160D1D197DB800C92580 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endian.swift; sourceTree = ""; }; 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; }; - 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ByteOrder.swift; sourceTree = ""; }; 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = ""; }; 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVError.swift; sourceTree = ""; }; 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSVVersion.h; sourceTree = ""; }; @@ -88,6 +101,8 @@ 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+init.swift"; sourceTree = ""; }; 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+subscript.swift"; sourceTree = ""; }; 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = ""; }; + 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = ""; }; + 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeIterator.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -172,13 +187,16 @@ 0E7E8C9B1D0BC7F10057A1C1 /* Sources */ = { isa = PBXGroup; children = ( - 0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */, + 0E0F16081D197D6000C92580 /* AnyIterator.swift */, + 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */, 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */, 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */, 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */, 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */, 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */, + 0E0F160D1D197DB800C92580 /* Endian.swift */, 0E7E8CAC1D0BC8610057A1C1 /* Info.plist */, + 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */, ); path = Sources; sourceTree = ""; @@ -373,7 +391,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = yaslab; TargetAttributes = { 0E7E8C801D0BC7BB0057A1C1 = { @@ -387,9 +405,11 @@ }; 0E7E8CC51D0BCA2A0057A1C1 = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; }; 0E7E8CCE1D0BCA2A0057A1C1 = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; }; 0E7E8CE71D0BCD0B0057A1C1 = { CreatedOnToolsVersion = 7.3.1; @@ -480,10 +500,13 @@ buildActionMask = 2147483647; files = ( 0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */, + 0E0F160A1D197D6000C92580 /* AnyIterator.swift in Sources */, + 0EA2AB821D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0E9317DA1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */, + 0E0F160F1D197DB800C92580 /* Endian.swift in Sources */, 0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */, - 0E7E8CA01D0BC7F10057A1C1 /* ByteOrder.swift in Sources */, + 0EA2AB7D1D183B45003EC967 /* BinaryReader.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -502,10 +525,13 @@ buildActionMask = 2147483647; files = ( 0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */, + 0E0F160C1D197D6000C92580 /* AnyIterator.swift in Sources */, + 0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0E9317DC1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */, + 0E0F16111D197DB800C92580 /* Endian.swift in Sources */, 0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */, - 0E7E8CBD1D0BC9D70057A1C1 /* ByteOrder.swift in Sources */, + 0EA2AB7F1D183B45003EC967 /* BinaryReader.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -514,10 +540,13 @@ buildActionMask = 2147483647; files = ( 0E9317D41D0DB2F200AC20A0 /* CSV+init.swift in Sources */, + 0E0F16091D197D6000C92580 /* AnyIterator.swift in Sources */, + 0EA2AB811D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0E9317D91D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */, + 0E0F160E1D197DB800C92580 /* Endian.swift in Sources */, 0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */, - 0E7E8CDF1D0BCA8E0057A1C1 /* ByteOrder.swift in Sources */, + 0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -536,10 +565,13 @@ buildActionMask = 2147483647; files = ( 0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */, + 0E0F160B1D197D6000C92580 /* AnyIterator.swift in Sources */, + 0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */, 0E9317DB1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */, + 0E0F16101D197DB800C92580 /* Endian.swift in Sources */, 0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */, - 0E7E8CFF1D0BCDCF0057A1C1 /* ByteOrder.swift in Sources */, + 0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -696,6 +728,7 @@ PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV; PRODUCT_NAME = CSV; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -716,6 +749,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -754,6 +788,7 @@ PRODUCT_NAME = CSV; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -777,6 +812,7 @@ PRODUCT_NAME = CSV; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -798,6 +834,8 @@ PRODUCT_NAME = CSV; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -812,6 +850,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -826,6 +865,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -862,6 +903,7 @@ PRODUCT_NAME = CSV; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; }; @@ -887,6 +929,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Release; diff --git a/CSV.xcodeproj/xcshareddata/xcschemes/CSV-OSX.xcscheme b/CSV.xcodeproj/xcshareddata/xcschemes/CSV-OSX.xcscheme index a9d1724..9589fd3 100644 --- a/CSV.xcodeproj/xcshareddata/xcschemes/CSV-OSX.xcscheme +++ b/CSV.xcodeproj/xcshareddata/xcschemes/CSV-OSX.xcscheme @@ -1,6 +1,6 @@ 0.2' +pod 'CSV.swift', '~> 0.3' ``` ### Carthage ``` -github "yaslab/CSV.swift" ~> 0.2 +github "yaslab/CSV.swift" ~> 0.3 ``` ### Swift Package Manager @@ -90,7 +92,7 @@ import PackageDescription let package = Package( name: "PackageName", dependencies: [ - .Package(url: "https://github.com/yaslab/CSV.swift", majorVersion: 0, minor: 2) + .Package(url: "https://github.com/yaslab/CSV.swift", majorVersion: 0, minor: 3) ] ) ``` diff --git a/Sources/AnyIterator.swift b/Sources/AnyIterator.swift new file mode 100644 index 0000000..b95aee3 --- /dev/null +++ b/Sources/AnyIterator.swift @@ -0,0 +1,22 @@ +// +// AnyIterator.swift +// CSV +// +// Created by Yasuhiro Hatta on 2016/06/21. +// Copyright © 2016 yaslab. All rights reserved. +// + +internal struct AnyIterator: GeneratorType { + + private var _base_next: (() -> T?) + + internal init(base: U) { + var base = base + _base_next = { base.next() } + } + + internal mutating func next() -> T? { + return _base_next() + } + +} diff --git a/Sources/BinaryReader.swift b/Sources/BinaryReader.swift new file mode 100755 index 0000000..ae7c8af --- /dev/null +++ b/Sources/BinaryReader.swift @@ -0,0 +1,247 @@ +// +// BinaryReader.swift +// CSV +// +// Created by Yasuhiro Hatta on 2016/06/20. +// Copyright © 2016 yaslab. All rights reserved. +// + +import Foundation + +internal let utf8BOM: [UInt8] = [0xef, 0xbb, 0xbf] +internal let utf16BigEndianBOM: [UInt8] = [0xfe, 0xff] +internal let utf16LittleEndianBOM: [UInt8] = [0xff, 0xfe] +internal let utf32BigEndianBOM: [UInt8] = [0x00, 0x00, 0xfe, 0xff] +internal let utf32LittleEndianBOM: [UInt8] = [0xff, 0xfe, 0x00, 0x00] + +private func readBOM(buffer buffer: UnsafePointer, length: Int) -> (Endian, Int)? { + if length >= 4 { + if memcmp(buffer, utf32BigEndianBOM, 4) == 0 { + return (.Big, 4) + } + if memcmp(buffer, utf32LittleEndianBOM, 4) == 0 { + return (.Little, 4) + } + } + if length >= 3 { + if memcmp(buffer, utf8BOM, 3) == 0 { + return (.Unknown, 3) + } + } + if length >= 2 { + if memcmp(buffer, utf16BigEndianBOM, 2) == 0 { + return (.Big, 2) + } + if memcmp(buffer, utf16LittleEndianBOM, 2) == 0 { + return (.Little, 2) + } + } + return nil +} + +internal class BinaryReader { + + private let stream: NSInputStream + private let endian: Endian + private let closeOnDeinit: Bool + + private var buffer = [UInt8](count: 4, repeatedValue: 0) + + private var tempBuffer = [UInt8](count: 4, repeatedValue: 0) + private let tempBufferSize = 4 + private var tempBufferOffset = 0 + + internal init(stream: NSInputStream, endian: Endian = .Unknown, closeOnDeinit: Bool = true) throws { + var endian = endian + + if stream.streamStatus == .NotOpen { + stream.open() + } + if stream.streamStatus != .Open { + throw CSVError.CannotOpenFile + } + + let readCount = stream.read(&tempBuffer, maxLength: tempBufferSize) + if let (e, l) = readBOM(buffer: &tempBuffer, length: readCount) { + if endian != .Unknown && endian != e { + throw CSVError.StringEndianMismatch + } + endian = e + tempBufferOffset = l + } + + self.stream = stream + self.endian = endian + self.closeOnDeinit = closeOnDeinit + } + + deinit { + if closeOnDeinit && stream.streamStatus != .Closed { + stream.close() + } + } + + internal var hasBytesAvailable: Bool { + return stream.hasBytesAvailable + } + + private func readStream(_ buffer: UnsafeMutablePointer, maxLength: Int) throws -> Int { + if stream.streamStatus != .Open { + throw CSVError.CannotReadFile + } + + var i = 0 + while tempBufferOffset < tempBufferSize { + buffer[i] = tempBuffer[tempBufferOffset] + i += 1 + tempBufferOffset += 1 + if i >= maxLength { + return i + } + } + return stream.read(buffer + i, maxLength: maxLength - i) + } + + internal func readUInt8() throws -> UInt8 { + let bufferSize = 1 + let length = try readStream(&buffer, maxLength: bufferSize) + if length < 0 { + throw CSVError.StreamErrorHasOccurred(error: stream.streamError!) + } + if length != bufferSize { + throw CSVError.CannotReadFile + } + return buffer[0] + } + + internal func readUInt16() throws -> UInt16 { + let bufferSize = 2 + let length = try readStream(&buffer, maxLength: bufferSize) + if length < 0 { + throw CSVError.StreamErrorHasOccurred(error: stream.streamError!) + } + if length != bufferSize { + throw CSVError.StringEncodingMismatch + } + let tmp = UnsafeMutablePointer(buffer) + switch endian { + case .Big: + return CFSwapInt16BigToHost(tmp[0]) + case .Little: + return CFSwapInt16LittleToHost(tmp[0]) + default: + throw CSVError.StringEndianMismatch + } + } + + internal func readUInt32() throws -> UInt32 { + let bufferSize = 4 + let length = try readStream(&buffer, maxLength: bufferSize) + if length < 0 { + throw CSVError.StreamErrorHasOccurred(error: stream.streamError!) + } + if length != 4 { + throw CSVError.StringEncodingMismatch + } + let tmp = UnsafeMutablePointer(buffer) + switch endian { + case .Big: + return CFSwapInt32BigToHost(tmp[0]) + case .Little: + return CFSwapInt32LittleToHost(tmp[0]) + default: + throw CSVError.StringEndianMismatch + } + } + +} + +extension BinaryReader { + + internal struct UInt8Iterator: SequenceType, GeneratorType { + + private let reader: BinaryReader + + private init(reader: BinaryReader) { + self.reader = reader + } + + internal mutating func next() -> UInt8? { + if !reader.hasBytesAvailable { + return nil + } + do { + return try reader.readUInt8() + } + catch /*let error*/ { + return nil + } + } + + } + + internal func makeUInt8Iterator() -> UInt8Iterator { + return UInt8Iterator(reader: self) + } + +} + +extension BinaryReader { + + internal struct UInt16Iterator: SequenceType, GeneratorType { + + private let reader: BinaryReader + + private init(reader: BinaryReader) { + self.reader = reader + } + + internal mutating func next() -> UInt16? { + if !reader.hasBytesAvailable { + return nil + } + do { + return try reader.readUInt16() + } + catch /*let error*/ { + return nil + } + } + + } + + internal func makeUInt16Iterator() -> UInt16Iterator { + return UInt16Iterator(reader: self) + } + +} + +extension BinaryReader { + + internal struct UInt32Iterator: SequenceType, GeneratorType { + + private let reader: BinaryReader + + private init(reader: BinaryReader) { + self.reader = reader + } + + internal mutating func next() -> UInt32? { + if !reader.hasBytesAvailable { + return nil + } + do { + return try reader.readUInt32() + } + catch /*let error*/ { + return nil + } + } + + } + + internal func makeUInt32Iterator() -> UInt32Iterator { + return UInt32Iterator(reader: self) + } + +} diff --git a/Sources/ByteOrder.swift b/Sources/ByteOrder.swift deleted file mode 100644 index 3e802ea..0000000 --- a/Sources/ByteOrder.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ByteOrder.swift -// CSV -// -// Created by Yasuhiro Hatta on 2016/06/11. -// -// - -import CoreFoundation - -internal func ReadBigInt16(base: UnsafePointer, byteOffset: Int) -> UInt16 { - let bytes = UnsafePointer(base).advancedBy(byteOffset) - let int16Array = UnsafePointer(bytes) - return CFSwapInt16BigToHost(int16Array[0]) -} - -internal func ReadBigInt32(base: UnsafePointer, byteOffset: Int) -> UInt32 { - let bytes = UnsafePointer(base).advancedBy(byteOffset) - let int32Array = UnsafePointer(bytes) - return CFSwapInt32BigToHost(int32Array[0]) -} - -internal func ReadLittleInt16(base: UnsafePointer, byteOffset: Int) -> UInt16 { - let bytes = UnsafePointer(base).advancedBy(byteOffset) - let int16Array = UnsafePointer(bytes) - return CFSwapInt16LittleToHost(int16Array[0]) -} - -internal func ReadLittleInt32(base: UnsafePointer, byteOffset: Int) -> UInt32 { - let bytes = UnsafePointer(base).advancedBy(byteOffset) - let int32Array = UnsafePointer(bytes) - return CFSwapInt32LittleToHost(int32Array[0]) -} - -internal func IsBigEndian() -> Bool { - return CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderBigEndian.rawValue) -} diff --git a/Sources/CSV+init.swift b/Sources/CSV+init.swift old mode 100644 new mode 100755 index 049ea4a..e823ac7 --- a/Sources/CSV+init.swift +++ b/Sources/CSV+init.swift @@ -9,62 +9,28 @@ import Foundation extension CSV { - - public convenience init( - path: String, + + public init( + stream: NSInputStream, hasHeaderRow: Bool = defaultHasHeaderRow, - encoding: NSStringEncoding = defaultEncoding, - delimiter: CChar = defaultDelimiter, - bufferSize: Int = defaultBufferSize) + delimiter: UnicodeScalar = defaultDelimiter) throws { - guard let stream = NSInputStream(fileAtPath: path) else { - throw CSVError.StreamError - } - try self.init( - stream: stream, - hasHeaderRow: hasHeaderRow, - encoding: encoding, - delimiter: delimiter, - bufferSize: bufferSize) + try self.init(stream: stream, codecType: UTF8.self, hasHeaderRow: hasHeaderRow, delimiter: delimiter) } + +} + +extension CSV { - public convenience init( - url: NSURL, - hasHeaderRow: Bool = defaultHasHeaderRow, - encoding: NSStringEncoding = defaultEncoding, - delimiter: CChar = defaultDelimiter, - bufferSize: Int = defaultBufferSize) - throws - { - guard let stream = NSInputStream(URL: url) else { - throw CSVError.StreamError - } - try self.init( - stream: stream, - hasHeaderRow: hasHeaderRow, - encoding: encoding, - delimiter: delimiter, - bufferSize: bufferSize) - } - - public convenience init( + public init( string: String, hasHeaderRow: Bool = defaultHasHeaderRow, - delimiter: CChar = defaultDelimiter, - bufferSize: Int = defaultBufferSize) + delimiter: UnicodeScalar = defaultDelimiter) throws { - let encoding = defaultEncoding - guard let data = string.dataUsingEncoding(encoding) else { - throw CSVError.StringEncodingMismatch - } - try self.init( - stream: NSInputStream(data: data), - hasHeaderRow: hasHeaderRow, - encoding: encoding, - delimiter: delimiter, - bufferSize: bufferSize) + let iterator = string.unicodeScalars.generate() + try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, delimiter: delimiter) } } diff --git a/Sources/CSV+subscript.swift b/Sources/CSV+subscript.swift old mode 100644 new mode 100755 index 950f714..df90b21 --- a/Sources/CSV+subscript.swift +++ b/Sources/CSV+subscript.swift @@ -6,21 +6,16 @@ // Copyright © 2016 yaslab. All rights reserved. // -import Foundation - extension CSV { public subscript(key: String) -> String? { get { - guard let headerRow = headerRow else { + guard let headerRow = headerRow, let currentRow = currentRow else { return nil } guard let index = headerRow.indexOf(key) else { return nil } - guard let currentRow = currentRow else { - return nil - } if index >= currentRow.count { return nil } diff --git a/Sources/CSV.swift b/Sources/CSV.swift old mode 100644 new mode 100755 index 15f2f43..8f805db --- a/Sources/CSV.swift +++ b/Sources/CSV.swift @@ -3,358 +3,240 @@ // CSV // // Created by Yasuhiro Hatta on 2016/06/11. -// +// Copyright © 2016 yaslab. All rights reserved. // import Foundation -private let LF: UInt32 = 0x0a //'\n' -private let CR: UInt32 = 0x0d //'\r' -private let DQUOTE: UInt32 = 0x22 //'"' +private let LF = "\n".unicodeScalars.first! +private let CR = "\r".unicodeScalars.first! +private let DQUOTE = "\"".unicodeScalars.first! internal let defaultHasHeaderRow = false -internal let defaultEncoding = NSUTF8StringEncoding -internal let defaultDelimiter: CChar = 0x2c //',' -internal let defaultBufferSize = 8192 +internal let defaultDelimiter = ",".unicodeScalars.first! -internal let utf8BOM: [UInt8] = [0xef, 0xbb, 0xbf] -internal let utf16BigEndianBOM: [UInt8] = [0xfe, 0xff] -internal let utf16LittleEndianBOM: [UInt8] = [0xff, 0xfe] -internal let utf32BigEndianBOM: [UInt8] = [0x00, 0x00, 0xfe, 0xff] -internal let utf32LittleEndianBOM: [UInt8] = [0xff, 0xfe, 0x00, 0x00] +public struct CSV: GeneratorType, SequenceType { -public class CSV: SequenceType, GeneratorType { + private var iterator: AnyIterator + private let delimiter: UnicodeScalar - internal let stream: NSInputStream - internal let encoding: NSStringEncoding - internal let delimiter: UInt32 - internal let bufferSize: Int - - internal var buffer: UnsafeMutablePointer! - internal var bufferOffset: Int - internal var lastReadCount: Int - - internal let charWidth: Int - - internal let fieldBuffer: NSMutableData - - internal var closed: Bool = false + 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 var headerRow: [String]? { return _headerRow } private var _headerRow: [String]? = nil - - /** - The value is set when an error occurs. - */ - public private(set) var lastError: CSVError? = nil - /** - Create CSV instance with `NSInputStream`. - - - parameter stream: An `NSInputStream` object. If the stream is not open, initializer opens automatically. - - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`. - - parameter encoding: The character encoding for `stream`. Default: `NSUTF8StringEncoding`. - - parameter delimiter: Default: `","`. - - parameter bufferSize: Size in bytes to be read at a time from the stream. Default: `8192`. - */ - public init( - stream: NSInputStream, - hasHeaderRow: Bool = defaultHasHeaderRow, - encoding: NSStringEncoding = defaultEncoding, - delimiter: CChar = defaultDelimiter, - bufferSize: Int = defaultBufferSize) + internal init( + iterator: T, + hasHeaderRow: Bool, + delimiter: UnicodeScalar) throws { - self.stream = stream - - var bs = bufferSize - if bs < 0 { - throw CSVError.ParameterError - } - if bs < 8 { - bs = 8 - } - let mod = bs % 4 - if mod != 0 { - bs += 4 - mod - } - self.bufferSize = bs - - self.delimiter = UInt32(delimiter) - - let b = malloc(bufferSize) - if b == nil { - throw CSVError.MemoryAllocationFailed - } - self.buffer = UnsafeMutablePointer(b) - self.bufferOffset = 0 - - self.fieldBuffer = NSMutableData() - - if stream.streamStatus == .NotOpen { - stream.open() - } - if stream.streamStatus != .Open { - throw CSVError.StreamError - } - - self.lastReadCount = stream.read(self.buffer, maxLength: bufferSize) - - var e = encoding - - switch encoding { - case NSUTF16StringEncoding, - NSUTF16BigEndianStringEncoding, - NSUTF16LittleEndianStringEncoding: - - charWidth = 2 - if encoding == NSUTF16StringEncoding { - let nativeEndian = IsBigEndian() - ? NSUTF16BigEndianStringEncoding - : NSUTF16LittleEndianStringEncoding - e = nativeEndian - if lastReadCount >= charWidth { - if memcmp(buffer, utf16BigEndianBOM, charWidth) == 0 { - e = NSUTF16BigEndianStringEncoding - self.bufferOffset += charWidth - } - else if memcmp(buffer, utf16LittleEndianBOM, charWidth) == 0 { - e = NSUTF16LittleEndianStringEncoding - self.bufferOffset += charWidth - } - } - } - - case NSUTF32StringEncoding, - NSUTF32BigEndianStringEncoding, - NSUTF32LittleEndianStringEncoding: - - charWidth = 4 - if encoding == NSUTF32StringEncoding { - let nativeEndian = IsBigEndian() - ? NSUTF32BigEndianStringEncoding - : NSUTF32LittleEndianStringEncoding - e = nativeEndian - if lastReadCount >= charWidth { - if memcmp(buffer, utf32BigEndianBOM, charWidth) == 0 { - e = NSUTF32BigEndianStringEncoding - self.bufferOffset += charWidth - } - else if memcmp(buffer, utf32LittleEndianBOM, charWidth) == 0 { - e = NSUTF32LittleEndianStringEncoding - self.bufferOffset += charWidth - } - } - } - - default: - charWidth = 1 - if encoding == NSUTF8StringEncoding { - let bomSize = 3 - if lastReadCount >= bomSize { - if memcmp(buffer, utf8BOM, charWidth) == 0 { - self.bufferOffset += bomSize - } - } - } - } - - self.encoding = e + self.iterator = AnyIterator(base: iterator) + self.delimiter = delimiter if hasHeaderRow { - guard let nextRow = next() else { - throw CSVError.HeaderReadError + guard let headerRow = next() else { + throw CSVError.CannotReadHeaderRow } - _headerRow = nextRow - currentRow = nil + _headerRow = headerRow } } - deinit { - close() + /// Create an instance with `InputStream`. + /// + /// - parameter stream: An `InputStream` object. If the stream is not open, initializer opens automatically. + /// - parameter codecType: A `UnicodeCodec` type for `stream`. + /// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`. + /// - parameter delimiter: Default: `","`. + public init( + stream: NSInputStream, + codecType: T.Type, + hasHeaderRow: Bool = defaultHasHeaderRow, + delimiter: UnicodeScalar = defaultDelimiter) + throws + { + let reader = try BinaryReader(stream: stream, endian: .Unknown, closeOnDeinit: true) + let iterator = UnicodeIterator(input: reader.makeUInt8Iterator(), inputEncodingType: codecType) + try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, delimiter: delimiter) } - /** - Close stream. - */ - public func close() { - if !closed { - stream.close() - if buffer != nil { - free(buffer) - buffer = nil - } - closed = true - } + /// Create an instance with `InputStream`. + /// + /// - parameter stream: An `InputStream` object. If the stream is not open, initializer opens automatically. + /// - parameter codecType: A `UnicodeCodec` type for `stream`. + /// - 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 delimiter: Default: `","`. + public init( + stream: NSInputStream, + codecType: T.Type, + endian: Endian = .Big, + hasHeaderRow: Bool = defaultHasHeaderRow, + delimiter: UnicodeScalar = defaultDelimiter) + throws + { + let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true) + let iterator = UnicodeIterator(input: reader.makeUInt16Iterator(), inputEncodingType: codecType) + try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, delimiter: delimiter) } - // MARK: GeneratorType + /// Create an instance with `InputStream`. + /// + /// - parameter stream: An `InputStream` object. If the stream is not open, initializer opens automatically. + /// - parameter codecType: A `UnicodeCodec` type for `stream`. + /// - 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 delimiter: Default: `","`. + public init( + stream: NSInputStream, + codecType: T.Type, + endian: Endian = .Big, + hasHeaderRow: Bool = defaultHasHeaderRow, + delimiter: UnicodeScalar = defaultDelimiter) + throws + { + 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, delimiter: delimiter) + } - public func next() -> [String]? { - fieldBuffer.length = 0 + // MARK: IteratorProtocol + + /// Advances and returns the next element of the underlying sequence, or + /// `nil` if no next element exists. + /// + /// Repeatedly calling this method returns, in order, all the elements of the + /// underlying sequence. After the sequence has run out of elements, the + /// `next()` method returns `nil`. + /// + /// You must not call this method if it has previously returned `nil` or if + /// any other copy of this iterator has been advanced with a call to its + /// `next()` method. + /// + /// The following example shows how an iterator can be used explicitly to + /// emulate a `for`-`in` loop. First, retrieve a sequence's iterator, and + /// then call the iterator's `next()` method until it returns `nil`. + /// + /// let numbers = [2, 3, 5, 7] + /// var numbersIterator = numbers.makeIterator() + /// + /// while let num = numbersIterator.next() { + /// print(num) + /// } + /// // Prints "2" + /// // Prints "3" + /// // Prints "5" + /// // Prints "7" + /// + /// - Returns: The next element in the underlying sequence if a next element + /// exists; otherwise, `nil`. + public mutating func next() -> [String]? { + return readRow() + } + + internal mutating func moveNext() -> UnicodeScalar? { + if back != nil { + defer { back = nil } + return back + } + return iterator.next() + } + + internal mutating func readRow() -> [String]? { currentRow = nil + + var next = moveNext() + if next == nil { + return nil + } - if closed { - return nil - } - if lastReadCount <= 0 { - return nil - } - - var fields = [String]() - - var fieldStart = bufferOffset - var charLength = 0 - var escaping = false - var quotationCount = 0 - - var prev: UInt32 = 0 - + var row = [String]() + var field: String + var end: Bool while true { - if bufferOffset >= lastReadCount { - if charLength > 0 { - fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength) - } - bufferOffset = 0 - fieldStart = 0 - charLength = 0 - - lastReadCount = stream.read(buffer, maxLength: bufferSize) - if lastReadCount < 0 { - // bad end - lastError = CSVError.StreamError - return nil - } - if lastReadCount == 0 { - // true end - break - } + if next == nil { + (field, end) = ("", true) } - - var c: UInt32 = 0 - - switch encoding { - case NSUTF16BigEndianStringEncoding: - let _c = ReadBigInt16(buffer, byteOffset: bufferOffset) - c = UInt32(_c) - - case NSUTF16LittleEndianStringEncoding: - let _c = ReadLittleInt16(buffer, byteOffset: bufferOffset) - c = UInt32(_c) - - case NSUTF32BigEndianStringEncoding: - c = ReadBigInt32(buffer, byteOffset: bufferOffset) - - case NSUTF32LittleEndianStringEncoding: - c = ReadLittleInt32(buffer, byteOffset: bufferOffset) - - default: // multi-byte character encodings - let _c = (buffer + bufferOffset)[0] - c = UInt32(_c) - } - - if c == DQUOTE { - quotationCount += 1 - } - - if c == DQUOTE && charLength == 0 { - escaping = true - } - - if escaping && prev == DQUOTE && (c == delimiter || c == CR || c == LF) && (quotationCount % 2 == 0) { - escaping = false - } - - if !escaping && prev == CR && c != LF { - fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength) - break - } - - prev = c - bufferOffset += charWidth - - if !escaping { - if c == CR { - continue - } - if c == LF { - fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength) - break - } - } - - // フィールドの終わり - if !escaping && c == delimiter { - fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength) - - guard let field = getField(quotationCount) else { - return nil - } - fields.append(field) - - // reset - fieldBuffer.length = 0 - quotationCount = 0 - charLength = 0 - - fieldStart = bufferOffset + else if next == DQUOTE { + (field, end) = readField(quoted: true) } else { - charLength += 1 + back = next + (field, end) = readField(quoted: false) } + row.append(field) + if end { + break + } + next = moveNext() } - guard let field = getField(quotationCount) else { - return nil - } - - // 最後の空行 - if isBufferEOF && fields.count == 0 && field.isEmpty { - return nil - } - - fields.append(field) - currentRow = fields - - return fields + currentRow = row + return row } - // MARK: Utility + internal mutating func readField(quoted quoted: Bool) -> (String, Bool) { + var field = "" - private var isBufferEOF: Bool { - if stream.hasBytesAvailable { - return false + var next = moveNext() + while let c = next { + if quoted { + if c == DQUOTE { + let cNext = moveNext() + if cNext == nil || cNext == CR || cNext == LF { + if cNext == CR { + let cNextNext = moveNext() + if cNextNext != LF { + back = cNextNext + } + } + // END ROW + return (field, true) + } + else if cNext == delimiter { + // END FIELD + return (field, false) + } + else if cNext == DQUOTE { + // ESC + field.append(DQUOTE) + } + else { + // ERROR?? + field.append(c) + } + } + else { + field.append(c) + } + } + else { + if c == CR || c == LF { + if c == CR { + let cNext = moveNext() + if cNext != LF { + back = cNext + } + } + // END ROW + return (field, true) + } + else if c == delimiter { + // END FIELD + return (field, false) + } + else { + field.append(c) + } + } + + next = moveNext() } - return bufferOffset >= (lastReadCount - 1) + + return (field, true) } - - private func getField(quotationCount: Int) -> String? { - guard var field = String(data: fieldBuffer, encoding: encoding) else { - lastError = CSVError.StringEncodingMismatch - return nil - } - - if quotationCount >= 2 - && field.hasPrefix("\"") - && field.hasSuffix("\"") - { - //let start = field.index(field.startIndex, offsetBy: 1) - //let end = field.index(field.endIndex, offsetBy: -1) - let start = field.startIndex.advancedBy(1) - let end = field.endIndex.advancedBy(-1) - field = field[start..= 4 { - field = field.stringByReplacingOccurrencesOfString("\"\"", withString: "\"") - } - - return field - } - + } diff --git a/Sources/CSVError.swift b/Sources/CSVError.swift index fd5c25b..e97a9f3 100644 --- a/Sources/CSVError.swift +++ b/Sources/CSVError.swift @@ -3,15 +3,16 @@ // CSV // // Created by Yasuhiro Hatta on 2016/06/11. -// +// Copyright © 2016 yaslab. All rights reserved. // import Foundation public enum CSVError: ErrorType { - case ParameterError - case StreamError - case HeaderReadError - case MemoryAllocationFailed + case CannotOpenFile + case CannotReadFile + case StreamErrorHasOccurred(error: NSError) + case CannotReadHeaderRow case StringEncodingMismatch + case StringEndianMismatch } diff --git a/Sources/CSVVersion.h b/Sources/CSVVersion.h old mode 100644 new mode 100755 index adcf904..cb395da --- a/Sources/CSVVersion.h +++ b/Sources/CSVVersion.h @@ -3,7 +3,7 @@ // CSV // // Created by Yasuhiro Hatta on 2016/06/11. -// +// Copyright © 2016 yaslab. All rights reserved. // @import Foundation; diff --git a/Sources/Endian.swift b/Sources/Endian.swift new file mode 100644 index 0000000..770ff42 --- /dev/null +++ b/Sources/Endian.swift @@ -0,0 +1,13 @@ +// +// Endian.swift +// CSV +// +// Created by Yasuhiro Hatta on 2016/06/21. +// Copyright © 2016 yaslab. All rights reserved. +// + +public enum Endian { + case Big + case Little + case Unknown +} diff --git a/Sources/Info.plist b/Sources/Info.plist index 6019f05..f42ccf5 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.2.0 + 0.3.0 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/UnicodeIterator.swift b/Sources/UnicodeIterator.swift new file mode 100755 index 0000000..ab117af --- /dev/null +++ b/Sources/UnicodeIterator.swift @@ -0,0 +1,31 @@ +// +// UnicodeIterator.swift +// CSV +// +// Created by Yasuhiro Hatta on 2016/06/20. +// Copyright © 2016 yaslab. All rights reserved. +// + +internal struct UnicodeIterator< + Input: GeneratorType, + InputEncoding: UnicodeCodecType + where InputEncoding.CodeUnit == Input.Element> + : GeneratorType { + + private var input: Input + private var inputEncoding: InputEncoding + + internal init(input: Input, inputEncodingType: InputEncoding.Type) { + self.input = input + self.inputEncoding = inputEncodingType.init() + } + + internal mutating func next() -> UnicodeScalar? { + switch inputEncoding.decode(&input) { + case .Result(let c): return c + case .EmptyInput: return nil + case .Error: return nil + } + } + +} diff --git a/Tests/CSV/CSVReaderTests.swift b/Tests/CSV/CSVReaderTests.swift index e5a2917..f6de148 100644 --- a/Tests/CSV/CSVReaderTests.swift +++ b/Tests/CSV/CSVReaderTests.swift @@ -13,69 +13,60 @@ class CSVReaderTests: XCTestCase { func test1Line() { let csv = "abab,cdcd,efef" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) } func testQuoted() { let csv = "abab,\"cdcd\",efef" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) } func testLF() { let csv = "abab,cdcd,efef\nzxcv,asdf,qwer" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"]) } func testCommaInQuotationMarks() { let csv = "abab,\"cd,cd\",efef" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "cd,cd", "efef"]) } func testCRLF() { let csv = "abab,cdcd,efef\r\nzxcv,asdf,qwer" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"]) } - func testEscapedQuotationMark() { + func testEscapedQuotationMark1() { let csv = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "\"cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"]) } - func testQuotationMark2() { + func testEscapedQuotationMark2() { let csv = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er"]) } func testEmptyField() { let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) } func testLastCR() { let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 2) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) @@ -83,8 +74,7 @@ class CSVReaderTests: XCTestCase { func testLastCRLF() { let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r\n" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 2) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) @@ -92,8 +82,7 @@ class CSVReaderTests: XCTestCase { func testLastLF() { let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\n" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 2) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) @@ -101,8 +90,7 @@ class CSVReaderTests: XCTestCase { func testLFInQuotationMarks() { let csv = "abab,,\"\rcdcd\n\",efef\r\nzxcv,asdf,\"qw\"\"er\",\n" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 2) XCTAssertEqual(records[0], ["abab", "", "\rcdcd\n", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) @@ -110,8 +98,7 @@ class CSVReaderTests: XCTestCase { func testLineBreakLF() { let csv = "qwe,asd\nzxc,rty" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 2) XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[1], ["zxc", "rty"]) @@ -119,8 +106,7 @@ class CSVReaderTests: XCTestCase { func testLineBreakCR() { let csv = "qwe,asd\rzxc,rty" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 2) XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[1], ["zxc", "rty"]) @@ -128,8 +114,7 @@ class CSVReaderTests: XCTestCase { func testLineBreakCRLF() { let csv = "qwe,asd\r\nzxc,rty" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 2) XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[1], ["zxc", "rty"]) @@ -137,8 +122,7 @@ class CSVReaderTests: XCTestCase { func testLineBreakLFLF() { let csv = "qwe,asd\n\nzxc,rty" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 3) XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[1], [""]) @@ -147,8 +131,7 @@ class CSVReaderTests: XCTestCase { func testLineBreakCRCR() { let csv = "qwe,asd\r\rzxc,rty" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 3) XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[1], [""]) @@ -157,125 +140,159 @@ class CSVReaderTests: XCTestCase { func testLineBreakCRLFCRLF() { let csv = "qwe,asd\r\n\r\nzxc,rty" - let encoding = NSUTF8StringEncoding - let records = parseCSV(csv, encoding: encoding) + let records = parse(csv: csv) XCTAssertEqual(records.count, 3) XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[1], [""]) XCTAssertEqual(records[2], ["zxc", "rty"]) } - func testEncodingWithoutBOM() { - var index = 0 - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," - for encoding in allEncodings() { - print("index: \(index)") - let records = parseCSV(csv, encoding: encoding) - XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) - XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) - index += 1 - } - } +// func testEncodingWithoutBOM() { +// var index = 0 +// let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," +// for encoding in allEncodings() { +// print("index: \(index)") +// let records = parse(csv: csv, encoding: encoding) +// XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) +// XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) +// index += 1 +// } +// } func testUTF8WithBOM() { - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," + let csvString = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let encoding = NSUTF8StringEncoding - let mutableData = NSMutableData() + var mutableData = NSMutableData() mutableData.appendBytes(utf8BOM, length: utf8BOM.count) - mutableData.appendData(csv.dataUsingEncoding(encoding)!) - let records = parseData(mutableData, encoding: encoding) + mutableData.appendData(csvString.dataUsingEncoding(encoding)!) + let stream = NSInputStream(data: mutableData) + let csv = try! CSV(stream: stream, codecType: UTF8.self) + let records = getRecords(csv: csv) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) } func testUTF16WithNativeEndianBOM() { - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," + let csvString = "abab,,cdcd,efef\r\nzxcv,😆asdf,\"qw\"\"er\"," let encoding = NSUTF16StringEncoding - let records = parseCSV(csv, encoding: encoding) + var mutableData = NSMutableData() + mutableData.appendData(csvString.dataUsingEncoding(encoding)!) + let stream = NSInputStream(data: mutableData) + let csv = try! CSV(stream: stream, codecType: UTF16.self, endian: .Unknown) + let records = getRecords(csv: csv) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) - XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) + XCTAssertEqual(records[1], ["zxcv", "😆asdf", "qw\"er", ""]) } func testUTF16WithBigEndianBOM() { - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," - let encoding = NSUTF16StringEncoding - let mutableData = NSMutableData() + let csvString = "abab,,cdcd,efef\r\n😆zxcv,asdf,\"qw\"\"er\"," + let encoding = NSUTF16BigEndianStringEncoding + var mutableData = NSMutableData() mutableData.appendBytes(utf16BigEndianBOM, length: utf16BigEndianBOM.count) - mutableData.appendData(csv.dataUsingEncoding(NSUTF16BigEndianStringEncoding)!) - let records = parseData(mutableData, encoding: encoding) + mutableData.appendData(csvString.dataUsingEncoding(encoding)!) + let stream = NSInputStream(data: mutableData) + let csv = try! CSV(stream: stream, codecType: UTF16.self, endian: .Big) + let records = getRecords(csv: csv) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) - XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) + XCTAssertEqual(records[1], ["😆zxcv", "asdf", "qw\"er", ""]) } func testUTF16WithLittleEndianBOM() { - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," - let encoding = NSUTF16StringEncoding - let mutableData = NSMutableData() + let csvString = "abab,,cdcd,efef\r\nzxcv😆,asdf,\"qw\"\"er\"," + let encoding = NSUTF16LittleEndianStringEncoding + var mutableData = NSMutableData() mutableData.appendBytes(utf16LittleEndianBOM, length: utf16LittleEndianBOM.count) - mutableData.appendData(csv.dataUsingEncoding(NSUTF16LittleEndianStringEncoding)!) - let records = parseData(mutableData, encoding: encoding) + mutableData.appendData(csvString.dataUsingEncoding(encoding)!) + let stream = NSInputStream(data: mutableData) + let csv = try! CSV(stream: stream, codecType: UTF16.self, endian: .Little) + let records = getRecords(csv: csv) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) - XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) + XCTAssertEqual(records[1], ["zxcv😆", "asdf", "qw\"er", ""]) } func testUTF32WithNativeEndianBOM() { - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," + let csvString = "😆abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let encoding = NSUTF32StringEncoding - let records = parseCSV(csv, encoding: encoding) - XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) + var mutableData = NSMutableData() + mutableData.appendData(csvString.dataUsingEncoding(encoding)!) + let stream = NSInputStream(data: mutableData) + let csv = try! CSV(stream: stream, codecType: UTF32.self, endian: .Unknown) + let records = getRecords(csv: csv) + XCTAssertEqual(records[0], ["😆abab", "", "cdcd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) } func testUTF32WithBigEndianBOM() { - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," - let encoding = NSUTF32StringEncoding - let mutableData = NSMutableData() + let csvString = "abab,,cd😆cd,efef\r\nzxcv,asdf,\"qw\"\"er\"," + let encoding = NSUTF32BigEndianStringEncoding + var mutableData = NSMutableData() mutableData.appendBytes(utf32BigEndianBOM, length: utf32BigEndianBOM.count) - mutableData.appendData(csv.dataUsingEncoding(NSUTF32BigEndianStringEncoding)!) - let records = parseData(mutableData, encoding: encoding) - XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) + mutableData.appendData(csvString.dataUsingEncoding(encoding)!) + let stream = NSInputStream(data: mutableData) + let csv = try! CSV(stream: stream, codecType: UTF32.self, endian: .Big) + let records = getRecords(csv: csv) + XCTAssertEqual(records[0], ["abab", "", "cd😆cd", "efef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) } func testUTF32WithLittleEndianBOM() { - let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," - let encoding = NSUTF32StringEncoding - let mutableData = NSMutableData() + let csvString = "abab,,cdcd,ef😆ef\r\nzxcv,asdf,\"qw\"\"er\"," + let encoding = NSUTF32LittleEndianStringEncoding + var mutableData = NSMutableData() mutableData.appendBytes(utf32LittleEndianBOM, length: utf32LittleEndianBOM.count) - mutableData.appendData(csv.dataUsingEncoding(NSUTF32LittleEndianStringEncoding)!) - let records = parseData(mutableData, encoding: encoding) - XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) + mutableData.appendData(csvString.dataUsingEncoding(encoding)!) + let stream = NSInputStream(data: mutableData) + let csv = try! CSV(stream: stream, codecType: UTF32.self, endian: .Little) + let records = getRecords(csv: csv) + XCTAssertEqual(records[0], ["abab", "", "cdcd", "ef😆ef"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) } - func allEncodings() -> [NSStringEncoding] { - return [ - // multi-byte character encodings - NSShiftJISStringEncoding, - NSJapaneseEUCStringEncoding, - NSUTF8StringEncoding, - // wide character encodings - NSUTF16BigEndianStringEncoding, - NSUTF16LittleEndianStringEncoding, - NSUTF32BigEndianStringEncoding, - NSUTF32LittleEndianStringEncoding, - ] - } - - func parseCSV(csv: String, encoding: NSStringEncoding) -> [[String]] { - let data = csv.dataUsingEncoding(encoding)! - return parseData(data, encoding: encoding) - } - - func parseData(data: NSData, encoding: NSStringEncoding) -> [[String]] { - let stream = NSInputStream(data: data) - let reader = try! CSV(stream: stream, encoding: encoding) +// func allEncodings() -> [String.Encoding] { +// return [ +// // multi-byte character encodings +// //String.Encoding.shiftJIS, +// //String.Encoding.japaneseEUC, +// String.Encoding.utf8, +// // wide character encodings +// String.Encoding.utf16BigEndian, +// String.Encoding.utf16LittleEndian, +// String.Encoding.utf32BigEndian, +// String.Encoding.utf32LittleEndian, +// ] +// } + + func parse(csv csv: String) -> [[String]] { + let reader = try! CSV(string: csv) var records = [[String]]() for row in reader { records.append(row) } return records } + + func getRecords(csv csv: CSV) -> [[String]] { + var records = [[String]]() + for row in csv { + records.append(row) + } + return records + } + +// func parse(csv: String, encoding: String.Encoding) -> [[String]] { +// let data = csv.data(using: encoding)! +// return parse(data: data, encoding: encoding) +// } +// +// func parse(data: Data, encoding: String.Encoding) -> [[String]] { +// let stream = InputStream(data: data) +// let reader = try! CSV(stream: stream, encoding: encoding) +// var records = [[String]]() +// for row in reader { +// records.append(row) +// } +// return records +// } static var allTests : [(String, (CSVReaderTests) -> () throws -> Void)] { return [ diff --git a/Tests/CSV/CSVTests.swift b/Tests/CSV/CSVTests.swift old mode 100644 new mode 100755 index fd97491..0017fd0 --- a/Tests/CSV/CSVTests.swift +++ b/Tests/CSV/CSVTests.swift @@ -97,53 +97,53 @@ class CSVTests: XCTestCase { XCTAssertEqual(i, 3) } - func testBufferSizeMod0() { - let csvString = "0,1,2,3,4,5,6,7,8,9\n" - let csv = try! CSV(string: csvString, bufferSize: 12) - XCTAssertEqual(csv.bufferSize, 12) - } - - func testBufferSizeMod1() { - let csvString = "0,1,2,3,4,5,6,7,8,9\n" - let csv = try! CSV(string: csvString, bufferSize: 13) - XCTAssertEqual(csv.bufferSize, 16) - } - - func testBufferSizeMod2() { - let csvString = "0,1,2,3,4,5,6,7,8,9\n" - let csv = try! CSV(string: csvString, bufferSize: 14) - XCTAssertEqual(csv.bufferSize, 16) - } - - func testBufferSizeMod3() { - let csvString = "0,1,2,3,4,5,6,7,8,9\n" - let csv = try! CSV(string: csvString, bufferSize: 15) - XCTAssertEqual(csv.bufferSize, 16) - } - - func testBufferSizeMod4() { - let csvString = "0,1,2,3,4,5,6,7,8,9\n" - let csv = try! CSV(string: csvString, bufferSize: 16) - XCTAssertEqual(csv.bufferSize, 16) - } - - func testBigDataAndSmallBufferSize() { - let line = "0,1,2,3,4,5,6,7,8,9\n" - var csv = "" - for _ in 0..<10000 { - csv += line - } - var i = 0 - for row in try! CSV(string: csv, bufferSize: 10) { - XCTAssertEqual(row, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]) - i += 1 - } - XCTAssertEqual(i, 10000) - } - +// func testBufferSizeMod0() { +// let csvString = "0,1,2,3,4,5,6,7,8,9\n" +// let csv = try! CSV(string: csvString, bufferSize: 12) +// XCTAssertEqual(csv.bufferSize, 12) +// } +// +// func testBufferSizeMod1() { +// let csvString = "0,1,2,3,4,5,6,7,8,9\n" +// let csv = try! CSV(string: csvString, bufferSize: 13) +// XCTAssertEqual(csv.bufferSize, 16) +// } +// +// func testBufferSizeMod2() { +// let csvString = "0,1,2,3,4,5,6,7,8,9\n" +// let csv = try! CSV(string: csvString, bufferSize: 14) +// XCTAssertEqual(csv.bufferSize, 16) +// } +// +// func testBufferSizeMod3() { +// let csvString = "0,1,2,3,4,5,6,7,8,9\n" +// let csv = try! CSV(string: csvString, bufferSize: 15) +// XCTAssertEqual(csv.bufferSize, 16) +// } +// +// func testBufferSizeMod4() { +// let csvString = "0,1,2,3,4,5,6,7,8,9\n" +// let csv = try! CSV(string: csvString, bufferSize: 16) +// XCTAssertEqual(csv.bufferSize, 16) +// } +// +// func testBigDataAndSmallBufferSize() { +// let line = "0,1,2,3,4,5,6,7,8,9\n" +// var csv = "" +// for _ in 0..<10000 { +// csv += line +// } +// var i = 0 +// for row in try! CSV(string: csv, bufferSize: 10) { +// XCTAssertEqual(row, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]) +// i += 1 +// } +// XCTAssertEqual(i, 10000) +// } + func testSubscript() { let csvString = "id,name\n001,hoge\n002,fuga" - let csv = try! CSV(string: csvString, hasHeaderRow: true) + var csv = try! CSV(string: csvString, hasHeaderRow: true) var i = 0 while csv.next() != nil { switch i { @@ -161,4 +161,18 @@ class CSVTests: XCTestCase { XCTAssertEqual(i, 2) } + func testCSVState1() { + let it = "あ,い1,\"う\",えお\n,,x,".unicodeScalars.generate() + var csv = try! CSV(iterator: it, hasHeaderRow: defaultHasHeaderRow, delimiter: defaultDelimiter) + + var rows = [[String]]() + + while let row = csv.next() { + rows.append(row) + } + XCTAssertEqual(rows.count, 2) + XCTAssertEqual(rows[0], ["あ", "い1", "う", "えお"]) + XCTAssertEqual(rows[1], ["", "", "x", ""]) + } + } diff --git a/Tests/CSV/ReadmeTests.swift b/Tests/CSV/ReadmeTests.swift old mode 100644 new mode 100755 index 14dd8eb..8ef8bde --- a/Tests/CSV/ReadmeTests.swift +++ b/Tests/CSV/ReadmeTests.swift @@ -20,9 +20,10 @@ class ReadmeTests: XCTestCase { } func testFromFilePath() { - //for row in try! CSV(path: "/path/to/file.csv") { - // print("\(row)") - //} +// let stream = NSInputStream(fileAtPath: "/path/to/file.csv")! +// for row in try! CSV(stream: stream) { +// print("\(row)") +// } } func testGettingTheHeaderRow() { @@ -41,7 +42,7 @@ class ReadmeTests: XCTestCase { } func testGetTheFieldValueUsingSubscript() { - let csv = try! CSV( + var csv = try! CSV( string: "id,name\n1,foo", hasHeaderRow: true) // It must be true. @@ -52,9 +53,10 @@ class ReadmeTests: XCTestCase { } func testProvideTheCharacterEncoding() { - //let csv = try! CSV( - // path: "/path/to/file.csv", - // encoding: NSUTF8StringEncoding) +// let csv = try! CSV( +// stream: NSInputStream(fileAtPath: "/path/to/file.csv")!, +// codecType: UTF16.self, +// endian: .Big) } }