Merge pull request #4 from yaslab/swift-2.2

This commit is contained in:
Yasuhiro Hatta 2016-08-21 10:59:55 +09:00 committed by GitHub
commit 7cd1d968dc
20 changed files with 786 additions and 588 deletions

65
CSV.xcodeproj/project.pbxproj Normal file → Executable file
View File

@ -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 = "<group>"; };
0E0F160D1D197DB800C92580 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endian.swift; sourceTree = "<group>"; };
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 = "<group>"; };
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = "<group>"; };
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVError.swift; sourceTree = "<group>"; };
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSVVersion.h; sourceTree = "<group>"; };
@ -88,6 +101,8 @@
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>"; };
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>"; };
/* 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 = "<group>";
@ -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;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -23,7 +23,8 @@ for row in try! CSV(string: "1,foo\n2,bar") {
```swift
import CSV
for row in try! CSV(path: "/path/to/file.csv") {
let stream = NSInputStream(fileAtPath: "/path/to/file.csv")!
for row in try! CSV(stream: stream) {
print("\(row)")
}
```
@ -64,8 +65,9 @@ If you use a file path, you can provide the character encoding to initializer.
```swift
let csv = try! CSV(
path: "/path/to/file.csv",
encoding: NSUTF8StringEncoding)
stream: NSInputStream(fileAtPath: "/path/to/file.csv")!,
codecType: UTF16.self,
endian: .Big)
```
## Installation
@ -73,13 +75,13 @@ let csv = try! CSV(
### CocoaPods
```ruby
pod 'CSV.swift', '~> 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)
]
)
```

22
Sources/AnyIterator.swift Normal file
View File

@ -0,0 +1,22 @@
//
// AnyIterator.swift
// CSV
//
// Created by Yasuhiro Hatta on 2016/06/21.
// Copyright © 2016 yaslab. All rights reserved.
//
internal struct AnyIterator<T>: GeneratorType {
private var _base_next: (() -> T?)
internal init<U: GeneratorType where U.Element == T>(base: U) {
var base = base
_base_next = { base.next() }
}
internal mutating func next() -> T? {
return _base_next()
}
}

247
Sources/BinaryReader.swift Executable file
View File

@ -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<UInt8>, 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<UInt8>, 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<UInt16>(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<UInt32>(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)
}
}

View File

@ -1,37 +0,0 @@
//
// ByteOrder.swift
// CSV
//
// Created by Yasuhiro Hatta on 2016/06/11.
//
//
import CoreFoundation
internal func ReadBigInt16(base: UnsafePointer<Void>, byteOffset: Int) -> UInt16 {
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
let int16Array = UnsafePointer<UInt16>(bytes)
return CFSwapInt16BigToHost(int16Array[0])
}
internal func ReadBigInt32(base: UnsafePointer<Void>, byteOffset: Int) -> UInt32 {
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
let int32Array = UnsafePointer<UInt32>(bytes)
return CFSwapInt32BigToHost(int32Array[0])
}
internal func ReadLittleInt16(base: UnsafePointer<Void>, byteOffset: Int) -> UInt16 {
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
let int16Array = UnsafePointer<UInt16>(bytes)
return CFSwapInt16LittleToHost(int16Array[0])
}
internal func ReadLittleInt32(base: UnsafePointer<Void>, byteOffset: Int) -> UInt32 {
let bytes = UnsafePointer<UInt8>(base).advancedBy(byteOffset)
let int32Array = UnsafePointer<UInt32>(bytes)
return CFSwapInt32LittleToHost(int32Array[0])
}
internal func IsBigEndian() -> Bool {
return CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderBigEndian.rawValue)
}

60
Sources/CSV+init.swift Normal file → Executable file
View File

@ -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)
}
}

7
Sources/CSV+subscript.swift Normal file → Executable file
View File

@ -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
}

508
Sources/CSV.swift Normal file → Executable file
View File

@ -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<UnicodeScalar>
private let delimiter: UnicodeScalar
internal let stream: NSInputStream
internal let encoding: NSStringEncoding
internal let delimiter: UInt32
internal let bufferSize: Int
internal var buffer: UnsafeMutablePointer<UInt8>!
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<T: GeneratorType where T.Element == UnicodeScalar>(
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<UInt8>(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<T: UnicodeCodecType where T.CodeUnit == UInt8>(
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<T: UnicodeCodecType where T.CodeUnit == UInt16>(
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<T: UnicodeCodecType where T.CodeUnit == UInt32>(
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..<end]
}
if quotationCount >= 4 {
field = field.stringByReplacingOccurrencesOfString("\"\"", withString: "\"")
}
return field
}
}

View File

@ -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
}

2
Sources/CSVVersion.h Normal file → Executable file
View File

@ -3,7 +3,7 @@
// CSV
//
// Created by Yasuhiro Hatta on 2016/06/11.
//
// Copyright © 2016 yaslab. All rights reserved.
//
@import Foundation;

13
Sources/Endian.swift Normal file
View File

@ -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
}

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.2.0</string>
<string>0.3.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

31
Sources/UnicodeIterator.swift Executable file
View File

@ -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
}
}
}

View File

@ -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 [

104
Tests/CSV/CSVTests.swift Normal file → Executable file
View File

@ -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", ""])
}
}

16
Tests/CSV/ReadmeTests.swift Normal file → Executable file
View File

@ -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)
}
}