csv 1.0.2 → 3.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/NEWS.md +868 -0
- data/README.md +6 -3
- data/doc/csv/arguments/io.rdoc +5 -0
- data/doc/csv/options/common/col_sep.rdoc +57 -0
- data/doc/csv/options/common/quote_char.rdoc +42 -0
- data/doc/csv/options/common/row_sep.rdoc +91 -0
- data/doc/csv/options/generating/force_quotes.rdoc +17 -0
- data/doc/csv/options/generating/quote_empty.rdoc +12 -0
- data/doc/csv/options/generating/write_converters.rdoc +25 -0
- data/doc/csv/options/generating/write_empty_value.rdoc +15 -0
- data/doc/csv/options/generating/write_headers.rdoc +29 -0
- data/doc/csv/options/generating/write_nil_value.rdoc +14 -0
- data/doc/csv/options/parsing/converters.rdoc +46 -0
- data/doc/csv/options/parsing/empty_value.rdoc +13 -0
- data/doc/csv/options/parsing/field_size_limit.rdoc +39 -0
- data/doc/csv/options/parsing/header_converters.rdoc +43 -0
- data/doc/csv/options/parsing/headers.rdoc +63 -0
- data/doc/csv/options/parsing/liberal_parsing.rdoc +38 -0
- data/doc/csv/options/parsing/nil_value.rdoc +12 -0
- data/doc/csv/options/parsing/return_headers.rdoc +22 -0
- data/doc/csv/options/parsing/skip_blanks.rdoc +31 -0
- data/doc/csv/options/parsing/skip_lines.rdoc +37 -0
- data/doc/csv/options/parsing/strip.rdoc +15 -0
- data/doc/csv/options/parsing/unconverted_fields.rdoc +27 -0
- data/doc/csv/recipes/filtering.rdoc +158 -0
- data/doc/csv/recipes/generating.rdoc +298 -0
- data/doc/csv/recipes/parsing.rdoc +545 -0
- data/doc/csv/recipes/recipes.rdoc +6 -0
- data/lib/csv/core_ext/array.rb +1 -1
- data/lib/csv/core_ext/string.rb +1 -1
- data/lib/csv/fields_converter.rb +89 -0
- data/lib/csv/input_record_separator.rb +18 -0
- data/lib/csv/parser.rb +1290 -0
- data/lib/csv/row.rb +505 -136
- data/lib/csv/table.rb +791 -114
- data/lib/csv/version.rb +1 -1
- data/lib/csv/writer.rb +210 -0
- data/lib/csv.rb +2432 -1329
- metadata +66 -13
- 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"
|