csv 1.0.2 → 3.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +868 -0
  3. data/README.md +6 -3
  4. data/doc/csv/arguments/io.rdoc +5 -0
  5. data/doc/csv/options/common/col_sep.rdoc +57 -0
  6. data/doc/csv/options/common/quote_char.rdoc +42 -0
  7. data/doc/csv/options/common/row_sep.rdoc +91 -0
  8. data/doc/csv/options/generating/force_quotes.rdoc +17 -0
  9. data/doc/csv/options/generating/quote_empty.rdoc +12 -0
  10. data/doc/csv/options/generating/write_converters.rdoc +25 -0
  11. data/doc/csv/options/generating/write_empty_value.rdoc +15 -0
  12. data/doc/csv/options/generating/write_headers.rdoc +29 -0
  13. data/doc/csv/options/generating/write_nil_value.rdoc +14 -0
  14. data/doc/csv/options/parsing/converters.rdoc +46 -0
  15. data/doc/csv/options/parsing/empty_value.rdoc +13 -0
  16. data/doc/csv/options/parsing/field_size_limit.rdoc +39 -0
  17. data/doc/csv/options/parsing/header_converters.rdoc +43 -0
  18. data/doc/csv/options/parsing/headers.rdoc +63 -0
  19. data/doc/csv/options/parsing/liberal_parsing.rdoc +38 -0
  20. data/doc/csv/options/parsing/nil_value.rdoc +12 -0
  21. data/doc/csv/options/parsing/return_headers.rdoc +22 -0
  22. data/doc/csv/options/parsing/skip_blanks.rdoc +31 -0
  23. data/doc/csv/options/parsing/skip_lines.rdoc +37 -0
  24. data/doc/csv/options/parsing/strip.rdoc +15 -0
  25. data/doc/csv/options/parsing/unconverted_fields.rdoc +27 -0
  26. data/doc/csv/recipes/filtering.rdoc +158 -0
  27. data/doc/csv/recipes/generating.rdoc +298 -0
  28. data/doc/csv/recipes/parsing.rdoc +545 -0
  29. data/doc/csv/recipes/recipes.rdoc +6 -0
  30. data/lib/csv/core_ext/array.rb +1 -1
  31. data/lib/csv/core_ext/string.rb +1 -1
  32. data/lib/csv/fields_converter.rb +89 -0
  33. data/lib/csv/input_record_separator.rb +18 -0
  34. data/lib/csv/parser.rb +1290 -0
  35. data/lib/csv/row.rb +505 -136
  36. data/lib/csv/table.rb +791 -114
  37. data/lib/csv/version.rb +1 -1
  38. data/lib/csv/writer.rb +210 -0
  39. data/lib/csv.rb +2432 -1329
  40. metadata +66 -13
  41. data/news.md +0 -112
@@ -0,0 +1,37 @@
1
+ ====== Option +skip_lines+
2
+
3
+ Specifies an object to use in identifying comment lines in the input that are to be ignored:
4
+ * If a \Regexp, ignores lines that match it.
5
+ * If a \String, converts it to a \Regexp, ignores lines that match it.
6
+ * If +nil+, no lines are considered to be comments.
7
+
8
+ Default value:
9
+ CSV::DEFAULT_OPTIONS.fetch(:skip_lines) # => nil
10
+
11
+ For examples in this section:
12
+ str = <<-EOT
13
+ # Comment
14
+ foo,0
15
+ bar,1
16
+ baz,2
17
+ # Another comment
18
+ EOT
19
+ str # => "# Comment\nfoo,0\nbar,1\nbaz,2\n# Another comment\n"
20
+
21
+ Using the default, +nil+:
22
+ ary = CSV.parse(str)
23
+ ary # => [["# Comment"], ["foo", "0"], ["bar", "1"], ["baz", "2"], ["# Another comment"]]
24
+
25
+ Using a \Regexp:
26
+ ary = CSV.parse(str, skip_lines: /^#/)
27
+ ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
28
+
29
+ Using a \String:
30
+ ary = CSV.parse(str, skip_lines: '#')
31
+ ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
32
+
33
+ ---
34
+
35
+ Raises an exception if given an object that is not a \Regexp, a \String, or +nil+:
36
+ # Raises ArgumentError (:skip_lines has to respond to #match: 0)
37
+ CSV.parse(str, skip_lines: 0)
@@ -0,0 +1,15 @@
1
+ ====== Option +strip+
2
+
3
+ Specifies the boolean value that determines whether
4
+ whitespace is stripped from each input field.
5
+
6
+ Default value:
7
+ CSV::DEFAULT_OPTIONS.fetch(:strip) # => false
8
+
9
+ With default value +false+:
10
+ ary = CSV.parse_line(' a , b ')
11
+ ary # => [" a ", " b "]
12
+
13
+ With value +true+:
14
+ ary = CSV.parse_line(' a , b ', strip: true)
15
+ ary # => ["a", "b"]
@@ -0,0 +1,27 @@
1
+ ====== Option +unconverted_fields+
2
+
3
+ Specifies the boolean that determines whether unconverted field values are to be available.
4
+
5
+ Default value:
6
+ CSV::DEFAULT_OPTIONS.fetch(:unconverted_fields) # => nil
7
+
8
+ The unconverted field values are those found in the source data,
9
+ prior to any conversions performed via option +converters+.
10
+
11
+ When option +unconverted_fields+ is +true+,
12
+ each returned row (\Array or \CSV::Row) has an added method,
13
+ +unconverted_fields+, that returns the unconverted field values:
14
+ str = <<-EOT
15
+ foo,0
16
+ bar,1
17
+ baz,2
18
+ EOT
19
+ # Without unconverted_fields
20
+ csv = CSV.parse(str, converters: :integer)
21
+ csv # => [["foo", 0], ["bar", 1], ["baz", 2]]
22
+ csv.first.respond_to?(:unconverted_fields) # => false
23
+ # With unconverted_fields
24
+ csv = CSV.parse(str, converters: :integer, unconverted_fields: true)
25
+ csv # => [["foo", 0], ["bar", 1], ["baz", 2]]
26
+ csv.first.respond_to?(:unconverted_fields) # => true
27
+ csv.first.unconverted_fields # => ["foo", "0"]
@@ -0,0 +1,158 @@
1
+ == Recipes for Filtering \CSV
2
+
3
+ These recipes are specific code examples for specific \CSV filtering tasks.
4
+
5
+ For other recipes, see {Recipes for CSV}[./recipes_rdoc.html].
6
+
7
+ All code snippets on this page assume that the following has been executed:
8
+ require 'csv'
9
+
10
+ === Contents
11
+
12
+ - {Source and Output Formats}[#label-Source+and+Output+Formats]
13
+ - {Filtering String to String}[#label-Filtering+String+to+String]
14
+ - {Recipe: Filter String to String with Headers}[#label-Recipe-3A+Filter+String+to+String+with+Headers]
15
+ - {Recipe: Filter String to String Without Headers}[#label-Recipe-3A+Filter+String+to+String+Without+Headers]
16
+ - {Filtering String to IO Stream}[#label-Filtering+String+to+IO+Stream]
17
+ - {Recipe: Filter String to IO Stream with Headers}[#label-Recipe-3A+Filter+String+to+IO+Stream+with+Headers]
18
+ - {Recipe: Filter String to IO Stream Without Headers}[#label-Recipe-3A+Filter+String+to+IO+Stream+Without+Headers]
19
+ - {Filtering IO Stream to String}[#label-Filtering+IO+Stream+to+String]
20
+ - {Recipe: Filter IO Stream to String with Headers}[#label-Recipe-3A+Filter+IO+Stream+to+String+with+Headers]
21
+ - {Recipe: Filter IO Stream to String Without Headers}[#label-Recipe-3A+Filter+IO+Stream+to+String+Without+Headers]
22
+ - {Filtering IO Stream to IO Stream}[#label-Filtering+IO+Stream+to+IO+Stream]
23
+ - {Recipe: Filter IO Stream to IO Stream with Headers}[#label-Recipe-3A+Filter+IO+Stream+to+IO+Stream+with+Headers]
24
+ - {Recipe: Filter IO Stream to IO Stream Without Headers}[#label-Recipe-3A+Filter+IO+Stream+to+IO+Stream+Without+Headers]
25
+
26
+ === Source and Output Formats
27
+
28
+ You can use a Unix-style "filter" for \CSV data.
29
+ The filter reads source \CSV data and writes output \CSV data as modified by the filter.
30
+ The input and output \CSV data may be any mixture of \Strings and \IO streams.
31
+
32
+ ==== Filtering \String to \String
33
+
34
+ You can filter one \String to another, with or without headers.
35
+
36
+ ===== Recipe: Filter \String to \String with Headers
37
+
38
+ Use class method CSV.filter with option +headers+ to filter a \String to another \String:
39
+ in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
40
+ out_string = ''
41
+ CSV.filter(in_string, out_string, headers: true) do |row|
42
+ row[0] = row[0].upcase
43
+ row[1] *= 4
44
+ end
45
+ out_string # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n"
46
+
47
+ ===== Recipe: Filter \String to \String Without Headers
48
+
49
+ Use class method CSV.filter without option +headers+ to filter a \String to another \String:
50
+ in_string = "foo,0\nbar,1\nbaz,2\n"
51
+ out_string = ''
52
+ CSV.filter(in_string, out_string) do |row|
53
+ row[0] = row[0].upcase
54
+ row[1] *= 4
55
+ end
56
+ out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
57
+
58
+ ==== Filtering \String to \IO Stream
59
+
60
+ You can filter a \String to an \IO stream, with or without headers.
61
+
62
+ ===== Recipe: Filter \String to \IO Stream with Headers
63
+
64
+ Use class method CSV.filter with option +headers+ to filter a \String to an \IO stream:
65
+ in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
66
+ path = 't.csv'
67
+ File.open(path, 'w') do |out_io|
68
+ CSV.filter(in_string, out_io, headers: true) do |row|
69
+ row[0] = row[0].upcase
70
+ row[1] *= 4
71
+ end
72
+ end
73
+ p File.read(path) # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n"
74
+
75
+ ===== Recipe: Filter \String to \IO Stream Without Headers
76
+
77
+ Use class method CSV.filter without option +headers+ to filter a \String to an \IO stream:
78
+ in_string = "foo,0\nbar,1\nbaz,2\n"
79
+ path = 't.csv'
80
+ File.open(path, 'w') do |out_io|
81
+ CSV.filter(in_string, out_io) do |row|
82
+ row[0] = row[0].upcase
83
+ row[1] *= 4
84
+ end
85
+ end
86
+ p File.read(path) # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
87
+
88
+ ==== Filtering \IO Stream to \String
89
+
90
+ You can filter an \IO stream to a \String, with or without headers.
91
+
92
+ ===== Recipe: Filter \IO Stream to \String with Headers
93
+
94
+ Use class method CSV.filter with option +headers+ to filter an \IO stream to a \String:
95
+ in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
96
+ path = 't.csv'
97
+ File.write(path, in_string)
98
+ out_string = ''
99
+ File.open(path, headers: true) do |in_io|
100
+ CSV.filter(in_io, out_string, headers: true) do |row|
101
+ row[0] = row[0].upcase
102
+ row[1] *= 4
103
+ end
104
+ end
105
+ out_string # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n"
106
+
107
+ ===== Recipe: Filter \IO Stream to \String Without Headers
108
+
109
+ Use class method CSV.filter without option +headers+ to filter an \IO stream to a \String:
110
+ in_string = "foo,0\nbar,1\nbaz,2\n"
111
+ path = 't.csv'
112
+ File.write(path, in_string)
113
+ out_string = ''
114
+ File.open(path) do |in_io|
115
+ CSV.filter(in_io, out_string) do |row|
116
+ row[0] = row[0].upcase
117
+ row[1] *= 4
118
+ end
119
+ end
120
+ out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
121
+
122
+ ==== Filtering \IO Stream to \IO Stream
123
+
124
+ You can filter an \IO stream to another \IO stream, with or without headers.
125
+
126
+ ===== Recipe: Filter \IO Stream to \IO Stream with Headers
127
+
128
+ Use class method CSV.filter with option +headers+ to filter an \IO stream to another \IO stream:
129
+ in_path = 't.csv'
130
+ in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
131
+ File.write(in_path, in_string)
132
+ out_path = 'u.csv'
133
+ File.open(in_path) do |in_io|
134
+ File.open(out_path, 'w') do |out_io|
135
+ CSV.filter(in_io, out_io, headers: true) do |row|
136
+ row[0] = row[0].upcase
137
+ row[1] *= 4
138
+ end
139
+ end
140
+ end
141
+ p File.read(out_path) # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n"
142
+
143
+ ===== Recipe: Filter \IO Stream to \IO Stream Without Headers
144
+
145
+ Use class method CSV.filter without option +headers+ to filter an \IO stream to another \IO stream:
146
+ in_path = 't.csv'
147
+ in_string = "foo,0\nbar,1\nbaz,2\n"
148
+ File.write(in_path, in_string)
149
+ out_path = 'u.csv'
150
+ File.open(in_path) do |in_io|
151
+ File.open(out_path, 'w') do |out_io|
152
+ CSV.filter(in_io, out_io) do |row|
153
+ row[0] = row[0].upcase
154
+ row[1] *= 4
155
+ end
156
+ end
157
+ end
158
+ p File.read(out_path) # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
@@ -0,0 +1,298 @@
1
+ == Recipes for Generating \CSV
2
+
3
+ These recipes are specific code examples for specific \CSV generating tasks.
4
+
5
+ For other recipes, see {Recipes for CSV}[./recipes_rdoc.html].
6
+
7
+ All code snippets on this page assume that the following has been executed:
8
+ require 'csv'
9
+
10
+ === Contents
11
+
12
+ - {Output Formats}[#label-Output+Formats]
13
+ - {Generating to a String}[#label-Generating+to+a+String]
14
+ - {Recipe: Generate to String with Headers}[#label-Recipe-3A+Generate+to+String+with+Headers]
15
+ - {Recipe: Generate to String Without Headers}[#label-Recipe-3A+Generate+to+String+Without+Headers]
16
+ - {Generating to a File}[#label-Generating+to+a+File]
17
+ - {Recipe: Generate to File with Headers}[#label-Recipe-3A+Generate+to+File+with+Headers]
18
+ - {Recipe: Generate to File Without Headers}[#label-Recipe-3A+Generate+to+File+Without+Headers]
19
+ - {Generating to IO an Stream}[#label-Generating+to+an+IO+Stream]
20
+ - {Recipe: Generate to IO Stream with Headers}[#label-Recipe-3A+Generate+to+IO+Stream+with+Headers]
21
+ - {Recipe: Generate to IO Stream Without Headers}[#label-Recipe-3A+Generate+to+IO+Stream+Without+Headers]
22
+ - {Converting Fields}[#label-Converting+Fields]
23
+ - {Recipe: Filter Generated Field Strings}[#label-Recipe-3A+Filter+Generated+Field+Strings]
24
+ - {Recipe: Specify Multiple Write Converters}[#label-Recipe-3A+Specify+Multiple+Write+Converters]
25
+ - {RFC 4180 Compliance}[#label-RFC+4180+Compliance]
26
+ - {Row Separator}[#label-Row+Separator]
27
+ - {Recipe: Generate Compliant Row Separator}[#label-Recipe-3A+Generate+Compliant+Row+Separator]
28
+ - {Recipe: Generate Non-Compliant Row Separator}[#label-Recipe-3A+Generate+Non-Compliant+Row+Separator]
29
+ - {Column Separator}[#label-Column+Separator]
30
+ - {Recipe: Generate Compliant Column Separator}[#label-Recipe-3A+Generate+Compliant+Column+Separator]
31
+ - {Recipe: Generate Non-Compliant Column Separator}[#label-Recipe-3A+Generate+Non-Compliant+Column+Separator]
32
+ - {Quotes}[#label-Quotes]
33
+ - {Recipe: Quote All Fields}[#label-Recipe-3A+Quote+All+Fields]
34
+ - {Recipe: Quote Empty Fields}[#label-Recipe-3A+Quote+Empty+Fields]
35
+ - {Recipe: Generate Compliant Quote Character}[#label-Recipe-3A+Generate+Compliant+Quote+Character]
36
+ - {Recipe: Generate Non-Compliant Quote Character}[#label-Recipe-3A+Generate+Non-Compliant+Quote+Character]
37
+
38
+ === Output Formats
39
+
40
+ You can generate \CSV output to a \String, to a \File (via its path), or to an \IO stream.
41
+
42
+ ==== Generating to a \String
43
+
44
+ You can generate \CSV output to a \String, with or without headers.
45
+
46
+ ===== Recipe: Generate to \String with Headers
47
+
48
+ Use class method CSV.generate with option +headers+ to generate to a \String.
49
+
50
+ This example uses method CSV#<< to append the rows
51
+ that are to be generated:
52
+ output_string = CSV.generate('', headers: ['Name', 'Value'], write_headers: true) do |csv|
53
+ csv << ['Foo', 0]
54
+ csv << ['Bar', 1]
55
+ csv << ['Baz', 2]
56
+ end
57
+ output_string # => "Name,Value\nFoo,0\nBar,1\nBaz,2\n"
58
+
59
+ ===== Recipe: Generate to \String Without Headers
60
+
61
+ Use class method CSV.generate without option +headers+ to generate to a \String.
62
+
63
+ This example uses method CSV#<< to append the rows
64
+ that are to be generated:
65
+ output_string = CSV.generate do |csv|
66
+ csv << ['Foo', 0]
67
+ csv << ['Bar', 1]
68
+ csv << ['Baz', 2]
69
+ end
70
+ output_string # => "Foo,0\nBar,1\nBaz,2\n"
71
+
72
+ ==== Generating to a \File
73
+
74
+ You can generate /CSV data to a \File, with or without headers.
75
+
76
+ ===== Recipe: Generate to \File with Headers
77
+
78
+ Use class method CSV.open with option +headers+ generate to a \File.
79
+
80
+ This example uses method CSV#<< to append the rows
81
+ that are to be generated:
82
+ path = 't.csv'
83
+ CSV.open(path, 'w', headers: ['Name', 'Value'], write_headers: true) do |csv|
84
+ csv << ['Foo', 0]
85
+ csv << ['Bar', 1]
86
+ csv << ['Baz', 2]
87
+ end
88
+ p File.read(path) # => "Name,Value\nFoo,0\nBar,1\nBaz,2\n"
89
+
90
+ ===== Recipe: Generate to \File Without Headers
91
+
92
+ Use class method CSV.open without option +headers+ to generate to a \File.
93
+
94
+ This example uses method CSV#<< to append the rows
95
+ that are to be generated:
96
+ path = 't.csv'
97
+ CSV.open(path, 'w') do |csv|
98
+ csv << ['Foo', 0]
99
+ csv << ['Bar', 1]
100
+ csv << ['Baz', 2]
101
+ end
102
+ p File.read(path) # => "Foo,0\nBar,1\nBaz,2\n"
103
+
104
+ ==== Generating to an \IO Stream
105
+
106
+ You can generate \CSV data to an \IO stream, with or without headers.
107
+
108
+ ==== Recipe: Generate to \IO Stream with Headers
109
+
110
+ Use class method CSV.new with option +headers+ to generate \CSV data to an \IO stream:
111
+ path = 't.csv'
112
+ File.open(path, 'w') do |file|
113
+ csv = CSV.new(file, headers: ['Name', 'Value'], write_headers: true)
114
+ csv << ['Foo', 0]
115
+ csv << ['Bar', 1]
116
+ csv << ['Baz', 2]
117
+ end
118
+ p File.read(path) # => "Name,Value\nFoo,0\nBar,1\nBaz,2\n"
119
+
120
+ ===== Recipe: Generate to \IO Stream Without Headers
121
+
122
+ Use class method CSV.new without option +headers+ to generate \CSV data to an \IO stream:
123
+ path = 't.csv'
124
+ File.open(path, 'w') do |file|
125
+ csv = CSV.new(file)
126
+ csv << ['Foo', 0]
127
+ csv << ['Bar', 1]
128
+ csv << ['Baz', 2]
129
+ end
130
+ p File.read(path) # => "Foo,0\nBar,1\nBaz,2\n"
131
+
132
+ === Converting Fields
133
+
134
+ You can use _write_ _converters_ to convert fields when generating \CSV.
135
+
136
+ ==== Recipe: Filter Generated Field Strings
137
+
138
+ Use option <tt>:write_converters</tt> and a custom converter to convert field values when generating \CSV.
139
+
140
+ This example defines and uses a custom write converter to strip whitespace from generated fields:
141
+ strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field }
142
+ output_string = CSV.generate(write_converters: strip_converter) do |csv|
143
+ csv << [' foo ', 0]
144
+ csv << [' bar ', 1]
145
+ csv << [' baz ', 2]
146
+ end
147
+ output_string # => "foo,0\nbar,1\nbaz,2\n"
148
+
149
+ ==== Recipe: Specify Multiple Write Converters
150
+
151
+ Use option <tt>:write_converters</tt> and multiple custom converters
152
+ to convert field values when generating \CSV.
153
+
154
+ This example defines and uses two custom write converters to strip and upcase generated fields:
155
+ strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field }
156
+ upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field }
157
+ converters = [strip_converter, upcase_converter]
158
+ output_string = CSV.generate(write_converters: converters) do |csv|
159
+ csv << [' foo ', 0]
160
+ csv << [' bar ', 1]
161
+ csv << [' baz ', 2]
162
+ end
163
+ output_string # => "FOO,0\nBAR,1\nBAZ,2\n"
164
+
165
+ === RFC 4180 Compliance
166
+
167
+ By default, \CSV generates data that is compliant with
168
+ {RFC 4180}[https://tools.ietf.org/html/rfc4180]
169
+ with respect to:
170
+ - Column separator.
171
+ - Quote character.
172
+
173
+ ==== Row Separator
174
+
175
+ RFC 4180 specifies the row separator CRLF (Ruby <tt>"\r\n"</tt>).
176
+
177
+ ===== Recipe: Generate Compliant Row Separator
178
+
179
+ For strict compliance, use option +:row_sep+ to specify row separator <tt>"\r\n"</tt>:
180
+ output_string = CSV.generate('', row_sep: "\r\n") do |csv|
181
+ csv << ['Foo', 0]
182
+ csv << ['Bar', 1]
183
+ csv << ['Baz', 2]
184
+ end
185
+ output_string # => "Foo,0\r\nBar,1\r\nBaz,2\r\n"
186
+
187
+ ===== Recipe: Generate Non-Compliant Row Separator
188
+
189
+ For data with non-compliant row separators, use option +:row_sep+ with a different value:
190
+ This example source uses semicolon (<tt>";'</tt>) as its row separator:
191
+ output_string = CSV.generate('', row_sep: ";") do |csv|
192
+ csv << ['Foo', 0]
193
+ csv << ['Bar', 1]
194
+ csv << ['Baz', 2]
195
+ end
196
+ output_string # => "Foo,0;Bar,1;Baz,2;"
197
+
198
+ ==== Column Separator
199
+
200
+ RFC 4180 specifies column separator COMMA (Ruby <tt>","</tt>).
201
+
202
+ ===== Recipe: Generate Compliant Column Separator
203
+
204
+ Because the \CSV default comma separator is <tt>","</tt>,
205
+ you need not specify option +:col_sep+ for compliant data:
206
+ output_string = CSV.generate('') do |csv|
207
+ csv << ['Foo', 0]
208
+ csv << ['Bar', 1]
209
+ csv << ['Baz', 2]
210
+ end
211
+ output_string # => "Foo,0\nBar,1\nBaz,2\n"
212
+
213
+ ===== Recipe: Generate Non-Compliant Column Separator
214
+
215
+ For data with non-compliant column separators, use option +:col_sep+.
216
+ This example source uses TAB (<tt>"\t"</tt>) as its column separator:
217
+ output_string = CSV.generate('', col_sep: "\t") do |csv|
218
+ csv << ['Foo', 0]
219
+ csv << ['Bar', 1]
220
+ csv << ['Baz', 2]
221
+ end
222
+ output_string # => "Foo\t0\nBar\t1\nBaz\t2\n"
223
+
224
+ ==== Quotes
225
+
226
+ IFC 4180 allows most fields to be quoted or not.
227
+ By default, \CSV does not quote most fields.
228
+
229
+ However, a field containing the current row separator, column separator,
230
+ or quote character is automatically quoted, producing IFC 4180 compliance:
231
+ # Field contains row separator.
232
+ output_string = CSV.generate('') do |csv|
233
+ row_sep = csv.row_sep
234
+ csv << ["Foo#{row_sep}Foo", 0]
235
+ csv << ['Bar', 1]
236
+ csv << ['Baz', 2]
237
+ end
238
+ output_string # => "\"Foo\nFoo\",0\nBar,1\nBaz,2\n"
239
+ # Field contains column separator.
240
+ output_string = CSV.generate('') do |csv|
241
+ col_sep = csv.col_sep
242
+ csv << ["Foo#{col_sep}Foo", 0]
243
+ csv << ['Bar', 1]
244
+ csv << ['Baz', 2]
245
+ end
246
+ output_string # => "\"Foo,Foo\",0\nBar,1\nBaz,2\n"
247
+ # Field contains quote character.
248
+ output_string = CSV.generate('') do |csv|
249
+ quote_char = csv.quote_char
250
+ csv << ["Foo#{quote_char}Foo", 0]
251
+ csv << ['Bar', 1]
252
+ csv << ['Baz', 2]
253
+ end
254
+ output_string # => "\"Foo\"\"Foo\",0\nBar,1\nBaz,2\n"
255
+
256
+ ===== Recipe: Quote All Fields
257
+
258
+ Use option +:force_quotes+ to force quoted fields:
259
+ output_string = CSV.generate('', force_quotes: true) do |csv|
260
+ csv << ['Foo', 0]
261
+ csv << ['Bar', 1]
262
+ csv << ['Baz', 2]
263
+ end
264
+ output_string # => "\"Foo\",\"0\"\n\"Bar\",\"1\"\n\"Baz\",\"2\"\n"
265
+
266
+ ===== Recipe: Quote Empty Fields
267
+
268
+ Use option +:quote_empty+ to force quoting for empty fields:
269
+ output_string = CSV.generate('', quote_empty: true) do |csv|
270
+ csv << ['Foo', 0]
271
+ csv << ['Bar', 1]
272
+ csv << ['', 2]
273
+ end
274
+ output_string # => "Foo,0\nBar,1\n\"\",2\n"
275
+
276
+ ===== Recipe: Generate Compliant Quote Character
277
+
278
+ RFC 4180 specifies quote character DQUOTE (Ruby <tt>"\""</tt>).
279
+
280
+ Because the \CSV default quote character is also <tt>"\""</tt>,
281
+ you need not specify option +:quote_char+ for compliant data:
282
+ output_string = CSV.generate('', force_quotes: true) do |csv|
283
+ csv << ['Foo', 0]
284
+ csv << ['Bar', 1]
285
+ csv << ['Baz', 2]
286
+ end
287
+ output_string # => "\"Foo\",\"0\"\n\"Bar\",\"1\"\n\"Baz\",\"2\"\n"
288
+
289
+ ===== Recipe: Generate Non-Compliant Quote Character
290
+
291
+ For data with non-compliant quote characters, use option +:quote_char+.
292
+ This example source uses SQUOTE (<tt>"'"</tt>) as its quote character:
293
+ output_string = CSV.generate('', quote_char: "'", force_quotes: true) do |csv|
294
+ csv << ['Foo', 0]
295
+ csv << ['Bar', 1]
296
+ csv << ['Baz', 2]
297
+ end
298
+ output_string # => "'Foo','0'\n'Bar','1'\n'Baz','2'\n"