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 = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; 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 */; }; 0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; }; 0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */; };
0E7E8CA31D0BC7F10057A1C1 /* CSVVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; 0E7E8CA91D0BC8050057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; };
0E7E8CAA1D0BC8050057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.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 */; }; 0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.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, ); }; }; 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 */; }; 0E7E8CD01D0BCA2A0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8CC61D0BCA2A0057A1C1 /* CSV.framework */; };
0E7E8CDD1D0BCA840057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; }; 0E7E8CDD1D0BCA840057A1C1 /* CSVReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA61D0BC8050057A1C1 /* CSVReaderTests.swift */; };
0E7E8CDE1D0BCA840057A1C1 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8CA71D0BC8050057A1C1 /* CSVTests.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 */; }; 0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.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, ); }; }; 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 */; }; 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 */; }; 0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.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, ); }; }; 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 */; }; 0E9317DE1D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; };
0E9317DF1D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; }; 0E9317DF1D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; };
0E9317E01D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; }; 0E9317E01D0DBCC500AC20A0 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */; };
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 */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -70,9 +82,10 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference 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; }; 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+subscript.swift"; sourceTree = "<group>"; };
0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = "<group>"; }; 0E9317DD1D0DBCC500AC20A0 /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = "<group>"; };
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = "<group>"; };
0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeIterator.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -172,13 +187,16 @@
0E7E8C9B1D0BC7F10057A1C1 /* Sources */ = { 0E7E8C9B1D0BC7F10057A1C1 /* Sources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0E7E8C9C1D0BC7F10057A1C1 /* ByteOrder.swift */, 0E0F16081D197D6000C92580 /* AnyIterator.swift */,
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */,
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */, 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */,
0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */, 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */,
0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */, 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */,
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */, 0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */,
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */, 0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */,
0E0F160D1D197DB800C92580 /* Endian.swift */,
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */, 0E7E8CAC1D0BC8610057A1C1 /* Info.plist */,
0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */,
); );
path = Sources; path = Sources;
sourceTree = "<group>"; sourceTree = "<group>";
@ -373,7 +391,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0730; LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730; LastUpgradeCheck = 0800;
ORGANIZATIONNAME = yaslab; ORGANIZATIONNAME = yaslab;
TargetAttributes = { TargetAttributes = {
0E7E8C801D0BC7BB0057A1C1 = { 0E7E8C801D0BC7BB0057A1C1 = {
@ -387,9 +405,11 @@
}; };
0E7E8CC51D0BCA2A0057A1C1 = { 0E7E8CC51D0BCA2A0057A1C1 = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0800;
}; };
0E7E8CCE1D0BCA2A0057A1C1 = { 0E7E8CCE1D0BCA2A0057A1C1 = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0800;
}; };
0E7E8CE71D0BCD0B0057A1C1 = { 0E7E8CE71D0BCD0B0057A1C1 = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
@ -480,10 +500,13 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0E0F160A1D197D6000C92580 /* AnyIterator.swift in Sources */,
0EA2AB821D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317DA1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E9317DA1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */, 0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */,
0E0F160F1D197DB800C92580 /* Endian.swift in Sources */,
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */, 0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */,
0E7E8CA01D0BC7F10057A1C1 /* ByteOrder.swift in Sources */, 0EA2AB7D1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -502,10 +525,13 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0E0F160C1D197D6000C92580 /* AnyIterator.swift in Sources */,
0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317DC1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E9317DC1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */, 0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */,
0E0F16111D197DB800C92580 /* Endian.swift in Sources */,
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */, 0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */,
0E7E8CBD1D0BC9D70057A1C1 /* ByteOrder.swift in Sources */, 0EA2AB7F1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -514,10 +540,13 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E9317D41D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D41D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0E0F16091D197D6000C92580 /* AnyIterator.swift in Sources */,
0EA2AB811D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317D91D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E9317D91D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */, 0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */,
0E0F160E1D197DB800C92580 /* Endian.swift in Sources */,
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */, 0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */,
0E7E8CDF1D0BCA8E0057A1C1 /* ByteOrder.swift in Sources */, 0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -536,10 +565,13 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */, 0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
0E0F160B1D197D6000C92580 /* AnyIterator.swift in Sources */,
0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
0E9317DB1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */, 0E9317DB1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */, 0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */,
0E0F16101D197DB800C92580 /* Endian.swift in Sources */,
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */, 0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */,
0E7E8CFF1D0BCDCF0057A1C1 /* ByteOrder.swift in Sources */, 0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -696,6 +728,7 @@
PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV; PRODUCT_BUNDLE_IDENTIFIER = net.yaslab.CSV;
PRODUCT_NAME = CSV; PRODUCT_NAME = CSV;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
}; };
name = Release; name = Release;
}; };
@ -716,6 +749,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS"; PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-iOS";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
}; };
name = Release; name = Release;
}; };
@ -754,6 +788,7 @@
PRODUCT_NAME = CSV; PRODUCT_NAME = CSV;
SDKROOT = watchos; SDKROOT = watchos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = 4; TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 2.0; WATCHOS_DEPLOYMENT_TARGET = 2.0;
}; };
@ -777,6 +812,7 @@
PRODUCT_NAME = CSV; PRODUCT_NAME = CSV;
SDKROOT = macosx; SDKROOT = macosx;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
}; };
name = Debug; name = Debug;
}; };
@ -798,6 +834,8 @@
PRODUCT_NAME = CSV; PRODUCT_NAME = CSV;
SDKROOT = macosx; SDKROOT = macosx;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
}; };
name = Release; name = Release;
}; };
@ -812,6 +850,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX"; PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_VERSION = 3.0;
}; };
name = Debug; name = Debug;
}; };
@ -826,6 +865,8 @@
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX"; PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-OSX";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
}; };
name = Release; name = Release;
}; };
@ -862,6 +903,7 @@
PRODUCT_NAME = CSV; PRODUCT_NAME = CSV;
SDKROOT = appletvos; SDKROOT = appletvos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = 3; TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
}; };
@ -887,6 +929,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS"; PRODUCT_BUNDLE_IDENTIFIER = "net.yaslab.CSVTests-tvOS";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos; SDKROOT = appletvos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TVOS_DEPLOYMENT_TARGET = 9.2; TVOS_DEPLOYMENT_TARGET = 9.2;
}; };
name = Release; name = Release;

View File

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

View File

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

View File

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

View File

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

View File

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

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

@ -10,61 +10,27 @@ import Foundation
extension CSV { extension CSV {
public convenience init( public init(
path: String, stream: NSInputStream,
hasHeaderRow: Bool = defaultHasHeaderRow, hasHeaderRow: Bool = defaultHasHeaderRow,
encoding: NSStringEncoding = defaultEncoding, delimiter: UnicodeScalar = defaultDelimiter)
delimiter: CChar = defaultDelimiter,
bufferSize: Int = defaultBufferSize)
throws throws
{ {
guard let stream = NSInputStream(fileAtPath: path) else { try self.init(stream: stream, codecType: UTF8.self, hasHeaderRow: hasHeaderRow, delimiter: delimiter)
throw CSVError.StreamError
}
try self.init(
stream: stream,
hasHeaderRow: hasHeaderRow,
encoding: encoding,
delimiter: delimiter,
bufferSize: bufferSize)
} }
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( extension CSV {
public init(
string: String, string: String,
hasHeaderRow: Bool = defaultHasHeaderRow, hasHeaderRow: Bool = defaultHasHeaderRow,
delimiter: CChar = defaultDelimiter, delimiter: UnicodeScalar = defaultDelimiter)
bufferSize: Int = defaultBufferSize)
throws throws
{ {
let encoding = defaultEncoding let iterator = string.unicodeScalars.generate()
guard let data = string.dataUsingEncoding(encoding) else { try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, delimiter: delimiter)
throw CSVError.StringEncodingMismatch
}
try self.init(
stream: NSInputStream(data: data),
hasHeaderRow: hasHeaderRow,
encoding: encoding,
delimiter: delimiter,
bufferSize: bufferSize)
} }
} }

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

@ -6,21 +6,16 @@
// Copyright © 2016 yaslab. All rights reserved. // Copyright © 2016 yaslab. All rights reserved.
// //
import Foundation
extension CSV { extension CSV {
public subscript(key: String) -> String? { public subscript(key: String) -> String? {
get { get {
guard let headerRow = headerRow else { guard let headerRow = headerRow, let currentRow = currentRow else {
return nil return nil
} }
guard let index = headerRow.indexOf(key) else { guard let index = headerRow.indexOf(key) else {
return nil return nil
} }
guard let currentRow = currentRow else {
return nil
}
if index >= currentRow.count { if index >= currentRow.count {
return nil return nil
} }

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

@ -3,358 +3,240 @@
// CSV // CSV
// //
// Created by Yasuhiro Hatta on 2016/06/11. // Created by Yasuhiro Hatta on 2016/06/11.
// // Copyright © 2016 yaslab. All rights reserved.
// //
import Foundation import Foundation
private let LF: UInt32 = 0x0a //'\n' private let LF = "\n".unicodeScalars.first!
private let CR: UInt32 = 0x0d //'\r' private let CR = "\r".unicodeScalars.first!
private let DQUOTE: UInt32 = 0x22 //'"' private let DQUOTE = "\"".unicodeScalars.first!
internal let defaultHasHeaderRow = false internal let defaultHasHeaderRow = false
internal let defaultEncoding = NSUTF8StringEncoding internal let defaultDelimiter = ",".unicodeScalars.first!
internal let defaultDelimiter: CChar = 0x2c //','
internal let defaultBufferSize = 8192
internal let utf8BOM: [UInt8] = [0xef, 0xbb, 0xbf] public struct CSV: GeneratorType, SequenceType {
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 class CSV: SequenceType, GeneratorType { private var iterator: AnyIterator<UnicodeScalar>
private let delimiter: UnicodeScalar
internal let stream: NSInputStream private var back: UnicodeScalar? = nil
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
internal var currentRow: [String]? = 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 } public var headerRow: [String]? { return _headerRow }
private var _headerRow: [String]? = nil private var _headerRow: [String]? = nil
/** internal init<T: GeneratorType where T.Element == UnicodeScalar>(
The value is set when an error occurs. iterator: T,
*/ hasHeaderRow: Bool,
public private(set) var lastError: CSVError? = nil delimiter: UnicodeScalar)
/**
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)
throws throws
{ {
self.stream = stream self.iterator = AnyIterator(base: iterator)
self.delimiter = delimiter
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
if hasHeaderRow { if hasHeaderRow {
guard let nextRow = next() else { guard let headerRow = next() else {
throw CSVError.HeaderReadError throw CSVError.CannotReadHeaderRow
} }
_headerRow = nextRow _headerRow = headerRow
currentRow = nil
} }
} }
deinit { /// Create an instance with `InputStream`.
close() ///
/// - 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)
} }
/** /// Create an instance with `InputStream`.
Close stream. ///
*/ /// - parameter stream: An `InputStream` object. If the stream is not open, initializer opens automatically.
public func close() { /// - parameter codecType: A `UnicodeCodec` type for `stream`.
if !closed { /// - parameter endian: Endian to use when reading a stream. Default: `.big`.
stream.close() /// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`.
if buffer != nil { /// - parameter delimiter: Default: `","`.
free(buffer) public init<T: UnicodeCodecType where T.CodeUnit == UInt16>(
buffer = nil stream: NSInputStream,
} codecType: T.Type,
closed = true 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]? { // MARK: IteratorProtocol
fieldBuffer.length = 0
/// 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 currentRow = nil
if closed { var next = moveNext()
return nil if next == nil {
}
if lastReadCount <= 0 {
return nil return nil
} }
var fields = [String]() var row = [String]()
var field: String
var fieldStart = bufferOffset var end: Bool
var charLength = 0
var escaping = false
var quotationCount = 0
var prev: UInt32 = 0
while true { while true {
if bufferOffset >= lastReadCount { if next == nil {
if charLength > 0 { (field, end) = ("", true)
fieldBuffer.appendBytes(buffer + fieldStart, length: charWidth * charLength)
} }
bufferOffset = 0 else if next == DQUOTE {
fieldStart = 0 (field, end) = readField(quoted: true)
charLength = 0
lastReadCount = stream.read(buffer, maxLength: bufferSize)
if lastReadCount < 0 {
// bad end
lastError = CSVError.StreamError
return nil
}
if lastReadCount == 0 {
// true end
break
}
}
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 { else {
charLength += 1 back = next
(field, end) = readField(quoted: false)
}
row.append(field)
if end {
break
}
next = moveNext()
}
currentRow = row
return row
}
internal mutating func readField(quoted quoted: Bool) -> (String, Bool) {
var field = ""
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)
} }
} }
guard let field = getField(quotationCount) else { next = moveNext()
return nil
} }
// return (field, true)
if isBufferEOF && fields.count == 0 && field.isEmpty {
return nil
}
fields.append(field)
currentRow = fields
return fields
}
// MARK: Utility
private var isBufferEOF: Bool {
if stream.hasBytesAvailable {
return false
}
return bufferOffset >= (lastReadCount - 1)
}
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 // CSV
// //
// Created by Yasuhiro Hatta on 2016/06/11. // Created by Yasuhiro Hatta on 2016/06/11.
// // Copyright © 2016 yaslab. All rights reserved.
// //
import Foundation import Foundation
public enum CSVError: ErrorType { public enum CSVError: ErrorType {
case ParameterError case CannotOpenFile
case StreamError case CannotReadFile
case HeaderReadError case StreamErrorHasOccurred(error: NSError)
case MemoryAllocationFailed case CannotReadHeaderRow
case StringEncodingMismatch case StringEncodingMismatch
case StringEndianMismatch
} }

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

@ -3,7 +3,7 @@
// CSV // CSV
// //
// Created by Yasuhiro Hatta on 2016/06/11. // Created by Yasuhiro Hatta on 2016/06/11.
// // Copyright © 2016 yaslab. All rights reserved.
// //
@import Foundation; @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> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.2.0</string> <string>0.3.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <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() { func test1Line() {
let csv = "abab,cdcd,efef" let csv = "abab,cdcd,efef"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
} }
func testQuoted() { func testQuoted() {
let csv = "abab,\"cdcd\",efef" let csv = "abab,\"cdcd\",efef"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
} }
func testLF() { func testLF() {
let csv = "abab,cdcd,efef\nzxcv,asdf,qwer" let csv = "abab,cdcd,efef\nzxcv,asdf,qwer"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
} }
func testCommaInQuotationMarks() { func testCommaInQuotationMarks() {
let csv = "abab,\"cd,cd\",efef" let csv = "abab,\"cd,cd\",efef"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "cd,cd", "efef"]) XCTAssertEqual(records[0], ["abab", "cd,cd", "efef"])
} }
func testCRLF() { func testCRLF() {
let csv = "abab,cdcd,efef\r\nzxcv,asdf,qwer" let csv = "abab,cdcd,efef\r\nzxcv,asdf,qwer"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
} }
func testEscapedQuotationMark() { func testEscapedQuotationMark1() {
let csv = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer" let csv = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "\"cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "\"cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qwer"])
} }
func testQuotationMark2() { func testEscapedQuotationMark2() {
let csv = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"" let csv = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\""
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er"]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er"])
} }
func testEmptyField() { func testEmptyField() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
func testLastCR() { func testLastCR() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r" let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 2) XCTAssertEqual(records.count, 2)
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
@ -83,8 +74,7 @@ class CSVReaderTests: XCTestCase {
func testLastCRLF() { func testLastCRLF() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r\n" let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\r\n"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 2) XCTAssertEqual(records.count, 2)
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
@ -92,8 +82,7 @@ class CSVReaderTests: XCTestCase {
func testLastLF() { func testLastLF() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\n" let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\",\n"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 2) XCTAssertEqual(records.count, 2)
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
@ -101,8 +90,7 @@ class CSVReaderTests: XCTestCase {
func testLFInQuotationMarks() { func testLFInQuotationMarks() {
let csv = "abab,,\"\rcdcd\n\",efef\r\nzxcv,asdf,\"qw\"\"er\",\n" let csv = "abab,,\"\rcdcd\n\",efef\r\nzxcv,asdf,\"qw\"\"er\",\n"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 2) XCTAssertEqual(records.count, 2)
XCTAssertEqual(records[0], ["abab", "", "\rcdcd\n", "efef"]) XCTAssertEqual(records[0], ["abab", "", "\rcdcd\n", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
@ -110,8 +98,7 @@ class CSVReaderTests: XCTestCase {
func testLineBreakLF() { func testLineBreakLF() {
let csv = "qwe,asd\nzxc,rty" let csv = "qwe,asd\nzxc,rty"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 2) XCTAssertEqual(records.count, 2)
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], ["zxc", "rty"]) XCTAssertEqual(records[1], ["zxc", "rty"])
@ -119,8 +106,7 @@ class CSVReaderTests: XCTestCase {
func testLineBreakCR() { func testLineBreakCR() {
let csv = "qwe,asd\rzxc,rty" let csv = "qwe,asd\rzxc,rty"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 2) XCTAssertEqual(records.count, 2)
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], ["zxc", "rty"]) XCTAssertEqual(records[1], ["zxc", "rty"])
@ -128,8 +114,7 @@ class CSVReaderTests: XCTestCase {
func testLineBreakCRLF() { func testLineBreakCRLF() {
let csv = "qwe,asd\r\nzxc,rty" let csv = "qwe,asd\r\nzxc,rty"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 2) XCTAssertEqual(records.count, 2)
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], ["zxc", "rty"]) XCTAssertEqual(records[1], ["zxc", "rty"])
@ -137,8 +122,7 @@ class CSVReaderTests: XCTestCase {
func testLineBreakLFLF() { func testLineBreakLFLF() {
let csv = "qwe,asd\n\nzxc,rty" let csv = "qwe,asd\n\nzxc,rty"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 3) XCTAssertEqual(records.count, 3)
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], [""]) XCTAssertEqual(records[1], [""])
@ -147,8 +131,7 @@ class CSVReaderTests: XCTestCase {
func testLineBreakCRCR() { func testLineBreakCRCR() {
let csv = "qwe,asd\r\rzxc,rty" let csv = "qwe,asd\r\rzxc,rty"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 3) XCTAssertEqual(records.count, 3)
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], [""]) XCTAssertEqual(records[1], [""])
@ -157,119 +140,130 @@ class CSVReaderTests: XCTestCase {
func testLineBreakCRLFCRLF() { func testLineBreakCRLFCRLF() {
let csv = "qwe,asd\r\n\r\nzxc,rty" let csv = "qwe,asd\r\n\r\nzxc,rty"
let encoding = NSUTF8StringEncoding let records = parse(csv: csv)
let records = parseCSV(csv, encoding: encoding)
XCTAssertEqual(records.count, 3) XCTAssertEqual(records.count, 3)
XCTAssertEqual(records[0], ["qwe", "asd"]) XCTAssertEqual(records[0], ["qwe", "asd"])
XCTAssertEqual(records[1], [""]) XCTAssertEqual(records[1], [""])
XCTAssertEqual(records[2], ["zxc", "rty"]) XCTAssertEqual(records[2], ["zxc", "rty"])
} }
func testEncodingWithoutBOM() { // func testEncodingWithoutBOM() {
var index = 0 // var index = 0
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," // let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
for encoding in allEncodings() { // for encoding in allEncodings() {
print("index: \(index)") // print("index: \(index)")
let records = parseCSV(csv, encoding: encoding) // let records = parse(csv: csv, encoding: encoding)
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) // XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) // XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
index += 1 // index += 1
} // }
} // }
func testUTF8WithBOM() { 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 encoding = NSUTF8StringEncoding
let mutableData = NSMutableData() var mutableData = NSMutableData()
mutableData.appendBytes(utf8BOM, length: utf8BOM.count) mutableData.appendBytes(utf8BOM, length: utf8BOM.count)
mutableData.appendData(csv.dataUsingEncoding(encoding)!) mutableData.appendData(csvString.dataUsingEncoding(encoding)!)
let records = parseData(mutableData, encoding: 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[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
func testUTF16WithNativeEndianBOM() { 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 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[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv", "😆asdf", "qw\"er", ""])
} }
func testUTF16WithBigEndianBOM() { func testUTF16WithBigEndianBOM() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let csvString = "abab,,cdcd,efef\r\n😆zxcv,asdf,\"qw\"\"er\","
let encoding = NSUTF16StringEncoding let encoding = NSUTF16BigEndianStringEncoding
let mutableData = NSMutableData() var mutableData = NSMutableData()
mutableData.appendBytes(utf16BigEndianBOM, length: utf16BigEndianBOM.count) mutableData.appendBytes(utf16BigEndianBOM, length: utf16BigEndianBOM.count)
mutableData.appendData(csv.dataUsingEncoding(NSUTF16BigEndianStringEncoding)!) mutableData.appendData(csvString.dataUsingEncoding(encoding)!)
let records = parseData(mutableData, encoding: 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[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["😆zxcv", "asdf", "qw\"er", ""])
} }
func testUTF16WithLittleEndianBOM() { func testUTF16WithLittleEndianBOM() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let csvString = "abab,,cdcd,efef\r\nzxcv😆,asdf,\"qw\"\"er\","
let encoding = NSUTF16StringEncoding let encoding = NSUTF16LittleEndianStringEncoding
let mutableData = NSMutableData() var mutableData = NSMutableData()
mutableData.appendBytes(utf16LittleEndianBOM, length: utf16LittleEndianBOM.count) mutableData.appendBytes(utf16LittleEndianBOM, length: utf16LittleEndianBOM.count)
mutableData.appendData(csv.dataUsingEncoding(NSUTF16LittleEndianStringEncoding)!) mutableData.appendData(csvString.dataUsingEncoding(encoding)!)
let records = parseData(mutableData, encoding: 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[0], ["abab", "", "cdcd", "efef"])
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""]) XCTAssertEqual(records[1], ["zxcv😆", "asdf", "qw\"er", ""])
} }
func testUTF32WithNativeEndianBOM() { 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 encoding = NSUTF32StringEncoding
let records = parseCSV(csv, encoding: encoding) var mutableData = NSMutableData()
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: .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 testUTF32WithBigEndianBOM() { func testUTF32WithBigEndianBOM() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let csvString = "abab,,cd😆cd,efef\r\nzxcv,asdf,\"qw\"\"er\","
let encoding = NSUTF32StringEncoding let encoding = NSUTF32BigEndianStringEncoding
let mutableData = NSMutableData() var mutableData = NSMutableData()
mutableData.appendBytes(utf32BigEndianBOM, length: utf32BigEndianBOM.count) mutableData.appendBytes(utf32BigEndianBOM, length: utf32BigEndianBOM.count)
mutableData.appendData(csv.dataUsingEncoding(NSUTF32BigEndianStringEncoding)!) mutableData.appendData(csvString.dataUsingEncoding(encoding)!)
let records = parseData(mutableData, encoding: encoding) let stream = NSInputStream(data: mutableData)
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) 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", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
func testUTF32WithLittleEndianBOM() { func testUTF32WithLittleEndianBOM() {
let csv = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\"," let csvString = "abab,,cdcd,ef😆ef\r\nzxcv,asdf,\"qw\"\"er\","
let encoding = NSUTF32StringEncoding let encoding = NSUTF32LittleEndianStringEncoding
let mutableData = NSMutableData() var mutableData = NSMutableData()
mutableData.appendBytes(utf32LittleEndianBOM, length: utf32LittleEndianBOM.count) mutableData.appendBytes(utf32LittleEndianBOM, length: utf32LittleEndianBOM.count)
mutableData.appendData(csv.dataUsingEncoding(NSUTF32LittleEndianStringEncoding)!) mutableData.appendData(csvString.dataUsingEncoding(encoding)!)
let records = parseData(mutableData, encoding: encoding) let stream = NSInputStream(data: mutableData)
XCTAssertEqual(records[0], ["abab", "", "cdcd", "efef"]) 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", ""]) XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
} }
func allEncodings() -> [NSStringEncoding] { // func allEncodings() -> [String.Encoding] {
return [ // return [
// multi-byte character encodings // // multi-byte character encodings
NSShiftJISStringEncoding, // //String.Encoding.shiftJIS,
NSJapaneseEUCStringEncoding, // //String.Encoding.japaneseEUC,
NSUTF8StringEncoding, // String.Encoding.utf8,
// wide character encodings // // wide character encodings
NSUTF16BigEndianStringEncoding, // String.Encoding.utf16BigEndian,
NSUTF16LittleEndianStringEncoding, // String.Encoding.utf16LittleEndian,
NSUTF32BigEndianStringEncoding, // String.Encoding.utf32BigEndian,
NSUTF32LittleEndianStringEncoding, // String.Encoding.utf32LittleEndian,
] // ]
} // }
func parseCSV(csv: String, encoding: NSStringEncoding) -> [[String]] { func parse(csv csv: String) -> [[String]] {
let data = csv.dataUsingEncoding(encoding)! let reader = try! CSV(string: csv)
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)
var records = [[String]]() var records = [[String]]()
for row in reader { for row in reader {
records.append(row) records.append(row)
@ -277,6 +271,29 @@ class CSVReaderTests: XCTestCase {
return records 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)] { static var allTests : [(String, (CSVReaderTests) -> () throws -> Void)] {
return [ return [
//("testExample1", testExample1), //("testExample1", testExample1),

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

@ -97,53 +97,53 @@ class CSVTests: XCTestCase {
XCTAssertEqual(i, 3) XCTAssertEqual(i, 3)
} }
func testBufferSizeMod0() { // func testBufferSizeMod0() {
let csvString = "0,1,2,3,4,5,6,7,8,9\n" // let csvString = "0,1,2,3,4,5,6,7,8,9\n"
let csv = try! CSV(string: csvString, bufferSize: 12) // let csv = try! CSV(string: csvString, bufferSize: 12)
XCTAssertEqual(csv.bufferSize, 12) // XCTAssertEqual(csv.bufferSize, 12)
} // }
//
func testBufferSizeMod1() { // func testBufferSizeMod1() {
let csvString = "0,1,2,3,4,5,6,7,8,9\n" // let csvString = "0,1,2,3,4,5,6,7,8,9\n"
let csv = try! CSV(string: csvString, bufferSize: 13) // let csv = try! CSV(string: csvString, bufferSize: 13)
XCTAssertEqual(csv.bufferSize, 16) // XCTAssertEqual(csv.bufferSize, 16)
} // }
//
func testBufferSizeMod2() { // func testBufferSizeMod2() {
let csvString = "0,1,2,3,4,5,6,7,8,9\n" // let csvString = "0,1,2,3,4,5,6,7,8,9\n"
let csv = try! CSV(string: csvString, bufferSize: 14) // let csv = try! CSV(string: csvString, bufferSize: 14)
XCTAssertEqual(csv.bufferSize, 16) // XCTAssertEqual(csv.bufferSize, 16)
} // }
//
func testBufferSizeMod3() { // func testBufferSizeMod3() {
let csvString = "0,1,2,3,4,5,6,7,8,9\n" // let csvString = "0,1,2,3,4,5,6,7,8,9\n"
let csv = try! CSV(string: csvString, bufferSize: 15) // let csv = try! CSV(string: csvString, bufferSize: 15)
XCTAssertEqual(csv.bufferSize, 16) // XCTAssertEqual(csv.bufferSize, 16)
} // }
//
func testBufferSizeMod4() { // func testBufferSizeMod4() {
let csvString = "0,1,2,3,4,5,6,7,8,9\n" // let csvString = "0,1,2,3,4,5,6,7,8,9\n"
let csv = try! CSV(string: csvString, bufferSize: 16) // let csv = try! CSV(string: csvString, bufferSize: 16)
XCTAssertEqual(csv.bufferSize, 16) // XCTAssertEqual(csv.bufferSize, 16)
} // }
//
func testBigDataAndSmallBufferSize() { // func testBigDataAndSmallBufferSize() {
let line = "0,1,2,3,4,5,6,7,8,9\n" // let line = "0,1,2,3,4,5,6,7,8,9\n"
var csv = "" // var csv = ""
for _ in 0..<10000 { // for _ in 0..<10000 {
csv += line // csv += line
} // }
var i = 0 // var i = 0
for row in try! CSV(string: csv, bufferSize: 10) { // for row in try! CSV(string: csv, bufferSize: 10) {
XCTAssertEqual(row, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]) // XCTAssertEqual(row, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"])
i += 1 // i += 1
} // }
XCTAssertEqual(i, 10000) // XCTAssertEqual(i, 10000)
} // }
func testSubscript() { func testSubscript() {
let csvString = "id,name\n001,hoge\n002,fuga" 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 var i = 0
while csv.next() != nil { while csv.next() != nil {
switch i { switch i {
@ -161,4 +161,18 @@ class CSVTests: XCTestCase {
XCTAssertEqual(i, 2) 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", ""])
}
} }

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

@ -20,7 +20,8 @@ class ReadmeTests: XCTestCase {
} }
func testFromFilePath() { func testFromFilePath() {
//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)") // print("\(row)")
// } // }
} }
@ -41,7 +42,7 @@ class ReadmeTests: XCTestCase {
} }
func testGetTheFieldValueUsingSubscript() { func testGetTheFieldValueUsingSubscript() {
let csv = try! CSV( var csv = try! CSV(
string: "id,name\n1,foo", string: "id,name\n1,foo",
hasHeaderRow: true) // It must be true. hasHeaderRow: true) // It must be true.
@ -53,8 +54,9 @@ class ReadmeTests: XCTestCase {
func testProvideTheCharacterEncoding() { func testProvideTheCharacterEncoding() {
// let csv = try! CSV( // let csv = try! CSV(
// path: "/path/to/file.csv", // stream: NSInputStream(fileAtPath: "/path/to/file.csv")!,
// encoding: NSUTF8StringEncoding) // codecType: UTF16.self,
// endian: .Big)
} }
} }