Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ada8ce6

Browse files
Fix #102: Quote configurable delimiter character, not just commas (#107)
1 parent 50a62e4 commit ada8ce6

File tree

6 files changed

+72
-36
lines changed

6 files changed

+72
-36
lines changed

‎SwiftCSV.xcodeproj/project.pbxproj‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
DF94FE462898F3A3008FD3F9 /* utf8_with_bom.csv in Resources */ = {isa = PBXBuildFile; fileRef = DF94FE452898F3A3008FD3F9 /* utf8_with_bom.csv */; };
8383
DF94FE472898F3A3008FD3F9 /* utf8_with_bom.csv in Resources */ = {isa = PBXBuildFile; fileRef = DF94FE452898F3A3008FD3F9 /* utf8_with_bom.csv */; };
8484
DF94FE482898F3A3008FD3F9 /* utf8_with_bom.csv in Resources */ = {isa = PBXBuildFile; fileRef = DF94FE452898F3A3008FD3F9 /* utf8_with_bom.csv */; };
85+
DFAD8B7B28B601EB0042BB56 /* Serializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFAD8B7A28B601EB0042BB56 /* Serializer.swift */; };
8586
E46085921CCB1E8F00385286 /* large.csv in Resources */ = {isa = PBXBuildFile; fileRef = E46085911CCB1E8F00385286 /* large.csv */; };
8687
E46085941CCB1F5C00385286 /* PerformanceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E46085931CCB1F5C00385286 /* PerformanceTest.swift */; };
8788
F5C19F502283243C00920B06 /* ResourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C19F4F2283243C00920B06 /* ResourceHelper.swift */; };
@@ -153,6 +154,7 @@
153154
BE6C86061CB5CE44009A351D /* QuotedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuotedTests.swift; sourceTree = "<group>"; };
154155
BE9B02D71CBE57B8009FE424 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
155156
DF94FE452898F3A3008FD3F9 /* utf8_with_bom.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = utf8_with_bom.csv; sourceTree = "<group>"; };
157+
DFAD8B7A28B601EB0042BB56 /* Serializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Serializer.swift; sourceTree = "<group>"; };
156158
E46085911CCB1E8F00385286 /* large.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = large.csv; sourceTree = "<group>"; };
157159
E46085931CCB1F5C00385286 /* PerformanceTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTest.swift; sourceTree = "<group>"; };
158160
F5C19F4F2283243C00920B06 /* ResourceHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResourceHelper.swift; sourceTree = "<group>"; };
@@ -255,6 +257,7 @@
255257
3D444BCC1C7D88290001C60C /* String+Lines.swift */,
256258
BE9B02D71CBE57B8009FE424 /* Parser.swift */,
257259
508975D61DBF34CF006F3DBE /* ParsingState.swift */,
260+
DFAD8B7A28B601EB0042BB56 /* Serializer.swift */,
258261
);
259262
path = SwiftCSV;
260263
sourceTree = "<group>";
@@ -588,6 +591,7 @@
588591
buildActionMask = 2147483647;
589592
files = (
590593
50B3EEA4286F8A84007B3956 /* CSVDelimiter.swift in Sources */,
594+
DFAD8B7B28B601EB0042BB56 /* Serializer.swift in Sources */,
591595
508975D21DBB897A006F3DBE /* NamedCSVView.swift in Sources */,
592596
508CA0FB2771F2E70084C8E8 /* CSV+DelimiterGuessing.swift in Sources */,
593597
3DAAEE9C1C74C7EC00A933DB /* CSV.swift in Sources */,

‎SwiftCSV/CSV.swift‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,6 @@ extension CSV: CustomStringConvertible {
128128
}
129129
}
130130

131-
func enquoteContentsIfNeeded(cell: String) -> String {
132-
// Add quotes if value contains a comma
133-
if cell.contains(",") {
134-
return "\"\(cell)\""
135-
}
136-
return cell
137-
}
138-
139131
extension CSV {
140132
/// Load a CSV file from `url`.
141133
///

‎SwiftCSV/EnumeratedCSVView.swift‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,9 @@ public struct Enumerated: CSVView {
4444
}
4545

4646
public func serialize(header: [String], delimiter: CSVDelimiter) -> String {
47-
let separator = String(delimiter.rawValue)
48-
49-
let head = header
50-
.map(enquoteContentsIfNeeded(cell:))
51-
.joined(separator: separator) + "\n"
52-
53-
let content = rows.map { row in
54-
row.map(enquoteContentsIfNeeded(cell:))
55-
.joined(separator: separator)
56-
}.joined(separator: "\n")
57-
58-
return head + content
47+
return Serializer.serialize(header: header, rows: rows, delimiter: delimiter)
5948
}
49+
6050
}
6151

6252
extension Collection {

‎SwiftCSV/NamedCSVView.swift‎

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,11 @@ public struct Named: CSVView {
3535
}
3636

3737
public func serialize(header: [String], delimiter: CSVDelimiter) -> String {
38-
let separator = String(delimiter.rawValue)
38+
let rowsOrderingCellsByHeader = rows.map { row in
39+
header.map { cellID in row[cellID]! }
40+
}
3941

40-
let head = header
41-
.map(enquoteContentsIfNeeded(cell:))
42-
.joined(separator: separator) + "\n"
43-
44-
let content = rows.map { row in
45-
header
46-
.map { cellID in row[cellID]! }
47-
.map(enquoteContentsIfNeeded(cell:))
48-
.joined(separator: separator)
49-
}.joined(separator: "\n")
50-
51-
return head + content
42+
return Serializer.serialize(header: header, rows: rowsOrderingCellsByHeader, delimiter: delimiter)
5243
}
44+
5345
}

‎SwiftCSV/Serializer.swift‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// Serializer.swift
3+
// SwiftCSV
4+
//
5+
6+
import Foundation
7+
8+
enum Serializer {
9+
10+
static let newline = "\n"
11+
12+
static func serialize(header: [String], rows: [[String]], delimiter: CSVDelimiter) -> String {
13+
let head = serializeRow(row: header, delimiter: delimiter) + newline
14+
15+
let content = rows.map { row in
16+
serializeRow(row: row, delimiter: delimiter)
17+
}.joined(separator: newline)
18+
19+
return head + content
20+
}
21+
22+
23+
static func serializeRow(row: [String], delimiter: CSVDelimiter) -> String {
24+
let separator = String(delimiter.rawValue)
25+
26+
let content = row.map { cell in
27+
cell.enquoted(whenContaining: separator)
28+
}.joined(separator: separator)
29+
30+
return content
31+
}
32+
33+
}
34+
35+
fileprivate extension String {
36+
37+
func enquoted(whenContaining separator: String) -> String {
38+
// Add quotes if value contains a delimiter
39+
if self.contains(separator) {
40+
return "\"\(self)\""
41+
}
42+
43+
return self
44+
}
45+
46+
}

‎SwiftCSVTests/EnumeratedCSVViewTests.swift‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,24 @@ class EnumeratedViewTests: XCTestCase {
7676
XCTAssertEqual(csv.columns, expectedColumns)
7777
}
7878

79-
func testSerialization() {
79+
func testSerialization() throws {
80+
// Comma-separated values.
8081
XCTAssertEqual(csv.serialized, "id,name,age\n1,Alice,18\n2,Bob,19\n3,Charlie,20")
81-
}
8282

83-
func testSerializationWithDoubleQuotes()throws{
83+
// Comma-separated values with double quotes and embedded delimiters in cells.
8484
csv = try CSV<Enumerated>(string: "id,\"the, name\",age\n1,\"Alice, In, Wonderland\",18\n2,Bob,19\n3,Charlie,20")
8585
XCTAssertEqual(csv.serialized, "id,\"the, name\",age\n1,\"Alice, In, Wonderland\",18\n2,Bob,19\n3,Charlie,20")
86+
87+
// Tab-separated values with implicit delimiter (delimiter guessing).
88+
csv = try CSV<Enumerated>(string: "id\tname\tage\n1\tAlice\t18\n2\tBob\t19\n3\tCharlie\t20")
89+
XCTAssertEqual(csv.serialized, "id\tname\tage\n1\tAlice\t18\n2\tBob\t19\n3\tCharlie\t20")
90+
91+
// Tab-separated values with double quotes and embedded delimiters in cells.
92+
csv = try CSV<Enumerated>(string: "id\t\"the\t name\"\tage\n1\t\"Alice\t In\t Wonderland\"\t18\n2\tBob\t19\n3\tCharlie\t20")
93+
XCTAssertEqual(csv.serialized, "id\t\"the\t name\"\tage\n1\t\"Alice\t In\t Wonderland\"\t18\n2\tBob\t19\n3\tCharlie\t20")
94+
95+
// Tab-separated values with explicit alternate delimiter (tab) and embedded default delimiters (commas) in cells.
96+
csv = try CSV<Enumerated>(string: "id\tthe, name,age\n1\tAlice, In, Wonderland\t18\n2\tBob\t19\n3\tCharlie\t20", delimiter: .tab)
97+
XCTAssertEqual(csv.serialized, "id\tthe, name,age\n1\tAlice, In, Wonderland\t18\n2\tBob\t19\n3\tCharlie\t20")
8698
}
8799
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /