commit
1ca5d1a558
|
@ -1 +1 @@
|
||||||
3.0
|
3.1
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
disabled_rules:
|
||||||
|
- force_cast
|
||||||
|
- force_try
|
||||||
|
- variable_name
|
||||||
|
included:
|
||||||
|
- Sources
|
|
@ -1,5 +1,5 @@
|
||||||
language: objective-c
|
language: objective-c
|
||||||
osx_image: xcode8
|
osx_image: xcode8.3
|
||||||
env:
|
env:
|
||||||
- LC_CTYPE=en_US.UTF-8
|
- LC_CTYPE=en_US.UTF-8
|
||||||
git:
|
git:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'CSV.swift'
|
s.name = 'CSV.swift'
|
||||||
s.version = '1.1.2'
|
s.version = '2.0.0'
|
||||||
s.license = 'MIT'
|
s.license = 'MIT'
|
||||||
s.summary = 'CSV reading library written in Swift.'
|
s.summary = 'CSV reading library written in Swift.'
|
||||||
s.homepage = 'https://github.com/yaslab/CSV.swift'
|
s.homepage = 'https://github.com/yaslab/CSV.swift'
|
||||||
|
|
|
@ -11,29 +11,31 @@
|
||||||
0E0F160F1D197DB800C92580 /* 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 */; };
|
0E0F16101D197DB800C92580 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F160D1D197DB800C92580 /* Endian.swift */; };
|
||||||
0E0F16111D197DB800C92580 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F160D1D197DB800C92580 /* Endian.swift */; };
|
0E0F16111D197DB800C92580 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0F160D1D197DB800C92580 /* Endian.swift */; };
|
||||||
|
0E54021B1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */; };
|
||||||
|
0E54021C1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */; };
|
||||||
|
0E54021D1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */; };
|
||||||
|
0E5402221EDA82220019C3ED /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021E1EDA81E80019C3ED /* CSVWriter.swift */; };
|
||||||
|
0E5402231EDA82220019C3ED /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021E1EDA81E80019C3ED /* CSVWriter.swift */; };
|
||||||
|
0E5402241EDA82220019C3ED /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021E1EDA81E80019C3ED /* CSVWriter.swift */; };
|
||||||
|
0E5402251EDA82230019C3ED /* CSVWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E54021E1EDA81E80019C3ED /* CSVWriter.swift */; };
|
||||||
0E7E8C8C1D0BC7BB0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */; };
|
0E7E8C8C1D0BC7BB0057A1C1 /* CSV.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */; };
|
||||||
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
0E7E8CA11D0BC7F10057A1C1 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSVReader.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, ); }; };
|
||||||
0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
0E7E8CBE1D0BC9D70057A1C1 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSVReader.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 */; };
|
||||||
0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
0E7E8CE01D0BCA8E0057A1C1 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSVReader.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 */; };
|
||||||
0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */; };
|
0E7E8D001D0BCDCF0057A1C1 /* CSVReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E8C9D1D0BC7F10057A1C1 /* CSVReader.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, ); }; };
|
||||||
0E9317D41D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; };
|
0E7F657B1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */; };
|
||||||
0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; };
|
0E7F657C1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */; };
|
||||||
0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; };
|
0E7F657D1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */; };
|
||||||
0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */; };
|
|
||||||
0E9317D91D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
|
|
||||||
0E9317DA1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
|
|
||||||
0E9317DB1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
|
|
||||||
0E9317DC1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */; };
|
|
||||||
0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */; };
|
0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */; };
|
||||||
0EA2AB7D1D183B45003EC967 /* 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 */; };
|
0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */; };
|
||||||
|
@ -42,21 +44,25 @@
|
||||||
0EA2AB821D183BA9003EC967 /* 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 */; };
|
0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */; };
|
||||||
0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */; };
|
0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */; };
|
||||||
0EA37ADD1DD8C12600F5B274 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37AC91DD8C0B900F5B274 /* CSVTests.swift */; };
|
0EC628FE1EF675B300289554 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC628FD1EF675B300289554 /* CSV.swift */; };
|
||||||
0EA37ADE1DD8C12600F5B274 /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACA1DD8C0B900F5B274 /* LineBreakTests.swift */; };
|
0EC628FF1EF675FD00289554 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC628FD1EF675B300289554 /* CSV.swift */; };
|
||||||
0EA37ADF1DD8C12600F5B274 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACB1DD8C0B900F5B274 /* ReadmeTests.swift */; };
|
0EC629001EF675FE00289554 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC628FD1EF675B300289554 /* CSV.swift */; };
|
||||||
0EA37AE01DD8C12600F5B274 /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACC1DD8C0B900F5B274 /* TrimFieldsTests.swift */; };
|
0EC629011EF675FF00289554 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC628FD1EF675B300289554 /* CSV.swift */; };
|
||||||
0EA37AE11DD8C12600F5B274 /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACD1DD8C0B900F5B274 /* UnicodeTests.swift */; };
|
0EDF8ED71DDB73520068056A /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECD1DDB73370068056A /* CSVTests.swift */; };
|
||||||
0EA37AE21DD8C12700F5B274 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37AC91DD8C0B900F5B274 /* CSVTests.swift */; };
|
0EDF8ED81DDB73520068056A /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */; };
|
||||||
0EA37AE31DD8C12700F5B274 /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACA1DD8C0B900F5B274 /* LineBreakTests.swift */; };
|
0EDF8ED91DDB73520068056A /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */; };
|
||||||
0EA37AE41DD8C12700F5B274 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACB1DD8C0B900F5B274 /* ReadmeTests.swift */; };
|
0EDF8EDA1DDB73520068056A /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */; };
|
||||||
0EA37AE51DD8C12700F5B274 /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACC1DD8C0B900F5B274 /* TrimFieldsTests.swift */; };
|
0EDF8EDB1DDB73520068056A /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED11DDB73370068056A /* UnicodeTests.swift */; };
|
||||||
0EA37AE61DD8C12700F5B274 /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACD1DD8C0B900F5B274 /* UnicodeTests.swift */; };
|
0EDF8EDC1DDB73520068056A /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECD1DDB73370068056A /* CSVTests.swift */; };
|
||||||
0EA37AE71DD8C12700F5B274 /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37AC91DD8C0B900F5B274 /* CSVTests.swift */; };
|
0EDF8EDD1DDB73520068056A /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */; };
|
||||||
0EA37AE81DD8C12700F5B274 /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACA1DD8C0B900F5B274 /* LineBreakTests.swift */; };
|
0EDF8EDE1DDB73520068056A /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */; };
|
||||||
0EA37AE91DD8C12700F5B274 /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACB1DD8C0B900F5B274 /* ReadmeTests.swift */; };
|
0EDF8EDF1DDB73520068056A /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */; };
|
||||||
0EA37AEA1DD8C12700F5B274 /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACC1DD8C0B900F5B274 /* TrimFieldsTests.swift */; };
|
0EDF8EE01DDB73520068056A /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED11DDB73370068056A /* UnicodeTests.swift */; };
|
||||||
0EA37AEB1DD8C12700F5B274 /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA37ACD1DD8C0B900F5B274 /* UnicodeTests.swift */; };
|
0EDF8EE11DDB73530068056A /* CSVTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECD1DDB73370068056A /* CSVTests.swift */; };
|
||||||
|
0EDF8EE21DDB73530068056A /* LineBreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */; };
|
||||||
|
0EDF8EE31DDB73530068056A /* ReadmeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */; };
|
||||||
|
0EDF8EE41DDB73530068056A /* TrimFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */; };
|
||||||
|
0EDF8EE51DDB73530068056A /* UnicodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDF8ED11DDB73370068056A /* UnicodeTests.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -85,9 +91,11 @@
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
0E0F160D1D197DB800C92580 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endian.swift; sourceTree = "<group>"; };
|
0E0F160D1D197DB800C92580 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endian.swift; sourceTree = "<group>"; };
|
||||||
|
0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVWriterTests.swift; sourceTree = "<group>"; };
|
||||||
|
0E54021E1EDA81E80019C3ED /* CSVWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVWriter.swift; sourceTree = "<group>"; };
|
||||||
0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8C811D0BC7BB0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8C8B1D0BC7BB0057A1C1 /* CSVTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = "<group>"; };
|
0E7E8C9D1D0BC7F10057A1C1 /* CSVReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVReader.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>"; };
|
||||||
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
@ -97,15 +105,15 @@
|
||||||
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CCF1D0BCA2A0057A1C1 /* CSVTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CE81D0BCD0B0057A1C1 /* CSV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CSV.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
0E7E8CF11D0BCD0B0057A1C1 /* CSVTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CSVTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+init.swift"; sourceTree = "<group>"; };
|
0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Version1Tests.swift; sourceTree = "<group>"; };
|
||||||
0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSV+subscript.swift"; sourceTree = "<group>"; };
|
|
||||||
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = "<group>"; };
|
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryReader.swift; sourceTree = "<group>"; };
|
||||||
0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeIterator.swift; sourceTree = "<group>"; };
|
0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeIterator.swift; sourceTree = "<group>"; };
|
||||||
0EA37AC91DD8C0B900F5B274 /* CSVTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVTests.swift; sourceTree = "<group>"; };
|
0EC628FD1EF675B300289554 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = "<group>"; };
|
||||||
0EA37ACA1DD8C0B900F5B274 /* LineBreakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineBreakTests.swift; sourceTree = "<group>"; };
|
0EDF8ECD1DDB73370068056A /* CSVTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSVTests.swift; sourceTree = "<group>"; };
|
||||||
0EA37ACB1DD8C0B900F5B274 /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = "<group>"; };
|
0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineBreakTests.swift; sourceTree = "<group>"; };
|
||||||
0EA37ACC1DD8C0B900F5B274 /* TrimFieldsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrimFieldsTests.swift; sourceTree = "<group>"; };
|
0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadmeTests.swift; sourceTree = "<group>"; };
|
||||||
0EA37ACD1DD8C0B900F5B274 /* UnicodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeTests.swift; sourceTree = "<group>"; };
|
0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrimFieldsTests.swift; sourceTree = "<group>"; };
|
||||||
|
0EDF8ED11DDB73370068056A /* UnicodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeTests.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -192,11 +200,11 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */,
|
0EA2AB7B1D183B45003EC967 /* BinaryReader.swift */,
|
||||||
0E7E8C9D1D0BC7F10057A1C1 /* CSV.swift */,
|
0EC628FD1EF675B300289554 /* CSV.swift */,
|
||||||
0E9317D31D0DB2F200AC20A0 /* CSV+init.swift */,
|
|
||||||
0E9317D81D0DB30800AC20A0 /* CSV+subscript.swift */,
|
|
||||||
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */,
|
0E7E8C9E1D0BC7F10057A1C1 /* CSVError.swift */,
|
||||||
|
0E7E8C9D1D0BC7F10057A1C1 /* CSVReader.swift */,
|
||||||
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */,
|
0E7E8C9F1D0BC7F10057A1C1 /* CSVVersion.h */,
|
||||||
|
0E54021E1EDA81E80019C3ED /* CSVWriter.swift */,
|
||||||
0E0F160D1D197DB800C92580 /* Endian.swift */,
|
0E0F160D1D197DB800C92580 /* Endian.swift */,
|
||||||
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */,
|
0E7E8CAC1D0BC8610057A1C1 /* Info.plist */,
|
||||||
0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */,
|
0EA2AB801D183BA9003EC967 /* UnicodeIterator.swift */,
|
||||||
|
@ -216,11 +224,13 @@
|
||||||
0EA37AC81DD8C0B900F5B274 /* CSVTests */ = {
|
0EA37AC81DD8C0B900F5B274 /* CSVTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0EA37AC91DD8C0B900F5B274 /* CSVTests.swift */,
|
0EDF8ECD1DDB73370068056A /* CSVTests.swift */,
|
||||||
0EA37ACA1DD8C0B900F5B274 /* LineBreakTests.swift */,
|
0E54021A1ED9DDF40019C3ED /* CSVWriterTests.swift */,
|
||||||
0EA37ACB1DD8C0B900F5B274 /* ReadmeTests.swift */,
|
0EDF8ECE1DDB73370068056A /* LineBreakTests.swift */,
|
||||||
0EA37ACC1DD8C0B900F5B274 /* TrimFieldsTests.swift */,
|
0EDF8ECF1DDB73370068056A /* ReadmeTests.swift */,
|
||||||
0EA37ACD1DD8C0B900F5B274 /* UnicodeTests.swift */,
|
0EDF8ED01DDB73370068056A /* TrimFieldsTests.swift */,
|
||||||
|
0EDF8ED11DDB73370068056A /* UnicodeTests.swift */,
|
||||||
|
0E7F657A1EF6437E00E1E1A0 /* Version1Tests.swift */,
|
||||||
);
|
);
|
||||||
path = CSVTests;
|
path = CSVTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -321,6 +331,7 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */;
|
buildConfigurationList = 0E7E8CD71D0BCA2A0057A1C1 /* Build configuration list for PBXNativeTarget "CSV-OSX" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
0E47EEBF1DBBC05700EBF783 /* Run Script () */,
|
||||||
0E7E8CC11D0BCA2A0057A1C1 /* Sources */,
|
0E7E8CC11D0BCA2A0057A1C1 /* Sources */,
|
||||||
0E7E8CC21D0BCA2A0057A1C1 /* Frameworks */,
|
0E7E8CC21D0BCA2A0057A1C1 /* Frameworks */,
|
||||||
0E7E8CC31D0BCA2A0057A1C1 /* Headers */,
|
0E7E8CC31D0BCA2A0057A1C1 /* Headers */,
|
||||||
|
@ -504,17 +515,34 @@
|
||||||
};
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
0E47EEBF1DBBC05700EBF783 /* Run Script () */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script ()";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "if which swiftlint >/dev/null; then\n swiftlint autocorrect\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
0E7E8C7C1D0BC7BB0057A1C1 /* Sources */ = {
|
0E7E8C7C1D0BC7BB0057A1C1 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E9317D51D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
|
|
||||||
0EA2AB821D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
0EA2AB821D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
||||||
0E9317DA1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
|
0E5402241EDA82220019C3ED /* CSVWriter.swift in Sources */,
|
||||||
0E7E8CA11D0BC7F10057A1C1 /* CSV.swift in Sources */,
|
0E7E8CA11D0BC7F10057A1C1 /* CSVReader.swift in Sources */,
|
||||||
0E0F160F1D197DB800C92580 /* Endian.swift in Sources */,
|
0E0F160F1D197DB800C92580 /* Endian.swift in Sources */,
|
||||||
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */,
|
0E7E8CA21D0BC7F10057A1C1 /* CSVError.swift in Sources */,
|
||||||
|
0EC628FF1EF675FD00289554 /* CSV.swift in Sources */,
|
||||||
0EA2AB7D1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
0EA2AB7D1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -523,11 +551,13 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0EA37AE41DD8C12700F5B274 /* ReadmeTests.swift in Sources */,
|
0EDF8EDE1DDB73520068056A /* ReadmeTests.swift in Sources */,
|
||||||
0EA37AE21DD8C12700F5B274 /* CSVTests.swift in Sources */,
|
0EDF8EDC1DDB73520068056A /* CSVTests.swift in Sources */,
|
||||||
0EA37AE51DD8C12700F5B274 /* TrimFieldsTests.swift in Sources */,
|
0EDF8EDF1DDB73520068056A /* TrimFieldsTests.swift in Sources */,
|
||||||
0EA37AE61DD8C12700F5B274 /* UnicodeTests.swift in Sources */,
|
0E7F657C1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */,
|
||||||
0EA37AE31DD8C12700F5B274 /* LineBreakTests.swift in Sources */,
|
0EDF8EE01DDB73520068056A /* UnicodeTests.swift in Sources */,
|
||||||
|
0EDF8EDD1DDB73520068056A /* LineBreakTests.swift in Sources */,
|
||||||
|
0E54021C1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -535,12 +565,12 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E9317D71D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
|
|
||||||
0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
0EA2AB841D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
||||||
0E9317DC1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
|
0E5402221EDA82220019C3ED /* CSVWriter.swift in Sources */,
|
||||||
0E7E8CBE1D0BC9D70057A1C1 /* CSV.swift in Sources */,
|
0E7E8CBE1D0BC9D70057A1C1 /* CSVReader.swift in Sources */,
|
||||||
0E0F16111D197DB800C92580 /* Endian.swift in Sources */,
|
0E0F16111D197DB800C92580 /* Endian.swift in Sources */,
|
||||||
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */,
|
0E7E8CBF1D0BC9D70057A1C1 /* CSVError.swift in Sources */,
|
||||||
|
0EC629011EF675FF00289554 /* CSV.swift in Sources */,
|
||||||
0EA2AB7F1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
0EA2AB7F1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -549,12 +579,12 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E9317D41D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
|
|
||||||
0EA2AB811D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
0EA2AB811D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
||||||
0E9317D91D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
|
0E5402251EDA82230019C3ED /* CSVWriter.swift in Sources */,
|
||||||
0E7E8CE01D0BCA8E0057A1C1 /* CSV.swift in Sources */,
|
0E7E8CE01D0BCA8E0057A1C1 /* CSVReader.swift in Sources */,
|
||||||
0E0F160E1D197DB800C92580 /* Endian.swift in Sources */,
|
0E0F160E1D197DB800C92580 /* Endian.swift in Sources */,
|
||||||
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */,
|
0E7E8CE11D0BCA8E0057A1C1 /* CSVError.swift in Sources */,
|
||||||
|
0EC628FE1EF675B300289554 /* CSV.swift in Sources */,
|
||||||
0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
0EA2AB7C1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -563,11 +593,13 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0EA37ADF1DD8C12600F5B274 /* ReadmeTests.swift in Sources */,
|
0EDF8ED91DDB73520068056A /* ReadmeTests.swift in Sources */,
|
||||||
0EA37ADD1DD8C12600F5B274 /* CSVTests.swift in Sources */,
|
0EDF8ED71DDB73520068056A /* CSVTests.swift in Sources */,
|
||||||
0EA37AE01DD8C12600F5B274 /* TrimFieldsTests.swift in Sources */,
|
0EDF8EDA1DDB73520068056A /* TrimFieldsTests.swift in Sources */,
|
||||||
0EA37AE11DD8C12600F5B274 /* UnicodeTests.swift in Sources */,
|
0E7F657B1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */,
|
||||||
0EA37ADE1DD8C12600F5B274 /* LineBreakTests.swift in Sources */,
|
0EDF8EDB1DDB73520068056A /* UnicodeTests.swift in Sources */,
|
||||||
|
0EDF8ED81DDB73520068056A /* LineBreakTests.swift in Sources */,
|
||||||
|
0E54021B1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -575,12 +607,12 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0E9317D61D0DB2F200AC20A0 /* CSV+init.swift in Sources */,
|
|
||||||
0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
0EA2AB831D183BA9003EC967 /* UnicodeIterator.swift in Sources */,
|
||||||
0E9317DB1D0DB30800AC20A0 /* CSV+subscript.swift in Sources */,
|
0E5402231EDA82220019C3ED /* CSVWriter.swift in Sources */,
|
||||||
0E7E8D001D0BCDCF0057A1C1 /* CSV.swift in Sources */,
|
0E7E8D001D0BCDCF0057A1C1 /* CSVReader.swift in Sources */,
|
||||||
0E0F16101D197DB800C92580 /* Endian.swift in Sources */,
|
0E0F16101D197DB800C92580 /* Endian.swift in Sources */,
|
||||||
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */,
|
0E7E8D011D0BCDCF0057A1C1 /* CSVError.swift in Sources */,
|
||||||
|
0EC629001EF675FE00289554 /* CSV.swift in Sources */,
|
||||||
0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
0EA2AB7E1D183B45003EC967 /* BinaryReader.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -589,11 +621,13 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0EA37AE91DD8C12700F5B274 /* ReadmeTests.swift in Sources */,
|
0EDF8EE31DDB73530068056A /* ReadmeTests.swift in Sources */,
|
||||||
0EA37AE71DD8C12700F5B274 /* CSVTests.swift in Sources */,
|
0EDF8EE11DDB73530068056A /* CSVTests.swift in Sources */,
|
||||||
0EA37AEA1DD8C12700F5B274 /* TrimFieldsTests.swift in Sources */,
|
0EDF8EE41DDB73530068056A /* TrimFieldsTests.swift in Sources */,
|
||||||
0EA37AEB1DD8C12700F5B274 /* UnicodeTests.swift in Sources */,
|
0E7F657D1EF6437E00E1E1A0 /* Version1Tests.swift in Sources */,
|
||||||
0EA37AE81DD8C12700F5B274 /* LineBreakTests.swift in Sources */,
|
0EDF8EE51DDB73530068056A /* UnicodeTests.swift in Sources */,
|
||||||
|
0EDF8EE21DDB73530068056A /* LineBreakTests.swift in Sources */,
|
||||||
|
0E54021D1ED9DDF40019C3ED /* CSVWriterTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
94
README.md
94
README.md
|
@ -2,30 +2,33 @@
|
||||||
|
|
||||||
[](https://travis-ci.org/yaslab/CSV.swift)
|
[](https://travis-ci.org/yaslab/CSV.swift)
|
||||||
|
|
||||||
CSV reading library written in Swift.
|
CSV reading and writing library written in Swift.
|
||||||
|
|
||||||
## Usage
|
## Usage for reading CSV
|
||||||
|
|
||||||
### From CSV string
|
### From string
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import CSV
|
import CSV
|
||||||
|
|
||||||
for row in try! CSV(string: "1,foo\n2,bar") {
|
let csvString = "1,foo\n2,bar"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
while let row = csv.next() {
|
||||||
print("\(row)")
|
print("\(row)")
|
||||||
// => ["1", "foo"]
|
|
||||||
// => ["2", "bar"]
|
|
||||||
}
|
}
|
||||||
|
// => ["1", "foo"]
|
||||||
|
// => ["2", "bar"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### From file path
|
### From file
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import Foundation
|
import Foundation
|
||||||
import CSV
|
import CSV
|
||||||
|
|
||||||
let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
for row in try! CSV(stream: stream) {
|
let csv = try! CSVReader(stream: stream)
|
||||||
|
while let row = csv.next() {
|
||||||
print("\(row)")
|
print("\(row)")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -35,18 +38,18 @@ for row in try! CSV(stream: stream) {
|
||||||
```swift
|
```swift
|
||||||
import CSV
|
import CSV
|
||||||
|
|
||||||
let csv = try! CSV(
|
let csvString = "id,name\n1,foo\n2,bar"
|
||||||
string: "id,name\n1,foo\n2,bar",
|
let csv = try! CSVReader(string: csvString,
|
||||||
hasHeaderRow: true) // default: false
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
let headerRow = csv.headerRow!
|
let headerRow = csv.headerRow!
|
||||||
print("\(headerRow)") // => ["id", "name"]
|
print("\(headerRow)") // => ["id", "name"]
|
||||||
|
|
||||||
for row in csv {
|
while let row = csv.next() {
|
||||||
print("\(row)")
|
print("\(row)")
|
||||||
// => ["1", "foo"]
|
|
||||||
// => ["2", "bar"]
|
|
||||||
}
|
}
|
||||||
|
// => ["1", "foo"]
|
||||||
|
// => ["2", "bar"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get the field value using subscript
|
### Get the field value using subscript
|
||||||
|
@ -54,11 +57,11 @@ for row in csv {
|
||||||
```swift
|
```swift
|
||||||
import CSV
|
import CSV
|
||||||
|
|
||||||
var csv = try! CSV(
|
let csvString = "id,name\n1,foo"
|
||||||
string: "id,name\n1,foo",
|
let csv = try! CSVReader(string: csvString,
|
||||||
hasHeaderRow: true) // It must be true.
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
while let _ = csv.next() {
|
while csv.next() != nil {
|
||||||
print("\(csv["id"]!)") // => "1"
|
print("\(csv["id"]!)") // => "1"
|
||||||
print("\(csv["name"]!)") // => "foo"
|
print("\(csv["name"]!)") // => "foo"
|
||||||
}
|
}
|
||||||
|
@ -72,24 +75,71 @@ If you use a file path, you can provide the character encoding to initializer.
|
||||||
import Foundation
|
import Foundation
|
||||||
import CSV
|
import CSV
|
||||||
|
|
||||||
let csv = try! CSV(
|
let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
stream: InputStream(fileAtPath: "/path/to/file.csv")!,
|
let csv = try! CSV(stream: stream,
|
||||||
codecType: UTF16.self,
|
codecType: UTF16.self,
|
||||||
endian: .big)
|
endian: .big)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Usage for writing CSV
|
||||||
|
|
||||||
|
### Write to memory and get a CSV String
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import Foundation
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
|
||||||
|
// Write a row
|
||||||
|
try! csv.write(row: ["id", "name"])
|
||||||
|
|
||||||
|
// Write fields separately
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "1")
|
||||||
|
try! csv.write(field: "foo")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "2")
|
||||||
|
try! csv.write(field: "bar")
|
||||||
|
|
||||||
|
csv.stream.close()
|
||||||
|
|
||||||
|
// Get a String
|
||||||
|
let csvData = stream.property(forKey: .dataWrittenToMemoryStreamKey) as! NSData
|
||||||
|
let csvString = String(data: Data(referencing: csvData), encoding: .utf8)!
|
||||||
|
print(csvString)
|
||||||
|
// => "id,name\n1,foo\n2,bar"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write to file
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import Foundation
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
let stream = OutputStream(toFileAtPath: "/path/to/file.csv", append: false)!
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
|
||||||
|
try! csv.write(row: ["id", "name"])
|
||||||
|
try! csv.write(row: ["1", "foo"])
|
||||||
|
try! csv.write(row: ["1", "bar"])
|
||||||
|
|
||||||
|
csv.stream.close()
|
||||||
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### CocoaPods
|
### CocoaPods
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
pod 'CSV.swift', '~> 1.1'
|
pod 'CSV.swift', '~> 2.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Carthage
|
### Carthage
|
||||||
|
|
||||||
```
|
```
|
||||||
github "yaslab/CSV.swift" ~> 1.1
|
github "yaslab/CSV.swift" ~> 2.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Swift Package Manager
|
### Swift Package Manager
|
||||||
|
@ -100,7 +150,7 @@ import PackageDescription
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "PackageName",
|
name: "PackageName",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.Package(url: "https://github.com/yaslab/CSV.swift.git", majorVersion: 1, minor: 1)
|
.Package(url: "https://github.com/yaslab/CSV.swift.git", majorVersion: 2, minor: 0)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
|
@ -45,13 +45,17 @@ internal class BinaryReader {
|
||||||
private let endian: Endian
|
private let endian: Endian
|
||||||
private let closeOnDeinit: Bool
|
private let closeOnDeinit: Bool
|
||||||
|
|
||||||
private var buffer = [UInt8](repeating: 0, count: 4)
|
private var buffer = malloc(4).assumingMemoryBound(to: UInt8.self)
|
||||||
|
|
||||||
private var tempBuffer = [UInt8](repeating: 0, count: 4)
|
private var tempBuffer = malloc(4).assumingMemoryBound(to: UInt8.self)
|
||||||
private let tempBufferSize = 4
|
private let tempBufferSize = 4
|
||||||
private var tempBufferOffset = 0
|
private var tempBufferOffset = 0
|
||||||
|
|
||||||
internal init(stream: InputStream, endian: Endian = .unknown, closeOnDeinit: Bool = true) throws {
|
internal init(
|
||||||
|
stream: InputStream,
|
||||||
|
endian: Endian,
|
||||||
|
closeOnDeinit: Bool) throws {
|
||||||
|
|
||||||
var endian = endian
|
var endian = endian
|
||||||
|
|
||||||
if stream.streamStatus == .notOpen {
|
if stream.streamStatus == .notOpen {
|
||||||
|
@ -61,8 +65,8 @@ internal class BinaryReader {
|
||||||
throw CSVError.cannotOpenFile
|
throw CSVError.cannotOpenFile
|
||||||
}
|
}
|
||||||
|
|
||||||
let readCount = stream.read(&tempBuffer, maxLength: tempBufferSize)
|
let readCount = stream.read(tempBuffer, maxLength: tempBufferSize)
|
||||||
if let (e, l) = readBOM(buffer: &tempBuffer, length: readCount) {
|
if let (e, l) = readBOM(buffer: tempBuffer, length: readCount) {
|
||||||
if endian != .unknown && endian != e {
|
if endian != .unknown && endian != e {
|
||||||
throw CSVError.stringEndianMismatch
|
throw CSVError.stringEndianMismatch
|
||||||
}
|
}
|
||||||
|
@ -79,6 +83,8 @@ internal class BinaryReader {
|
||||||
if closeOnDeinit && stream.streamStatus != .closed {
|
if closeOnDeinit && stream.streamStatus != .closed {
|
||||||
stream.close()
|
stream.close()
|
||||||
}
|
}
|
||||||
|
free(buffer)
|
||||||
|
free(tempBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal var hasBytesAvailable: Bool {
|
internal var hasBytesAvailable: Bool {
|
||||||
|
@ -104,7 +110,7 @@ internal class BinaryReader {
|
||||||
|
|
||||||
internal func readUInt8() throws -> UInt8 {
|
internal func readUInt8() throws -> UInt8 {
|
||||||
let bufferSize = 1
|
let bufferSize = 1
|
||||||
let length = try readStream(&buffer, maxLength: bufferSize)
|
let length = try readStream(buffer, maxLength: bufferSize)
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
throw CSVError.streamErrorHasOccurred(error: stream.streamError!)
|
throw CSVError.streamErrorHasOccurred(error: stream.streamError!)
|
||||||
}
|
}
|
||||||
|
@ -116,14 +122,14 @@ internal class BinaryReader {
|
||||||
|
|
||||||
internal func readUInt16() throws -> UInt16 {
|
internal func readUInt16() throws -> UInt16 {
|
||||||
let bufferSize = 2
|
let bufferSize = 2
|
||||||
let length = try readStream(&buffer, maxLength: bufferSize)
|
let length = try readStream(buffer, maxLength: bufferSize)
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
throw CSVError.streamErrorHasOccurred(error: stream.streamError!)
|
throw CSVError.streamErrorHasOccurred(error: stream.streamError!)
|
||||||
}
|
}
|
||||||
if length != bufferSize {
|
if length != bufferSize {
|
||||||
throw CSVError.stringEncodingMismatch
|
throw CSVError.stringEncodingMismatch
|
||||||
}
|
}
|
||||||
return try UnsafePointer(buffer).withMemoryRebound(to: UInt16.self, capacity: 1) {
|
return try buffer.withMemoryRebound(to: UInt16.self, capacity: 1) {
|
||||||
switch endian {
|
switch endian {
|
||||||
case .big:
|
case .big:
|
||||||
return UInt16(bigEndian: $0[0])
|
return UInt16(bigEndian: $0[0])
|
||||||
|
@ -137,14 +143,14 @@ internal class BinaryReader {
|
||||||
|
|
||||||
internal func readUInt32() throws -> UInt32 {
|
internal func readUInt32() throws -> UInt32 {
|
||||||
let bufferSize = 4
|
let bufferSize = 4
|
||||||
let length = try readStream(&buffer, maxLength: bufferSize)
|
let length = try readStream(buffer, maxLength: bufferSize)
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
throw CSVError.streamErrorHasOccurred(error: stream.streamError!)
|
throw CSVError.streamErrorHasOccurred(error: stream.streamError!)
|
||||||
}
|
}
|
||||||
if length != 4 {
|
if length != bufferSize {
|
||||||
throw CSVError.stringEncodingMismatch
|
throw CSVError.stringEncodingMismatch
|
||||||
}
|
}
|
||||||
return try UnsafePointer(buffer).withMemoryRebound(to: UInt32.self, capacity: 1) {
|
return try buffer.withMemoryRebound(to: UInt32.self, capacity: 1) {
|
||||||
switch endian {
|
switch endian {
|
||||||
case .big:
|
case .big:
|
||||||
return UInt32(bigEndian: $0[0])
|
return UInt32(bigEndian: $0[0])
|
||||||
|
@ -160,22 +166,23 @@ internal class BinaryReader {
|
||||||
|
|
||||||
extension BinaryReader {
|
extension BinaryReader {
|
||||||
|
|
||||||
internal struct UInt8Iterator: Sequence, IteratorProtocol {
|
internal class UInt8Iterator: Sequence, IteratorProtocol {
|
||||||
|
|
||||||
private let reader: BinaryReader
|
private let reader: BinaryReader
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
fileprivate init(reader: BinaryReader) {
|
fileprivate init(reader: BinaryReader) {
|
||||||
self.reader = reader
|
self.reader = reader
|
||||||
}
|
}
|
||||||
|
|
||||||
internal mutating func next() -> UInt8? {
|
internal func next() -> UInt8? {
|
||||||
if !reader.hasBytesAvailable {
|
if !reader.hasBytesAvailable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
return try reader.readUInt8()
|
return try reader.readUInt8()
|
||||||
}
|
} catch {
|
||||||
catch {
|
errorHandler?(error)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,22 +197,23 @@ extension BinaryReader {
|
||||||
|
|
||||||
extension BinaryReader {
|
extension BinaryReader {
|
||||||
|
|
||||||
internal struct UInt16Iterator: Sequence, IteratorProtocol {
|
internal class UInt16Iterator: Sequence, IteratorProtocol {
|
||||||
|
|
||||||
private let reader: BinaryReader
|
private let reader: BinaryReader
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
fileprivate init(reader: BinaryReader) {
|
fileprivate init(reader: BinaryReader) {
|
||||||
self.reader = reader
|
self.reader = reader
|
||||||
}
|
}
|
||||||
|
|
||||||
internal mutating func next() -> UInt16? {
|
internal func next() -> UInt16? {
|
||||||
if !reader.hasBytesAvailable {
|
if !reader.hasBytesAvailable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
return try reader.readUInt16()
|
return try reader.readUInt16()
|
||||||
}
|
} catch {
|
||||||
catch {
|
errorHandler?(error)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,22 +228,23 @@ extension BinaryReader {
|
||||||
|
|
||||||
extension BinaryReader {
|
extension BinaryReader {
|
||||||
|
|
||||||
internal struct UInt32Iterator: Sequence, IteratorProtocol {
|
internal class UInt32Iterator: Sequence, IteratorProtocol {
|
||||||
|
|
||||||
private let reader: BinaryReader
|
private let reader: BinaryReader
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
fileprivate init(reader: BinaryReader) {
|
fileprivate init(reader: BinaryReader) {
|
||||||
self.reader = reader
|
self.reader = reader
|
||||||
}
|
}
|
||||||
|
|
||||||
internal mutating func next() -> UInt32? {
|
internal func next() -> UInt32? {
|
||||||
if !reader.hasBytesAvailable {
|
if !reader.hasBytesAvailable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
return try reader.readUInt32()
|
return try reader.readUInt32()
|
||||||
}
|
} catch {
|
||||||
catch {
|
errorHandler?(error)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
//
|
|
||||||
// CSV+init.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/13.
|
|
||||||
// Copyright © 2016 yaslab. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension CSV {
|
|
||||||
|
|
||||||
// TODO: Documentation
|
|
||||||
/// No overview available.
|
|
||||||
public init(
|
|
||||||
stream: InputStream,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
trimFields: Bool = defaultTrimFields,
|
|
||||||
delimiter: UnicodeScalar = defaultDelimiter)
|
|
||||||
throws
|
|
||||||
{
|
|
||||||
try self.init(stream: stream, codecType: UTF8.self, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CSV {
|
|
||||||
|
|
||||||
// TODO: Documentation
|
|
||||||
/// No overview available.
|
|
||||||
public init(
|
|
||||||
string: String,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
trimFields: Bool = defaultTrimFields,
|
|
||||||
delimiter: UnicodeScalar = defaultDelimiter)
|
|
||||||
throws
|
|
||||||
{
|
|
||||||
let iterator = string.unicodeScalars.makeIterator()
|
|
||||||
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
//
|
|
||||||
// CSV+subscript.swift
|
|
||||||
// CSV
|
|
||||||
//
|
|
||||||
// Created by Yasuhiro Hatta on 2016/06/13.
|
|
||||||
// Copyright © 2016 yaslab. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
extension CSV {
|
|
||||||
|
|
||||||
// TODO: Documentation
|
|
||||||
/// No overview available.
|
|
||||||
public subscript(key: String) -> String? {
|
|
||||||
get {
|
|
||||||
guard let headerRow = headerRow, let currentRow = currentRow else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard let index = headerRow.index(of: key) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if index >= currentRow.count {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return currentRow[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,255 +6,6 @@
|
||||||
// Copyright © 2016 yaslab. All rights reserved.
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
public typealias CSV = CSVReader
|
||||||
|
|
||||||
private let LF = UnicodeScalar("\n")!
|
|
||||||
private let CR = UnicodeScalar("\r")!
|
|
||||||
private let DQUOTE = UnicodeScalar("\"")!
|
|
||||||
|
|
||||||
internal let defaultHasHeaderRow = false
|
|
||||||
internal let defaultTrimFields = false
|
|
||||||
internal let defaultDelimiter = UnicodeScalar(",")!
|
|
||||||
|
|
||||||
extension CSV: Sequence { }
|
extension CSV: Sequence { }
|
||||||
|
|
||||||
extension CSV: IteratorProtocol {
|
|
||||||
|
|
||||||
// TODO: Documentation
|
|
||||||
/// No overview available.
|
|
||||||
public mutating func next() -> [String]? {
|
|
||||||
return readRow()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Documentation
|
|
||||||
/// No overview available.
|
|
||||||
public struct CSV {
|
|
||||||
|
|
||||||
private var iterator: AnyIterator<UnicodeScalar>
|
|
||||||
private let trimFields: Bool
|
|
||||||
private let delimiter: UnicodeScalar
|
|
||||||
|
|
||||||
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.
|
|
||||||
public var headerRow: [String]? { return _headerRow }
|
|
||||||
private var _headerRow: [String]? = nil
|
|
||||||
|
|
||||||
private let whitespaces: CharacterSet
|
|
||||||
|
|
||||||
internal init<T: IteratorProtocol>(
|
|
||||||
iterator: T,
|
|
||||||
hasHeaderRow: Bool,
|
|
||||||
trimFields: Bool,
|
|
||||||
delimiter: UnicodeScalar)
|
|
||||||
throws
|
|
||||||
where T.Element == UnicodeScalar
|
|
||||||
{
|
|
||||||
self.iterator = AnyIterator(iterator)
|
|
||||||
self.trimFields = trimFields
|
|
||||||
self.delimiter = delimiter
|
|
||||||
|
|
||||||
var whitespaces = CharacterSet.whitespaces
|
|
||||||
whitespaces.remove(delimiter)
|
|
||||||
self.whitespaces = whitespaces
|
|
||||||
|
|
||||||
if hasHeaderRow {
|
|
||||||
guard let headerRow = next() else {
|
|
||||||
throw CSVError.cannotReadHeaderRow
|
|
||||||
}
|
|
||||||
_headerRow = headerRow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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: UnicodeCodec>(
|
|
||||||
stream: InputStream,
|
|
||||||
codecType: T.Type,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
trimFields: Bool = defaultTrimFields,
|
|
||||||
delimiter: UnicodeScalar = defaultDelimiter)
|
|
||||||
throws
|
|
||||||
where T.CodeUnit == UInt8
|
|
||||||
{
|
|
||||||
let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true)
|
|
||||||
let iterator = UnicodeIterator(input: reader.makeUInt8Iterator(), inputEncodingType: codecType)
|
|
||||||
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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: UnicodeCodec>(
|
|
||||||
stream: InputStream,
|
|
||||||
codecType: T.Type,
|
|
||||||
endian: Endian = .big,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
trimFields: Bool = defaultTrimFields,
|
|
||||||
delimiter: UnicodeScalar = defaultDelimiter)
|
|
||||||
throws
|
|
||||||
where T.CodeUnit == UInt16
|
|
||||||
{
|
|
||||||
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
|
|
||||||
let iterator = UnicodeIterator(input: reader.makeUInt16Iterator(), inputEncodingType: codecType)
|
|
||||||
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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: UnicodeCodec>(
|
|
||||||
stream: InputStream,
|
|
||||||
codecType: T.Type,
|
|
||||||
endian: Endian = .big,
|
|
||||||
hasHeaderRow: Bool = defaultHasHeaderRow,
|
|
||||||
trimFields: Bool = defaultTrimFields,
|
|
||||||
delimiter: UnicodeScalar = defaultDelimiter)
|
|
||||||
throws
|
|
||||||
where T.CodeUnit == UInt32
|
|
||||||
{
|
|
||||||
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
|
|
||||||
let iterator = UnicodeIterator(input: reader.makeUInt32Iterator(), inputEncodingType: codecType)
|
|
||||||
try self.init(iterator: iterator, hasHeaderRow: hasHeaderRow, trimFields: trimFields, delimiter: delimiter)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate mutating func readRow() -> [String]? {
|
|
||||||
currentRow = nil
|
|
||||||
|
|
||||||
var next = moveNext()
|
|
||||||
if next == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var row = [String]()
|
|
||||||
var field: String
|
|
||||||
var end: Bool
|
|
||||||
while true {
|
|
||||||
if trimFields {
|
|
||||||
// Trim the leading spaces
|
|
||||||
while next != nil && whitespaces.contains(next!) {
|
|
||||||
next = moveNext()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if next == nil {
|
|
||||||
(field, end) = ("", true)
|
|
||||||
}
|
|
||||||
else if next == DQUOTE {
|
|
||||||
(field, end) = readField(quoted: true)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
back = next
|
|
||||||
(field, end) = readField(quoted: false)
|
|
||||||
|
|
||||||
if trimFields {
|
|
||||||
// Trim the trailing spaces
|
|
||||||
field = field.trimmingCharacters(in: whitespaces)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
row.append(field)
|
|
||||||
if end {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
next = moveNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRow = row
|
|
||||||
return row
|
|
||||||
}
|
|
||||||
|
|
||||||
private mutating func readField(quoted: Bool) -> (String, Bool) {
|
|
||||||
var field = ""
|
|
||||||
|
|
||||||
var next = moveNext()
|
|
||||||
while let c = next {
|
|
||||||
if quoted {
|
|
||||||
if c == DQUOTE {
|
|
||||||
var cNext = moveNext()
|
|
||||||
|
|
||||||
if trimFields {
|
|
||||||
// Trim the trailing spaces
|
|
||||||
while cNext != nil && whitespaces.contains(cNext!) {
|
|
||||||
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(String(DQUOTE))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// ERROR?
|
|
||||||
field.append(String(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
field.append(String(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(String(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next = moveNext()
|
|
||||||
}
|
|
||||||
|
|
||||||
// END FILE
|
|
||||||
return (field, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private mutating func moveNext() -> UnicodeScalar? {
|
|
||||||
if back != nil {
|
|
||||||
defer { back = nil }
|
|
||||||
return back
|
|
||||||
}
|
|
||||||
return iterator.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
// Copyright © 2016 yaslab. All rights reserved.
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
// TODO: Documentation
|
|
||||||
/// No overview available.
|
/// No overview available.
|
||||||
public enum CSVError: Error {
|
public enum CSVError: Error {
|
||||||
|
|
||||||
|
@ -15,8 +14,12 @@ public enum CSVError: Error {
|
||||||
/// No overview available.
|
/// No overview available.
|
||||||
case cannotReadFile
|
case cannotReadFile
|
||||||
/// No overview available.
|
/// No overview available.
|
||||||
|
case cannotWriteStream
|
||||||
|
/// No overview available.
|
||||||
case streamErrorHasOccurred(error: Error)
|
case streamErrorHasOccurred(error: Error)
|
||||||
/// No overview available.
|
/// No overview available.
|
||||||
|
case unicodeDecoding
|
||||||
|
/// No overview available.
|
||||||
case cannotReadHeaderRow
|
case cannotReadHeaderRow
|
||||||
/// No overview available.
|
/// No overview available.
|
||||||
case stringEncodingMismatch
|
case stringEncodingMismatch
|
||||||
|
|
|
@ -0,0 +1,399 @@
|
||||||
|
//
|
||||||
|
// CSVReader.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2016/06/11.
|
||||||
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
internal let LF: UnicodeScalar = "\n"
|
||||||
|
internal let CR: UnicodeScalar = "\r"
|
||||||
|
internal let DQUOTE: UnicodeScalar = "\""
|
||||||
|
|
||||||
|
internal let DQUOTE_STR: String = "\""
|
||||||
|
internal let DQUOTE2_STR: String = "\"\""
|
||||||
|
|
||||||
|
internal let defaultHasHeaderRow = false
|
||||||
|
internal let defaultTrimFields = false
|
||||||
|
internal let defaultDelimiter: UnicodeScalar = ","
|
||||||
|
internal let defaultWhitespaces = CharacterSet.whitespaces
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
public class CSVReader {
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
public struct Configuration {
|
||||||
|
|
||||||
|
/// `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
public var hasHeaderRow: Bool
|
||||||
|
/// No overview available.
|
||||||
|
public var trimFields: Bool
|
||||||
|
/// Default: `","`.
|
||||||
|
public var delimiter: UnicodeScalar
|
||||||
|
/// No overview available.
|
||||||
|
public var whitespaces: CharacterSet
|
||||||
|
|
||||||
|
/// No overview available.
|
||||||
|
internal init(
|
||||||
|
hasHeaderRow: Bool,
|
||||||
|
trimFields: Bool,
|
||||||
|
delimiter: UnicodeScalar,
|
||||||
|
whitespaces: CharacterSet) {
|
||||||
|
|
||||||
|
self.hasHeaderRow = hasHeaderRow
|
||||||
|
self.trimFields = trimFields
|
||||||
|
self.delimiter = delimiter
|
||||||
|
|
||||||
|
var whitespaces = whitespaces
|
||||||
|
_ = whitespaces.remove(delimiter)
|
||||||
|
self.whitespaces = whitespaces
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var iterator: AnyIterator<UnicodeScalar>
|
||||||
|
public let configuration: Configuration
|
||||||
|
public fileprivate (set) var error: Error?
|
||||||
|
|
||||||
|
fileprivate var back: UnicodeScalar?
|
||||||
|
fileprivate var fieldBuffer = String.UnicodeScalarView()
|
||||||
|
|
||||||
|
fileprivate var currentRowIndex: Int = 0
|
||||||
|
fileprivate var currentFieldIndex: Int = 0
|
||||||
|
|
||||||
|
/// CSV header row. To set a value for this property,
|
||||||
|
/// you set `true` to `headerRow` in initializer.
|
||||||
|
public private (set) var headerRow: [String]?
|
||||||
|
|
||||||
|
public fileprivate (set) var currentRow: [String]?
|
||||||
|
|
||||||
|
internal init<T: IteratorProtocol>(
|
||||||
|
iterator: T,
|
||||||
|
configuration: Configuration
|
||||||
|
) throws where T.Element == UnicodeScalar {
|
||||||
|
|
||||||
|
self.iterator = AnyIterator(iterator)
|
||||||
|
self.configuration = configuration
|
||||||
|
|
||||||
|
if configuration.hasHeaderRow {
|
||||||
|
guard let headerRow = readRow() else {
|
||||||
|
throw CSVError.cannotReadHeaderRow
|
||||||
|
}
|
||||||
|
self.headerRow = headerRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVReader {
|
||||||
|
|
||||||
|
/// 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 convenience init<T: UnicodeCodec>(
|
||||||
|
stream: InputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws where T.CodeUnit == UInt8 {
|
||||||
|
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: .unknown, closeOnDeinit: true)
|
||||||
|
let input = reader.makeUInt8Iterator()
|
||||||
|
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
input.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
iterator.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 convenience init<T: UnicodeCodec>(
|
||||||
|
stream: InputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws where T.CodeUnit == UInt16 {
|
||||||
|
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
|
||||||
|
let input = reader.makeUInt16Iterator()
|
||||||
|
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
input.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
iterator.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 convenience init<T: UnicodeCodec>(
|
||||||
|
stream: InputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws where T.CodeUnit == UInt32 {
|
||||||
|
|
||||||
|
let reader = try BinaryReader(stream: stream, endian: endian, closeOnDeinit: true)
|
||||||
|
let input = reader.makeUInt32Iterator()
|
||||||
|
let iterator = UnicodeIterator(input: input, inputEncodingType: codecType)
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
input.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
iterator.errorHandler = { [unowned self] in self.errorHandler(error: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance with `InputStream`.
|
||||||
|
///
|
||||||
|
/// - parameter stream: An `InputStream` 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 delimiter: Default: `","`.
|
||||||
|
public convenience init(
|
||||||
|
stream: InputStream,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws {
|
||||||
|
|
||||||
|
try self.init(
|
||||||
|
stream: stream,
|
||||||
|
codecType: UTF8.self,
|
||||||
|
hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an instance with CSV string.
|
||||||
|
///
|
||||||
|
/// - parameter string: An CSV string.
|
||||||
|
/// - parameter hasHeaderRow: `true` if the CSV has a header row, otherwise `false`. Default: `false`.
|
||||||
|
/// - parameter delimiter: Default: `","`.
|
||||||
|
public convenience init(
|
||||||
|
string: String,
|
||||||
|
hasHeaderRow: Bool = defaultHasHeaderRow,
|
||||||
|
trimFields: Bool = defaultTrimFields,
|
||||||
|
delimiter: UnicodeScalar = defaultDelimiter,
|
||||||
|
whitespaces: CharacterSet = defaultWhitespaces
|
||||||
|
) throws {
|
||||||
|
|
||||||
|
let iterator = string.unicodeScalars.makeIterator()
|
||||||
|
let config = Configuration(hasHeaderRow: hasHeaderRow,
|
||||||
|
trimFields: trimFields,
|
||||||
|
delimiter: delimiter,
|
||||||
|
whitespaces: whitespaces)
|
||||||
|
try self.init(iterator: iterator, configuration: config)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func errorHandler(error: Error) {
|
||||||
|
//configuration.fileInputErrorHandler?(error, currentRowIndex, currentFieldIndex)
|
||||||
|
self.error = error
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Parse CSV
|
||||||
|
|
||||||
|
extension CSVReader {
|
||||||
|
|
||||||
|
fileprivate func readRow() -> [String]? {
|
||||||
|
currentFieldIndex = 0
|
||||||
|
|
||||||
|
var c = moveNext()
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var row = [String]()
|
||||||
|
var field: String
|
||||||
|
var end: Bool
|
||||||
|
while true {
|
||||||
|
if configuration.trimFields {
|
||||||
|
// Trim the leading spaces
|
||||||
|
while c != nil && configuration.whitespaces.contains(c!) {
|
||||||
|
c = moveNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == nil {
|
||||||
|
(field, end) = ("", true)
|
||||||
|
} else if c == DQUOTE {
|
||||||
|
(field, end) = readField(quoted: true)
|
||||||
|
} else {
|
||||||
|
back = c
|
||||||
|
(field, end) = readField(quoted: false)
|
||||||
|
|
||||||
|
if configuration.trimFields {
|
||||||
|
// Trim the trailing spaces
|
||||||
|
field = field.trimmingCharacters(in: configuration.whitespaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.append(field)
|
||||||
|
if end {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFieldIndex += 1
|
||||||
|
|
||||||
|
c = moveNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRowIndex += 1
|
||||||
|
|
||||||
|
currentRow = row
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
private func readField(quoted: Bool) -> (String, Bool) {
|
||||||
|
fieldBuffer.removeAll(keepingCapacity: true)
|
||||||
|
|
||||||
|
while let c = moveNext() {
|
||||||
|
if quoted {
|
||||||
|
if c == DQUOTE {
|
||||||
|
var cNext = moveNext()
|
||||||
|
|
||||||
|
if configuration.trimFields {
|
||||||
|
// Trim the trailing spaces
|
||||||
|
while cNext != nil && configuration.whitespaces.contains(cNext!) {
|
||||||
|
cNext = moveNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cNext == nil || cNext == CR || cNext == LF {
|
||||||
|
if cNext == CR {
|
||||||
|
let cNextNext = moveNext()
|
||||||
|
if cNextNext != LF {
|
||||||
|
back = cNextNext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END ROW
|
||||||
|
return (String(fieldBuffer), true)
|
||||||
|
} else if cNext == configuration.delimiter {
|
||||||
|
// END FIELD
|
||||||
|
return (String(fieldBuffer), false)
|
||||||
|
} else if cNext == DQUOTE {
|
||||||
|
// ESC
|
||||||
|
fieldBuffer.append(DQUOTE)
|
||||||
|
} else {
|
||||||
|
// ERROR?
|
||||||
|
fieldBuffer.append(c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fieldBuffer.append(c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if c == CR || c == LF {
|
||||||
|
if c == CR {
|
||||||
|
let cNext = moveNext()
|
||||||
|
if cNext != LF {
|
||||||
|
back = cNext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END ROW
|
||||||
|
return (String(fieldBuffer), true)
|
||||||
|
} else if c == configuration.delimiter {
|
||||||
|
// END FIELD
|
||||||
|
return (String(fieldBuffer), false)
|
||||||
|
} else {
|
||||||
|
fieldBuffer.append(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// END FILE
|
||||||
|
return (String(fieldBuffer), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func moveNext() -> UnicodeScalar? {
|
||||||
|
if back != nil {
|
||||||
|
defer {
|
||||||
|
back = nil
|
||||||
|
}
|
||||||
|
return back
|
||||||
|
}
|
||||||
|
return iterator.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//extension CSVReader {
|
||||||
|
//
|
||||||
|
// public func enumerateRows(_ block: (([String], [String]?, inout Bool) throws -> Void)) throws {
|
||||||
|
// var stop = false
|
||||||
|
// while let row = readRow() {
|
||||||
|
// try block(row, headerRow, &stop)
|
||||||
|
// if stop {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if let error = error {
|
||||||
|
// throw error
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
extension CSVReader: IteratorProtocol {
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public func next() -> [String]? {
|
||||||
|
return readRow()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVReader {
|
||||||
|
|
||||||
|
public subscript(key: String) -> String? {
|
||||||
|
guard let header = headerRow else {
|
||||||
|
fatalError("CSVReader.headerRow must not be nil")
|
||||||
|
}
|
||||||
|
guard let index = header.index(of: key) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let row = currentRow else {
|
||||||
|
fatalError("CSVReader.currentRow must not be nil")
|
||||||
|
}
|
||||||
|
if index >= row.count {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return row[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
//
|
||||||
|
// CSVWriter.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2017/05/28.
|
||||||
|
// Copyright © 2017 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class CSVWriter {
|
||||||
|
|
||||||
|
public struct Configuration {
|
||||||
|
|
||||||
|
public var delimiter: String
|
||||||
|
public var newline: String
|
||||||
|
|
||||||
|
internal init(delimiter: String, newline: Newline) {
|
||||||
|
self.delimiter = delimiter
|
||||||
|
|
||||||
|
switch newline {
|
||||||
|
case .lf: self.newline = String(LF)
|
||||||
|
case .crlf: self.newline = String(CR) + String(LF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Newline {
|
||||||
|
|
||||||
|
/// "\n"
|
||||||
|
case lf
|
||||||
|
/// "\r\n"
|
||||||
|
case crlf
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public let stream: OutputStream
|
||||||
|
public let configuration: Configuration
|
||||||
|
fileprivate let writeScalar: ((UnicodeScalar) throws -> Void)
|
||||||
|
|
||||||
|
fileprivate var isFirstRow: Bool = true
|
||||||
|
fileprivate var isFirstField: Bool = true
|
||||||
|
|
||||||
|
fileprivate init(
|
||||||
|
stream: OutputStream,
|
||||||
|
configuration: Configuration,
|
||||||
|
writeScalar: @escaping ((UnicodeScalar) throws -> Void)) throws {
|
||||||
|
|
||||||
|
self.stream = stream
|
||||||
|
self.configuration = configuration
|
||||||
|
self.writeScalar = writeScalar
|
||||||
|
|
||||||
|
if stream.streamStatus == .notOpen {
|
||||||
|
stream.open()
|
||||||
|
}
|
||||||
|
if stream.streamStatus != .open {
|
||||||
|
throw CSVError.cannotOpenFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if stream.streamStatus == .open {
|
||||||
|
stream.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVWriter {
|
||||||
|
|
||||||
|
public convenience init(
|
||||||
|
stream: OutputStream,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws {
|
||||||
|
|
||||||
|
try self.init(stream: stream, codecType: UTF8.self, delimiter: delimiter, newline: newline)
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: OutputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws where T.CodeUnit == UInt8 {
|
||||||
|
|
||||||
|
let config = Configuration(delimiter: delimiter, newline: newline)
|
||||||
|
try self.init(stream: stream, configuration: config) { (scalar: UnicodeScalar) throws in
|
||||||
|
var error: CSVError? = nil
|
||||||
|
codecType.encode(scalar) { (code: UInt8) in
|
||||||
|
var code = code
|
||||||
|
let count = stream.write(&code, maxLength: 1)
|
||||||
|
if count != 1 {
|
||||||
|
error = CSVError.cannotWriteStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: OutputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws where T.CodeUnit == UInt16 {
|
||||||
|
|
||||||
|
let config = Configuration(delimiter: delimiter, newline: newline)
|
||||||
|
try self.init(stream: stream, configuration: config) { (scalar: UnicodeScalar) throws in
|
||||||
|
var error: CSVError? = nil
|
||||||
|
codecType.encode(scalar) { (code: UInt16) in
|
||||||
|
var code = (endian == .big) ? code.bigEndian : code.littleEndian
|
||||||
|
withUnsafeBytes(of: &code) { (buffer) -> Void in
|
||||||
|
let count = stream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||||
|
maxLength: buffer.count)
|
||||||
|
if count != buffer.count {
|
||||||
|
error = CSVError.cannotWriteStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init<T: UnicodeCodec>(
|
||||||
|
stream: OutputStream,
|
||||||
|
codecType: T.Type,
|
||||||
|
endian: Endian = .big,
|
||||||
|
delimiter: String = String(defaultDelimiter),
|
||||||
|
newline: Newline = .lf
|
||||||
|
) throws where T.CodeUnit == UInt32 {
|
||||||
|
|
||||||
|
let config = Configuration(delimiter: delimiter, newline: newline)
|
||||||
|
try self.init(stream: stream, configuration: config) { (scalar: UnicodeScalar) throws in
|
||||||
|
var error: CSVError? = nil
|
||||||
|
codecType.encode(scalar) { (code: UInt32) in
|
||||||
|
var code = (endian == .big) ? code.bigEndian : code.littleEndian
|
||||||
|
withUnsafeBytes(of: &code) { (buffer) -> Void in
|
||||||
|
let count = stream.write(buffer.baseAddress!.assumingMemoryBound(to: UInt8.self),
|
||||||
|
maxLength: buffer.count)
|
||||||
|
if count != buffer.count {
|
||||||
|
error = CSVError.cannotWriteStream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let error = error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CSVWriter {
|
||||||
|
|
||||||
|
public func beginNewRow() {
|
||||||
|
isFirstField = true
|
||||||
|
}
|
||||||
|
|
||||||
|
public func write(field value: String, quoted: Bool = false) throws {
|
||||||
|
if isFirstRow {
|
||||||
|
isFirstRow = false
|
||||||
|
} else {
|
||||||
|
if isFirstField {
|
||||||
|
try configuration.newline.unicodeScalars.forEach(writeScalar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFirstField {
|
||||||
|
isFirstField = false
|
||||||
|
} else {
|
||||||
|
try configuration.delimiter.unicodeScalars.forEach(writeScalar)
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = value
|
||||||
|
|
||||||
|
if quoted {
|
||||||
|
value = value.replacingOccurrences(of: DQUOTE_STR, with: DQUOTE2_STR)
|
||||||
|
try writeScalar(DQUOTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
try value.unicodeScalars.forEach(writeScalar)
|
||||||
|
|
||||||
|
if quoted {
|
||||||
|
try writeScalar(DQUOTE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func write(row values: [String], quotedAtIndex: ((Int) -> Bool) = { _ in false }) throws {
|
||||||
|
beginNewRow()
|
||||||
|
for (i, value) in values.enumerated() {
|
||||||
|
try write(field: value, quoted: quotedAtIndex(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,15 +6,14 @@
|
||||||
// Copyright © 2016 yaslab. All rights reserved.
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
// TODO: Documentation
|
/// Represents byte order.
|
||||||
/// No overview available.
|
|
||||||
public enum Endian {
|
public enum Endian {
|
||||||
|
|
||||||
/// No overview available.
|
/// Big endian.
|
||||||
case big
|
case big
|
||||||
/// No overview available.
|
/// Little endian.
|
||||||
case little
|
case little
|
||||||
/// No overview available.
|
/// Multibyte character sets.
|
||||||
case unknown
|
case unknown
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.1.2</string>
|
<string>2.0.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
// Copyright © 2016 yaslab. All rights reserved.
|
// Copyright © 2016 yaslab. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
internal struct UnicodeIterator<
|
internal class UnicodeIterator<
|
||||||
Input: IteratorProtocol,
|
Input: IteratorProtocol,
|
||||||
InputEncoding: UnicodeCodec>
|
InputEncoding: UnicodeCodec>
|
||||||
: IteratorProtocol
|
: IteratorProtocol
|
||||||
|
@ -14,17 +14,22 @@ internal struct UnicodeIterator<
|
||||||
|
|
||||||
private var input: Input
|
private var input: Input
|
||||||
private var inputEncoding: InputEncoding
|
private var inputEncoding: InputEncoding
|
||||||
|
internal var errorHandler: ((Error) -> Void)?
|
||||||
|
|
||||||
internal init(input: Input, inputEncodingType: InputEncoding.Type) {
|
internal init(input: Input, inputEncodingType: InputEncoding.Type) {
|
||||||
self.input = input
|
self.input = input
|
||||||
self.inputEncoding = inputEncodingType.init()
|
self.inputEncoding = inputEncodingType.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal mutating func next() -> UnicodeScalar? {
|
internal func next() -> UnicodeScalar? {
|
||||||
switch inputEncoding.decode(&input) {
|
switch inputEncoding.decode(&input) {
|
||||||
case .scalarValue(let c): return c
|
case .scalarValue(let c):
|
||||||
case .emptyInput: return nil
|
return c
|
||||||
case .error: return nil
|
case .emptyInput:
|
||||||
|
return nil
|
||||||
|
case .error:
|
||||||
|
errorHandler?(CSVError.unicodeDecoding)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,16 +22,22 @@ class CSVTests: XCTestCase {
|
||||||
("testEscapedQuotationMark2", testEscapedQuotationMark2),
|
("testEscapedQuotationMark2", testEscapedQuotationMark2),
|
||||||
("testEmptyField", testEmptyField),
|
("testEmptyField", testEmptyField),
|
||||||
("testDoubleQuoteBeforeLineBreak", testDoubleQuoteBeforeLineBreak),
|
("testDoubleQuoteBeforeLineBreak", testDoubleQuoteBeforeLineBreak),
|
||||||
("testSubscript", testSubscript),
|
("testCSVState1", testCSVState1),
|
||||||
("testCSVState1", testCSVState1)
|
("testSubscriptInt", testSubscriptInt),
|
||||||
|
("testSubscriptString1", testSubscriptString1),
|
||||||
|
("testSubscriptString2", testSubscriptString2),
|
||||||
|
("testToArray", testToArray)
|
||||||
|
//("testToDictionary1", testToDictionary1),
|
||||||
|
//("testToDictionary2", testToDictionary2)
|
||||||
]
|
]
|
||||||
|
|
||||||
func testOneLine() {
|
func testOneLine() {
|
||||||
let csv = "\"abc\",1,2"
|
let csv = "\"abc\",1,2"
|
||||||
var i = 0
|
var i = 0
|
||||||
for row in try! CSV(string: csv) {
|
|
||||||
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -42,10 +48,10 @@ class CSVTests: XCTestCase {
|
||||||
func testTwoLines() {
|
func testTwoLines() {
|
||||||
let csv = "\"abc\",1,2\n\"cde\",3,4"
|
let csv = "\"abc\",1,2\n\"cde\",3,4"
|
||||||
var i = 0
|
var i = 0
|
||||||
for row in try! CSV(string: csv) {
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
case 1: XCTAssertEqual(row, ["cde", "3", "4"])
|
case 1: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -56,10 +62,10 @@ class CSVTests: XCTestCase {
|
||||||
func testLastLineIsEmpty() {
|
func testLastLineIsEmpty() {
|
||||||
let csv = "\"abc\",1,2\n\"cde\",3,4\n"
|
let csv = "\"abc\",1,2\n\"cde\",3,4\n"
|
||||||
var i = 0
|
var i = 0
|
||||||
for row in try! CSV(string: csv) {
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
case 1: XCTAssertEqual(row, ["cde", "3", "4"])
|
case 1: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -70,11 +76,11 @@ class CSVTests: XCTestCase {
|
||||||
func testLastLineIsWhiteSpace() {
|
func testLastLineIsWhiteSpace() {
|
||||||
let csv = "\"abc\",1,2\n\"cde\",3,4\n "
|
let csv = "\"abc\",1,2\n\"cde\",3,4\n "
|
||||||
var i = 0
|
var i = 0
|
||||||
for row in try! CSV(string: csv) {
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
case 1: XCTAssertEqual(row, ["cde", "3", "4"])
|
case 1: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
case 2: XCTAssertEqual(row, [" "])
|
case 2: XCTAssertEqual(record, [" "])
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -85,11 +91,11 @@ class CSVTests: XCTestCase {
|
||||||
func testMiddleLineIsEmpty() {
|
func testMiddleLineIsEmpty() {
|
||||||
let csv = "\"abc\",1,2\n\n\"cde\",3,4"
|
let csv = "\"abc\",1,2\n\n\"cde\",3,4"
|
||||||
var i = 0
|
var i = 0
|
||||||
for row in try! CSV(string: csv) {
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
case 1: XCTAssertEqual(row, [""])
|
case 1: XCTAssertEqual(record, [""])
|
||||||
case 2: XCTAssertEqual(row, ["cde", "3", "4"])
|
case 2: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -99,46 +105,46 @@ class CSVTests: XCTestCase {
|
||||||
|
|
||||||
func testCommaInQuotationMarks() {
|
func testCommaInQuotationMarks() {
|
||||||
let csvString = "abab,\"cd,cd\",efef"
|
let csvString = "abab,\"cd,cd\",efef"
|
||||||
var csv = try! CSV(string: csvString)
|
let csv = try! CSVReader(string: csvString)
|
||||||
let row = csv.next()!
|
let record = csv.next()!
|
||||||
XCTAssertEqual(row, ["abab", "cd,cd", "efef"])
|
XCTAssertEqual(record, ["abab", "cd,cd", "efef"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEscapedQuotationMark1() {
|
func testEscapedQuotationMark1() {
|
||||||
let csvString = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer"
|
let csvString = "abab,\"\"\"cdcd\",efef\r\nzxcv,asdf,qwer"
|
||||||
var csv = try! CSV(string: csvString)
|
let csv = try! CSVReader(string: csvString)
|
||||||
var row = csv.next()!
|
var record = csv.next()!
|
||||||
XCTAssertEqual(row, ["abab", "\"cdcd", "efef"])
|
XCTAssertEqual(record, ["abab", "\"cdcd", "efef"])
|
||||||
row = csv.next()!
|
record = csv.next()!
|
||||||
XCTAssertEqual(row, ["zxcv", "asdf", "qwer"])
|
XCTAssertEqual(record, ["zxcv", "asdf", "qwer"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEscapedQuotationMark2() {
|
func testEscapedQuotationMark2() {
|
||||||
let csvString = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\""
|
let csvString = "abab,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\""
|
||||||
var csv = try! CSV(string: csvString)
|
let csv = try! CSVReader(string: csvString)
|
||||||
var row = csv.next()!
|
var record = csv.next()!
|
||||||
XCTAssertEqual(row, ["abab", "cdcd", "efef"])
|
XCTAssertEqual(record, ["abab", "cdcd", "efef"])
|
||||||
row = csv.next()!
|
record = csv.next()!
|
||||||
XCTAssertEqual(row, ["zxcv", "asdf", "qw\"er"])
|
XCTAssertEqual(record, ["zxcv", "asdf", "qw\"er"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEmptyField() {
|
func testEmptyField() {
|
||||||
let csvString = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
let csvString = "abab,,cdcd,efef\r\nzxcv,asdf,\"qw\"\"er\","
|
||||||
var csv = try! CSV(string: csvString)
|
let csv = try! CSVReader(string: csvString)
|
||||||
var row = csv.next()!
|
var record = csv.next()!
|
||||||
XCTAssertEqual(row, ["abab", "", "cdcd", "efef"])
|
XCTAssertEqual(record, ["abab", "", "cdcd", "efef"])
|
||||||
row = csv.next()!
|
record = csv.next()!
|
||||||
XCTAssertEqual(row, ["zxcv", "asdf", "qw\"er", ""])
|
XCTAssertEqual(record, ["zxcv", "asdf", "qw\"er", ""])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDoubleQuoteBeforeLineBreak() {
|
func testDoubleQuoteBeforeLineBreak() {
|
||||||
let csv = "\"abc\",1,\"2\"\n\n\"cde\",3,\"4\""
|
let csv = "\"abc\",1,\"2\"\n\n\"cde\",3,\"4\""
|
||||||
var i = 0
|
var i = 0
|
||||||
for row in try! CSV(string: csv) {
|
for record in AnyIterator(try! CSVReader(string: csv)) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0: XCTAssertEqual(row, ["abc", "1", "2"])
|
case 0: XCTAssertEqual(record, ["abc", "1", "2"])
|
||||||
case 1: XCTAssertEqual(row, [""])
|
case 1: XCTAssertEqual(record, [""])
|
||||||
case 2: XCTAssertEqual(row, ["cde", "3", "4"])
|
case 2: XCTAssertEqual(record, ["cde", "3", "4"])
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -146,38 +152,84 @@ class CSVTests: XCTestCase {
|
||||||
XCTAssertEqual(i, 3)
|
XCTAssertEqual(i, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSubscript() {
|
|
||||||
let csvString = "id,name\n001,hoge\n002,fuga"
|
|
||||||
var csv = try! CSV(string: csvString, hasHeaderRow: true)
|
|
||||||
var i = 0
|
|
||||||
while csv.next() != nil {
|
|
||||||
switch i {
|
|
||||||
case 0:
|
|
||||||
XCTAssertEqual(csv["id"], "001")
|
|
||||||
XCTAssertEqual(csv["name"], "hoge")
|
|
||||||
case 1:
|
|
||||||
XCTAssertEqual(csv["id"], "002")
|
|
||||||
XCTAssertEqual(csv["name"], "fuga")
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
XCTAssertEqual(i, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCSVState1() {
|
func testCSVState1() {
|
||||||
let it = "あ,い1,\"う\",えお\n,,x,".unicodeScalars.makeIterator()
|
let it = "あ,い1,\"う\",えお\n,,x,".unicodeScalars.makeIterator()
|
||||||
var csv = try! CSV(iterator: it, hasHeaderRow: defaultHasHeaderRow, trimFields: defaultTrimFields, delimiter: defaultDelimiter)
|
let config = CSVReader.Configuration(hasHeaderRow: false,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",",
|
||||||
|
whitespaces: .whitespaces)
|
||||||
|
let csv = try! CSVReader(iterator: it, configuration: config)
|
||||||
|
|
||||||
var rows = [[String]]()
|
var records = [[String]]()
|
||||||
|
|
||||||
while let row = csv.next() {
|
while let record = csv.next() {
|
||||||
rows.append(row)
|
records.append(record)
|
||||||
}
|
}
|
||||||
XCTAssertEqual(rows.count, 2)
|
XCTAssertEqual(records.count, 2)
|
||||||
XCTAssertEqual(rows[0], ["あ", "い1", "う", "えお"])
|
XCTAssertEqual(records[0], ["あ", "い1", "う", "えお"])
|
||||||
XCTAssertEqual(rows[1], ["", "", "x", ""])
|
XCTAssertEqual(records[1], ["", "", "x", ""])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSubscriptInt() {
|
||||||
|
let csvString = "a,bb,ccc"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
for record in AnyIterator(csv) {
|
||||||
|
XCTAssertEqual(record[0], "a")
|
||||||
|
XCTAssertEqual(record[1], "bb")
|
||||||
|
XCTAssertEqual(record[2], "ccc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSubscriptString1() {
|
||||||
|
let csvString = "key1,key2\nvalue1,value2"
|
||||||
|
let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
csv.next()
|
||||||
|
XCTAssertEqual(csv["key1"], "value1")
|
||||||
|
XCTAssertEqual(csv["key2"], "value2")
|
||||||
|
XCTAssertNil(csv["key9"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSubscriptString2() {
|
||||||
|
let csvString = "key1,key2\nvalue1"
|
||||||
|
let csv = try! CSVReader(string: csvString, hasHeaderRow: true)
|
||||||
|
csv.next()
|
||||||
|
XCTAssertEqual(csv["key1"], "value1")
|
||||||
|
XCTAssertNil(csv["key2"])
|
||||||
|
XCTAssertNil(csv["key9"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testToArray() {
|
||||||
|
let csvString = "1,2,3,4,5\n6,7,8,9,0"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
XCTAssertEqual(records[0], ["1", "2", "3", "4", "5"])
|
||||||
|
XCTAssertEqual(records[1], ["6", "7", "8", "9", "0"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// func testToDictionary1() {
|
||||||
|
// let csvString = "id,name\n1,name1\n2,name2"
|
||||||
|
// let config = CSVReader.Configuration(hasHeaderRow: true)
|
||||||
|
// let csv = try! CSVReader(string: csvString, configuration: config)
|
||||||
|
// let rows = AnyIterator(csv).map { $0.toDictionary() }
|
||||||
|
// XCTAssertEqual(rows[0]["id"], "1")
|
||||||
|
// XCTAssertEqual(rows[0]["name"], "name1")
|
||||||
|
// XCTAssertNil(rows[0]["xxx"])
|
||||||
|
// XCTAssertEqual(rows[1]["id"], "2")
|
||||||
|
// XCTAssertEqual(rows[1]["name"], "name2")
|
||||||
|
// XCTAssertNil(rows[1]["yyy"])
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func testToDictionary2() {
|
||||||
|
// let csvString = "id,name,id\n1,name1,11\n2,name2,22"
|
||||||
|
// let config = CSVReader.Configuration(hasHeaderRow: true)
|
||||||
|
// let csv = try! CSVReader(string: csvString, configuration: config)
|
||||||
|
// let rows = AnyIterator(csv).map { $0.toDictionary() }
|
||||||
|
// XCTAssertEqual(rows[0]["id"], "1")
|
||||||
|
// XCTAssertEqual(rows[0]["name"], "name1")
|
||||||
|
// XCTAssertNil(rows[0]["xxx"])
|
||||||
|
// XCTAssertEqual(rows[1]["id"], "2")
|
||||||
|
// XCTAssertEqual(rows[1]["name"], "name2")
|
||||||
|
// XCTAssertNil(rows[1]["yyy"])
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
//
|
||||||
|
// CSVWriterTests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2017/05/28.
|
||||||
|
// Copyright © 2017年 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
extension OutputStream {
|
||||||
|
|
||||||
|
var data: Data? {
|
||||||
|
guard let nsData = property(forKey: .dataWrittenToMemoryStreamKey) as? NSData else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Data(referencing: nsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class CSVWriterTests: XCTestCase {
|
||||||
|
|
||||||
|
static let allTests = [
|
||||||
|
("testSingleFieldSingleRecord", testSingleFieldSingleRecord),
|
||||||
|
("testSingleFieldMultipleRecord", testSingleFieldMultipleRecord),
|
||||||
|
("testMultipleFieldSingleRecord", testMultipleFieldSingleRecord),
|
||||||
|
("testMultipleFieldMultipleRecord", testMultipleFieldMultipleRecord),
|
||||||
|
("testQuoted", testQuoted),
|
||||||
|
("testQuotedNewline", testQuotedNewline),
|
||||||
|
("testEscapeQuote", testEscapeQuote),
|
||||||
|
("testDelimiter", testDelimiter),
|
||||||
|
("testNewline", testNewline),
|
||||||
|
("testUTF16BE", testUTF16BE),
|
||||||
|
("testUTF16LE", testUTF16LE),
|
||||||
|
("testUTF32BE", testUTF32BE),
|
||||||
|
("testUTF32LE", testUTF32LE)
|
||||||
|
]
|
||||||
|
|
||||||
|
let str = "TEST-test-1234-😄😆👨👩👧👦"
|
||||||
|
|
||||||
|
/// xxxx
|
||||||
|
func testSingleFieldSingleRecord() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx
|
||||||
|
/// xxxx
|
||||||
|
func testSingleFieldMultipleRecord() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1\n\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,xxxx
|
||||||
|
func testMultipleFieldSingleRecord() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1,\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,xxxx
|
||||||
|
/// xxxx,xxxx
|
||||||
|
func testMultipleFieldMultipleRecord() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1-1")
|
||||||
|
try! csv.write(field: str + "-1-2")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-2-1")
|
||||||
|
try! csv.write(field: str + "-2-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1-1,\(str)-1-2\n\(str)-2-1,\(str)-2-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "xxxx",xxxx
|
||||||
|
func testQuoted() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1", quoted: true)
|
||||||
|
try! csv.write(field: str + "-2") // quoted: false
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\"\(str)-1\",\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,"xx\nxx"
|
||||||
|
func testQuotedNewline() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1") // quoted: false
|
||||||
|
try! csv.write(field: str + "-\n-2", quoted: true)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1,\"\(str)-\n-2\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// xxxx,"xx""xx"
|
||||||
|
func testEscapeQuote() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1") // quoted: false
|
||||||
|
try! csv.write(field: str + "-\"-2", quoted: true)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1,\"\(str)-\"\"-2\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test delimiter: "\t"
|
||||||
|
func testDelimiter() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter.init(stream: stream, delimiter: "\t")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1\t\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test newline: "\r\n"
|
||||||
|
func testNewline() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter.init(stream: stream, newline: .crlf)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-1")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str + "-2")
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf8)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, "\(str)-1\r\n\(str)-2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF16 Big Endian
|
||||||
|
func testUTF16BE() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF16.self, endian: .big)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf16BigEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF16 Little Endian
|
||||||
|
func testUTF16LE() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF16.self, endian: .little)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf16LittleEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF32 Big Endian
|
||||||
|
func testUTF32BE() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF32.self, endian: .big)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf32BigEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UTF32 Little Endian
|
||||||
|
func testUTF32LE() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
stream.open()
|
||||||
|
|
||||||
|
let csv = try! CSVWriter(stream: stream, codecType: UTF32.self, endian: .little)
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: str)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
let data = stream.data!
|
||||||
|
let csvStr = String(data: data, encoding: .utf32LittleEndian)!
|
||||||
|
|
||||||
|
XCTAssertEqual(csvStr, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -124,12 +124,13 @@ class LineBreakTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func parse(csv: String) -> [[String]] {
|
private func parse(csv: String) -> [[String]] {
|
||||||
let reader = try! CSV(string: csv)
|
let reader = try! CSVReader(string: csv)
|
||||||
var records = [[String]]()
|
return reader.map { $0 }
|
||||||
for row in reader {
|
// var records = [[String]]()
|
||||||
records.append(row)
|
// try! reader.enumerateRows { (row, _, _) in
|
||||||
}
|
// records.append(row)
|
||||||
return records
|
// }
|
||||||
|
// return records
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,58 +13,102 @@ class ReadmeTests: XCTestCase {
|
||||||
|
|
||||||
static let allTests = [
|
static let allTests = [
|
||||||
("testFromCSVString", testFromCSVString),
|
("testFromCSVString", testFromCSVString),
|
||||||
("testFromFilePath", testFromFilePath),
|
("testFromFile", testFromFile),
|
||||||
("testGettingTheHeaderRow", testGettingTheHeaderRow),
|
("testGettingTheHeaderRow", testGettingTheHeaderRow),
|
||||||
("testGetTheFieldValueUsingSubscript", testGetTheFieldValueUsingSubscript),
|
("testGetTheFieldValueUsingKey", testGetTheFieldValueUsingKey),
|
||||||
("testProvideTheCharacterEncoding", testProvideTheCharacterEncoding)
|
("testProvideTheCharacterEncoding", testProvideTheCharacterEncoding),
|
||||||
|
("testWriteToMemory", testWriteToMemory),
|
||||||
|
("testWriteToFile", testWriteToFile)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// MARK: - Reading
|
||||||
|
|
||||||
func testFromCSVString() {
|
func testFromCSVString() {
|
||||||
for row in try! CSV(string: "1,foo\n2,bar") {
|
let csvString = "1,foo\n2,bar"
|
||||||
|
let csv = try! CSVReader(string: csvString)
|
||||||
|
while let row = csv.next() {
|
||||||
print("\(row)")
|
print("\(row)")
|
||||||
|
}
|
||||||
// => ["1", "foo"]
|
// => ["1", "foo"]
|
||||||
// => ["2", "bar"]
|
// => ["2", "bar"]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func testFromFilePath() {
|
func testFromFile() {
|
||||||
// let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
// let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
// for row in try! CSV(stream: stream) {
|
// let csv = try! CSVReader(stream: stream)
|
||||||
|
// while let row = csv.next() {
|
||||||
// print("\(row)")
|
// print("\(row)")
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGettingTheHeaderRow() {
|
func testGettingTheHeaderRow() {
|
||||||
let csv = try! CSV(
|
let csvString = "id,name\n1,foo\n2,bar"
|
||||||
string: "id,name\n1,foo\n2,bar",
|
let csv = try! CSVReader(string: csvString,
|
||||||
hasHeaderRow: true) // default: false
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
let headerRow = csv.headerRow!
|
let headerRow = csv.headerRow!
|
||||||
print("\(headerRow)") // => ["id", "name"]
|
print("\(headerRow)") // => ["id", "name"]
|
||||||
|
|
||||||
for row in csv {
|
while let row = csv.next() {
|
||||||
print("\(row)")
|
print("\(row)")
|
||||||
|
}
|
||||||
// => ["1", "foo"]
|
// => ["1", "foo"]
|
||||||
// => ["2", "bar"]
|
// => ["2", "bar"]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func testGetTheFieldValueUsingSubscript() {
|
func testGetTheFieldValueUsingKey() {
|
||||||
var csv = try! CSV(
|
let csvString = "id,name\n1,foo"
|
||||||
string: "id,name\n1,foo",
|
let csv = try! CSVReader(string: csvString,
|
||||||
hasHeaderRow: true) // It must be true.
|
hasHeaderRow: true) // It must be true.
|
||||||
|
|
||||||
while let _ = csv.next() {
|
while csv.next() != nil {
|
||||||
print("\(csv["id"]!)") // => "1"
|
print("\(csv["id"]!)") // => "1"
|
||||||
print("\(csv["name"]!)") // => "foo"
|
print("\(csv["name"]!)") // => "foo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testProvideTheCharacterEncoding() {
|
func testProvideTheCharacterEncoding() {
|
||||||
// let csv = try! CSV(
|
// let stream = InputStream(fileAtPath: "/path/to/file.csv")!
|
||||||
// stream: InputStream(fileAtPath: "/path/to/file.csv")!,
|
// let csv = try! CSV(stream: stream,
|
||||||
// codecType: UTF16.self,
|
// codecType: UTF16.self,
|
||||||
// endian: .big)
|
// endian: .big)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Writing
|
||||||
|
|
||||||
|
func testWriteToMemory() {
|
||||||
|
let stream = OutputStream(toMemory: ())
|
||||||
|
let csv = try! CSVWriter(stream: stream)
|
||||||
|
|
||||||
|
// Write a row
|
||||||
|
try! csv.write(row: ["id", "name"])
|
||||||
|
|
||||||
|
// Write fields separately
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "1")
|
||||||
|
try! csv.write(field: "foo")
|
||||||
|
csv.beginNewRow()
|
||||||
|
try! csv.write(field: "2")
|
||||||
|
try! csv.write(field: "bar")
|
||||||
|
|
||||||
|
csv.stream.close()
|
||||||
|
|
||||||
|
// Get a String
|
||||||
|
let csvData = stream.property(forKey: .dataWrittenToMemoryStreamKey) as! NSData
|
||||||
|
let csvString = String(data: Data(referencing: csvData), encoding: .utf8)!
|
||||||
|
print(csvString)
|
||||||
|
// => "id,name\n1,foo\n2,bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWriteToFile() {
|
||||||
|
// let stream = OutputStream(toFileAtPath: "/path/to/file.csv", append: false)!
|
||||||
|
// let csv = try! CSVWriter(stream: stream)
|
||||||
|
//
|
||||||
|
// try! csv.write(row: ["id", "name"])
|
||||||
|
// try! csv.write(row: ["1", "foo"])
|
||||||
|
// try! csv.write(row: ["1", "bar"])
|
||||||
|
//
|
||||||
|
// csv.stream.close()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,115 +24,160 @@ class TrimFieldsTests: XCTestCase {
|
||||||
("testTrimFields10", testTrimFields10),
|
("testTrimFields10", testTrimFields10),
|
||||||
("testTrimFields11", testTrimFields11),
|
("testTrimFields11", testTrimFields11),
|
||||||
("testTrimFields12", testTrimFields12),
|
("testTrimFields12", testTrimFields12),
|
||||||
("testTrimFields13", testTrimFields13),
|
("testTrimFields13", testTrimFields13)
|
||||||
]
|
]
|
||||||
|
|
||||||
func testTrimFields1() {
|
func testTrimFields1() {
|
||||||
let csvString = "abc,def,ghi"
|
let csvString = "abc,def,ghi"
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields2() {
|
func testTrimFields2() {
|
||||||
let csvString = " abc, def, ghi"
|
let csvString = " abc, def, ghi"
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields3() {
|
func testTrimFields3() {
|
||||||
let csvString = "abc ,def ,ghi "
|
let csvString = "abc ,def ,ghi "
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields4() {
|
func testTrimFields4() {
|
||||||
let csvString = " abc , def , ghi "
|
let csvString = " abc , def , ghi "
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields5() {
|
func testTrimFields5() {
|
||||||
let csvString = "\"abc\",\"def\",\"ghi\""
|
let csvString = "\"abc\",\"def\",\"ghi\""
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields6() {
|
func testTrimFields6() {
|
||||||
let csvString = " \"abc\", \"def\", \"ghi\""
|
let csvString = " \"abc\", \"def\", \"ghi\""
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields7() {
|
func testTrimFields7() {
|
||||||
let csvString = "\"abc\" ,\"def\" ,\"ghi\" "
|
let csvString = "\"abc\" ,\"def\" ,\"ghi\" "
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields8() {
|
func testTrimFields8() {
|
||||||
let csvString = " \"abc\" , \"def\" , \"ghi\" "
|
let csvString = " \"abc\" , \"def\" , \"ghi\" "
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields9() {
|
func testTrimFields9() {
|
||||||
let csvString = "\" abc \",\" def \",\" ghi \""
|
let csvString = "\" abc \",\" def \",\" ghi \""
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, [" abc ", " def ", " ghi "])
|
XCTAssertEqual(record, [" abc ", " def ", " ghi "])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields10() {
|
func testTrimFields10() {
|
||||||
let csvString = "\tabc,\t\tdef\t,ghi\t"
|
let csvString = "\tabc,\t\tdef\t,ghi\t"
|
||||||
let csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields11() {
|
func testTrimFields11() {
|
||||||
let csvString = " abc \n def "
|
let csvString = " abc \n def "
|
||||||
var csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
|
||||||
let row1 = csv.next()!
|
let record1 = csv.next()!
|
||||||
XCTAssertEqual(row1, ["abc"])
|
XCTAssertEqual(record1, ["abc"])
|
||||||
let row2 = csv.next()!
|
let record2 = csv.next()!
|
||||||
XCTAssertEqual(row2, ["def"])
|
XCTAssertEqual(record2, ["def"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields12() {
|
func testTrimFields12() {
|
||||||
let csvString = " \"abc \" \n \" def\" "
|
let csvString = " \"abc \" \n \" def\" "
|
||||||
var csv = try! CSV(string: csvString, trimFields: true)
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
|
||||||
let row1 = csv.next()!
|
let record1 = csv.next()!
|
||||||
XCTAssertEqual(row1, ["abc "])
|
XCTAssertEqual(record1, ["abc "])
|
||||||
let row2 = csv.next()!
|
let record2 = csv.next()!
|
||||||
XCTAssertEqual(row2, [" def"])
|
XCTAssertEqual(record2, [" def"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTrimFields13() {
|
func testTrimFields13() {
|
||||||
let csvString = " abc \t\tdef\t ghi "
|
let csvString = " abc \t\tdef\t ghi "
|
||||||
let csv = try! CSV(string: csvString, trimFields: true, delimiter: UnicodeScalar("\t")!)
|
let csv = try! CSVReader(string: csvString, trimFields: true, delimiter: "\t")
|
||||||
for row in csv {
|
for record in AnyIterator(csv) {
|
||||||
XCTAssertEqual(row, ["abc", "", "def", "ghi"])
|
XCTAssertEqual(record, ["abc", "", "def", "ghi"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testTrimFields14() {
|
||||||
|
let csvString = ""
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields15() {
|
||||||
|
let csvString = " "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 1)
|
||||||
|
XCTAssertEqual(records[0], [""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields16() {
|
||||||
|
let csvString = " , "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 1)
|
||||||
|
XCTAssertEqual(records[0], ["", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields17() {
|
||||||
|
let csvString = " , \n"
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 1)
|
||||||
|
XCTAssertEqual(records[0], ["", ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTrimFields18() {
|
||||||
|
let csvString = " , \n "
|
||||||
|
let csv = try! CSVReader(string: csvString, trimFields: true)
|
||||||
|
let records = AnyIterator(csv).map { $0 }
|
||||||
|
|
||||||
|
XCTAssertEqual(records.count, 2)
|
||||||
|
XCTAssertEqual(records[0], ["", ""])
|
||||||
|
XCTAssertEqual(records[1], [""])
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class UnicodeTests: XCTestCase {
|
||||||
mutableData.append(utf8BOM, count: utf8BOM.count)
|
mutableData.append(utf8BOM, count: utf8BOM.count)
|
||||||
mutableData.append(csvString.data(using: encoding)!)
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
let stream = InputStream(data: mutableData)
|
let stream = InputStream(data: mutableData)
|
||||||
let csv = try! CSV(stream: stream, codecType: UTF8.self)
|
let csv = try! CSVReader(stream: stream, codecType: UTF8.self)
|
||||||
let records = getRecords(csv: csv)
|
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", ""])
|
||||||
|
@ -41,7 +41,7 @@ class UnicodeTests: XCTestCase {
|
||||||
var mutableData = Data()
|
var mutableData = Data()
|
||||||
mutableData.append(csvString.data(using: encoding)!)
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
let stream = InputStream(data: mutableData as Data)
|
let stream = InputStream(data: mutableData as Data)
|
||||||
let csv = try! CSV(stream: stream, codecType: UTF16.self, endian: .unknown)
|
let csv = try! CSVReader(stream: stream, codecType: UTF16.self, endian: .unknown)
|
||||||
let records = getRecords(csv: csv)
|
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", ""])
|
||||||
|
@ -54,7 +54,7 @@ class UnicodeTests: XCTestCase {
|
||||||
mutableData.append(utf16BigEndianBOM, count: utf16BigEndianBOM.count)
|
mutableData.append(utf16BigEndianBOM, count: utf16BigEndianBOM.count)
|
||||||
mutableData.append(csvString.data(using: encoding)!)
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
let stream = InputStream(data: mutableData as Data)
|
let stream = InputStream(data: mutableData as Data)
|
||||||
let csv = try! CSV(stream: stream, codecType: UTF16.self, endian: .big)
|
let csv = try! CSVReader(stream: stream, codecType: UTF16.self, endian: .big)
|
||||||
let records = getRecords(csv: csv)
|
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", ""])
|
||||||
|
@ -67,7 +67,7 @@ class UnicodeTests: XCTestCase {
|
||||||
mutableData.append(utf16LittleEndianBOM, count: utf16LittleEndianBOM.count)
|
mutableData.append(utf16LittleEndianBOM, count: utf16LittleEndianBOM.count)
|
||||||
mutableData.append(csvString.data(using: encoding)!)
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
let stream = InputStream(data: mutableData as Data)
|
let stream = InputStream(data: mutableData as Data)
|
||||||
let csv = try! CSV(stream: stream, codecType: UTF16.self, endian: .little)
|
let csv = try! CSVReader(stream: stream, codecType: UTF16.self, endian: .little)
|
||||||
let records = getRecords(csv: csv)
|
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", ""])
|
||||||
|
@ -79,7 +79,7 @@ class UnicodeTests: XCTestCase {
|
||||||
var mutableData = Data()
|
var mutableData = Data()
|
||||||
mutableData.append(csvString.data(using: encoding)!)
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
let stream = InputStream(data: mutableData as Data)
|
let stream = InputStream(data: mutableData as Data)
|
||||||
let csv = try! CSV(stream: stream, codecType: UTF32.self, endian: .unknown)
|
let csv = try! CSVReader(stream: stream, codecType: UTF32.self, endian: .unknown)
|
||||||
let records = getRecords(csv: csv)
|
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", ""])
|
||||||
|
@ -92,7 +92,7 @@ class UnicodeTests: XCTestCase {
|
||||||
mutableData.append(utf32BigEndianBOM, count: utf32BigEndianBOM.count)
|
mutableData.append(utf32BigEndianBOM, count: utf32BigEndianBOM.count)
|
||||||
mutableData.append(csvString.data(using: encoding)!)
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
let stream = InputStream(data: mutableData as Data)
|
let stream = InputStream(data: mutableData as Data)
|
||||||
let csv = try! CSV(stream: stream, codecType: UTF32.self, endian: .big)
|
let csv = try! CSVReader(stream: stream, codecType: UTF32.self, endian: .big)
|
||||||
let records = getRecords(csv: csv)
|
let records = getRecords(csv: csv)
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cd😆cd", "efef"])
|
XCTAssertEqual(records[0], ["abab", "", "cd😆cd", "efef"])
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
|
@ -105,18 +105,19 @@ class UnicodeTests: XCTestCase {
|
||||||
mutableData.append(utf32LittleEndianBOM, count: utf32LittleEndianBOM.count)
|
mutableData.append(utf32LittleEndianBOM, count: utf32LittleEndianBOM.count)
|
||||||
mutableData.append(csvString.data(using: encoding)!)
|
mutableData.append(csvString.data(using: encoding)!)
|
||||||
let stream = InputStream(data: mutableData as Data)
|
let stream = InputStream(data: mutableData as Data)
|
||||||
let csv = try! CSV(stream: stream, codecType: UTF32.self, endian: .little)
|
let csv = try! CSVReader(stream: stream, codecType: UTF32.self, endian: .little)
|
||||||
let records = getRecords(csv: csv)
|
let records = getRecords(csv: csv)
|
||||||
XCTAssertEqual(records[0], ["abab", "", "cdcd", "ef😆ef"])
|
XCTAssertEqual(records[0], ["abab", "", "cdcd", "ef😆ef"])
|
||||||
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
XCTAssertEqual(records[1], ["zxcv", "asdf", "qw\"er", ""])
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getRecords(csv: CSV) -> [[String]] {
|
private func getRecords(csv: CSVReader) -> [[String]] {
|
||||||
var records = [[String]]()
|
return csv.map { $0 }
|
||||||
for row in csv {
|
// var records = [[String]]()
|
||||||
records.append(row)
|
// try! csv.enumerateRows { (record, _, _) in
|
||||||
}
|
// records.append(record)
|
||||||
return records
|
// }
|
||||||
|
// return records
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
//
|
||||||
|
// Version1Tests.swift
|
||||||
|
// CSV
|
||||||
|
//
|
||||||
|
// Created by Yasuhiro Hatta on 2017/06/18.
|
||||||
|
// Copyright © 2017 yaslab. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
import CSV
|
||||||
|
|
||||||
|
class Version1Tests: XCTestCase {
|
||||||
|
|
||||||
|
static let allTests = [
|
||||||
|
("testV1", testV1)
|
||||||
|
]
|
||||||
|
|
||||||
|
func testV1() {
|
||||||
|
let str = "a,b,c\n1,2,3"
|
||||||
|
let data8 = str.data(using: .utf8)!
|
||||||
|
let data16 = str.data(using: .utf16BigEndian)!
|
||||||
|
let data32 = str.data(using: .utf32BigEndian)!
|
||||||
|
|
||||||
|
let headerRow = ["a", "b", "c"]
|
||||||
|
let row = ["1", "2", "3"]
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data8)
|
||||||
|
var csv = try CSV(stream: stream,
|
||||||
|
codecType: UTF8.self,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data16)
|
||||||
|
var csv = try CSV(stream: stream,
|
||||||
|
codecType: UTF16.self,
|
||||||
|
endian: .big,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data32)
|
||||||
|
var csv = try CSV(stream: stream,
|
||||||
|
codecType: UTF32.self,
|
||||||
|
endian: .big,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let stream = InputStream(data: data8)
|
||||||
|
var csv = try CSV(stream: stream,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
var csv = try CSV(string: str,
|
||||||
|
hasHeaderRow: true,
|
||||||
|
trimFields: false,
|
||||||
|
delimiter: ",")
|
||||||
|
XCTAssertEqual(csv.headerRow!, headerRow)
|
||||||
|
XCTAssertEqual(csv.next()!, row)
|
||||||
|
XCTAssertEqual(csv["a"], row[0])
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = CSVError.cannotOpenFile
|
||||||
|
_ = CSVError.cannotReadFile
|
||||||
|
_ = CSVError.streamErrorHasOccurred(error: NSError(domain: "", code: 0, userInfo: nil))
|
||||||
|
_ = CSVError.cannotReadHeaderRow
|
||||||
|
_ = CSVError.stringEncodingMismatch
|
||||||
|
_ = CSVError.stringEndianMismatch
|
||||||
|
|
||||||
|
_ = Endian.big
|
||||||
|
_ = Endian.little
|
||||||
|
_ = Endian.unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 XCTest
|
import XCTest
|
||||||
|
@ -11,8 +11,10 @@ import XCTest
|
||||||
|
|
||||||
XCTMain([
|
XCTMain([
|
||||||
testCase(CSVTests.allTests),
|
testCase(CSVTests.allTests),
|
||||||
|
testCase(CSVWriterTests.allTests),
|
||||||
testCase(LineBreakTests.allTests),
|
testCase(LineBreakTests.allTests),
|
||||||
testCase(ReadmeTests.allTests),
|
testCase(ReadmeTests.allTests),
|
||||||
testCase(TrimFieldsTests.allTests),
|
testCase(TrimFieldsTests.allTests),
|
||||||
testCase(UnicodeTests.allTests)
|
testCase(UnicodeTests.allTests),
|
||||||
|
testCase(Version1Tests.allTests)
|
||||||
])
|
])
|
||||||
|
|
Loading…
Reference in New Issue