csv 3.1.5 → 3.1.6
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 +36 -0
- data/doc/arguments/io.rdoc +5 -0
- data/doc/options/common/col_sep.rdoc +63 -0
- data/doc/options/common/quote_char.rdoc +42 -0
- data/doc/{row_sep.rdoc → options/common/row_sep.rdoc} +15 -6
- data/doc/{force_quotes.rdoc → options/generating/force_quotes.rdoc} +0 -0
- data/doc/{quote_empty.rdoc → options/generating/quote_empty.rdoc} +0 -0
- data/doc/{write_converters.rdoc → options/generating/write_converters.rdoc} +8 -6
- data/doc/{write_empty_value.rdoc → options/generating/write_empty_value.rdoc} +0 -0
- data/doc/{write_headers.rdoc → options/generating/write_headers.rdoc} +0 -0
- data/doc/{write_nil_value.rdoc → options/generating/write_nil_value.rdoc} +1 -1
- data/doc/options/parsing/converters.rdoc +46 -0
- data/doc/{empty_value.rdoc → options/parsing/empty_value.rdoc} +0 -0
- data/doc/{field_size_limit.rdoc → options/parsing/field_size_limit.rdoc} +0 -0
- data/doc/options/parsing/header_converters.rdoc +43 -0
- data/doc/{headers.rdoc → options/parsing/headers.rdoc} +0 -0
- data/doc/{liberal_parsing.rdoc → options/parsing/liberal_parsing.rdoc} +0 -0
- data/doc/{nil_value.rdoc → options/parsing/nil_value.rdoc} +0 -0
- data/doc/{return_headers.rdoc → options/parsing/return_headers.rdoc} +0 -0
- data/doc/{skip_blanks.rdoc → options/parsing/skip_blanks.rdoc} +0 -0
- data/doc/{skip_lines.rdoc → options/parsing/skip_lines.rdoc} +0 -0
- data/doc/{strip.rdoc → options/parsing/strip.rdoc} +0 -0
- data/doc/{unconverted_fields.rdoc → options/parsing/unconverted_fields.rdoc} +0 -0
- data/lib/csv.rb +1334 -495
- data/lib/csv/version.rb +1 -1
- data/lib/csv/writer.rb +45 -4
- metadata +38 -23
- data/doc/col_sep.rdoc +0 -45
- data/doc/converters.rdoc +0 -45
- data/doc/header_converters.rdoc +0 -31
- data/doc/quote_char.rdoc +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 684d334d79d666022640de8b3bfa8d2e6fa879cb0c9e59349e9adf8307147fa0
|
4
|
+
data.tar.gz: 6298def337c705d19acb6d1da52560aa53f0dfeba5660202d8fc0259418d96a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42349aa586b53f75c75ea29a5388889c21645b24e1ad8ac8e1752697f94dc9e7564b62f36b2b6219516f34c1a27bbf581566f3f2dafef556f9783b907e932d04
|
7
|
+
data.tar.gz: 69319007e9a35acb7659d73b5c9a38256a572a5a21ef59365b8f4fd0fee9d06c2c0c89ae346bf701e6c0af430fc99868c625323cc75f620340eb80006ce0d036
|
data/NEWS.md
CHANGED
@@ -1,5 +1,41 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 3.1.6 - 2020-07-20
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* Improved document.
|
8
|
+
[GitHub#127][GitHub#135][GitHub#136][GitHub#137][GitHub#139][GitHub#140]
|
9
|
+
[GitHub#141][GitHub#142][GitHub#143][GitHub#145][GitHub#146][GitHub#148]
|
10
|
+
[GitHub#148][GitHub#151][GitHub#152][GitHub#154][GitHub#155][GitHub#157]
|
11
|
+
[Patch by Burdette Lamar]
|
12
|
+
|
13
|
+
* `CSV.open`: Added support for `undef: :replace`.
|
14
|
+
[GitHub#129][Patch by Koichi ITO]
|
15
|
+
|
16
|
+
* `CSV.open`: Added support for `invalid: :replace`.
|
17
|
+
[GitHub#129][Patch by Koichi ITO]
|
18
|
+
|
19
|
+
* Don't run quotable check for invalid encoding field values.
|
20
|
+
[GitHub#131][Patch by Koichi ITO]
|
21
|
+
|
22
|
+
* Added support for specifying the target indexes and names to
|
23
|
+
`force_quotes:`.
|
24
|
+
[GitHub#153][Reported by Aleksandr]
|
25
|
+
|
26
|
+
* `CSV.generate`: Changed to use the encoding of the first non-ASCII
|
27
|
+
field rather than the encoding of ASCII only field.
|
28
|
+
|
29
|
+
* Changed to require the stringio gem 0.1.3 or later.
|
30
|
+
|
31
|
+
### Thanks
|
32
|
+
|
33
|
+
* Burdette Lamar
|
34
|
+
|
35
|
+
* Koichi ITO
|
36
|
+
|
37
|
+
* Aleksandr
|
38
|
+
|
3
39
|
## 3.1.5 - 2020-05-18
|
4
40
|
|
5
41
|
### Improvements
|
@@ -0,0 +1,5 @@
|
|
1
|
+
* Argument +io+ should be an IO object that is:
|
2
|
+
* Open for reading; on return, the IO object will be closed.
|
3
|
+
* Positioned at the beginning.
|
4
|
+
To position at the end, for appending, use method CSV.generate.
|
5
|
+
For any other positioning, pass a preset \StringIO object instead.
|
@@ -0,0 +1,63 @@
|
|
1
|
+
====== Option +col_sep+
|
2
|
+
|
3
|
+
Specifies the \String field separator to be used
|
4
|
+
for both parsing and generating.
|
5
|
+
The \String will be transcoded into the data's \Encoding before use.
|
6
|
+
|
7
|
+
Default value:
|
8
|
+
CSV::DEFAULT_OPTIONS.fetch(:col_sep) # => "," (comma)
|
9
|
+
|
10
|
+
Using the default (comma):
|
11
|
+
str = CSV.generate do |csv|
|
12
|
+
csv << [:foo, 0]
|
13
|
+
csv << [:bar, 1]
|
14
|
+
csv << [:baz, 2]
|
15
|
+
end
|
16
|
+
str # => "foo,0\nbar,1\nbaz,2\n"
|
17
|
+
ary = CSV.parse(str)
|
18
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
19
|
+
|
20
|
+
Using +:+ (colon):
|
21
|
+
col_sep = ':'
|
22
|
+
str = CSV.generate(col_sep: col_sep) do |csv|
|
23
|
+
csv << [:foo, 0]
|
24
|
+
csv << [:bar, 1]
|
25
|
+
csv << [:baz, 2]
|
26
|
+
end
|
27
|
+
str # => "foo:0\nbar:1\nbaz:2\n"
|
28
|
+
ary = CSV.parse(str, col_sep: col_sep)
|
29
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
30
|
+
|
31
|
+
Using +::+ (two colons):
|
32
|
+
col_sep = '::'
|
33
|
+
str = CSV.generate(col_sep: col_sep) do |csv|
|
34
|
+
csv << [:foo, 0]
|
35
|
+
csv << [:bar, 1]
|
36
|
+
csv << [:baz, 2]
|
37
|
+
end
|
38
|
+
str # => "foo::0\nbar::1\nbaz::2\n"
|
39
|
+
ary = CSV.parse(str, col_sep: col_sep)
|
40
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
41
|
+
|
42
|
+
Using <tt>''</tt> (empty string):
|
43
|
+
col_sep = ''
|
44
|
+
str = CSV.generate(col_sep: col_sep) do |csv|
|
45
|
+
csv << [:foo, 0]
|
46
|
+
csv << [:bar, 1]
|
47
|
+
csv << [:baz, 2]
|
48
|
+
end
|
49
|
+
str # => "foo0\nbar1\nbaz2\n"
|
50
|
+
|
51
|
+
---
|
52
|
+
|
53
|
+
Raises an exception if parsing with the empty \String:
|
54
|
+
col_sep = ''
|
55
|
+
# Raises ArgumentError (:col_sep must be 1 or more characters: "")
|
56
|
+
CSV.parse("foo0\nbar1\nbaz2\n", col_sep: col_sep)
|
57
|
+
|
58
|
+
Raises an exception if the given value is not String-convertible:
|
59
|
+
col_sep = BasicObject.new
|
60
|
+
# Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
|
61
|
+
CSV.generate(line, col_sep: col_sep)
|
62
|
+
# Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
|
63
|
+
CSV.parse(str, col_sep: col_sep)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
====== Option +quote_char+
|
2
|
+
|
3
|
+
Specifies the character (\String of length 1) used used to quote fields
|
4
|
+
in both parsing and generating.
|
5
|
+
This String will be transcoded into the data's \Encoding before use.
|
6
|
+
|
7
|
+
Default value:
|
8
|
+
CSV::DEFAULT_OPTIONS.fetch(:quote_char) # => "\"" (double quote)
|
9
|
+
|
10
|
+
This is useful for an application that incorrectly uses <tt>'</tt> (single-quote)
|
11
|
+
to quote fields, instead of the correct <tt>"</tt> (double-quote).
|
12
|
+
|
13
|
+
Using the default (double quote):
|
14
|
+
str = CSV.generate do |csv|
|
15
|
+
csv << ['foo', 0]
|
16
|
+
csv << ["'bar'", 1]
|
17
|
+
csv << ['"baz"', 2]
|
18
|
+
end
|
19
|
+
str # => "foo,0\n'bar',1\n\"\"\"baz\"\"\",2\n"
|
20
|
+
ary = CSV.parse(str)
|
21
|
+
ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
|
22
|
+
|
23
|
+
Using <tt>'</tt> (single-quote):
|
24
|
+
quote_char = "'"
|
25
|
+
str = CSV.generate(quote_char: quote_char) do |csv|
|
26
|
+
csv << ['foo', 0]
|
27
|
+
csv << ["'bar'", 1]
|
28
|
+
csv << ['"baz"', 2]
|
29
|
+
end
|
30
|
+
str # => "foo,0\n'''bar''',1\n\"baz\",2\n"
|
31
|
+
ary = CSV.parse(str, quote_char: quote_char)
|
32
|
+
ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
|
33
|
+
|
34
|
+
---
|
35
|
+
|
36
|
+
Raises an exception if the \String length is greater than 1:
|
37
|
+
# Raises ArgumentError (:quote_char has to be nil or a single character String)
|
38
|
+
CSV.new('', quote_char: 'xx')
|
39
|
+
|
40
|
+
Raises an exception if the value is not a \String:
|
41
|
+
# Raises ArgumentError (:quote_char has to be nil or a single character String)
|
42
|
+
CSV.new('', quote_char: :foo)
|
@@ -12,7 +12,8 @@ When +row_sep+ is a \String, that \String becomes the row separator.
|
|
12
12
|
The String will be transcoded into the data's Encoding before use.
|
13
13
|
|
14
14
|
Using <tt>"\n"</tt>:
|
15
|
-
|
15
|
+
row_sep = "\n"
|
16
|
+
str = CSV.generate(row_sep: row_sep) do |csv|
|
16
17
|
csv << [:foo, 0]
|
17
18
|
csv << [:bar, 1]
|
18
19
|
csv << [:baz, 2]
|
@@ -57,20 +58,28 @@ Using '' (empty string):
|
|
57
58
|
---
|
58
59
|
|
59
60
|
When +row_sep+ is the \Symbol +:auto+ (the default),
|
60
|
-
|
61
|
+
generating uses <tt>"\n"</tt> as the row separator:
|
62
|
+
str = CSV.generate do |csv|
|
63
|
+
csv << [:foo, 0]
|
64
|
+
csv << [:bar, 1]
|
65
|
+
csv << [:baz, 2]
|
66
|
+
end
|
67
|
+
str # => "foo,0\nbar,1\nbaz,2\n"
|
68
|
+
|
69
|
+
Parsing, on the other hand, invokes auto-discovery of the row separator.
|
61
70
|
|
62
71
|
Auto-discovery reads ahead in the data looking for the next <tt>\r\n</tt>, +\n+, or +\r+ sequence.
|
63
72
|
The sequence will be selected even if it occurs in a quoted field,
|
64
73
|
assuming that you would have the same line endings there.
|
65
74
|
|
66
|
-
|
67
|
-
str = CSV.generate
|
75
|
+
Example:
|
76
|
+
str = CSV.generate do |csv|
|
68
77
|
csv << [:foo, 0]
|
69
78
|
csv << [:bar, 1]
|
70
79
|
csv << [:baz, 2]
|
71
80
|
end
|
72
81
|
str # => "foo,0\nbar,1\nbaz,2\n"
|
73
|
-
ary = CSV.parse(str
|
82
|
+
ary = CSV.parse(str)
|
74
83
|
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
75
84
|
|
76
85
|
The default <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) is used
|
@@ -86,6 +95,6 @@ Obviously, discovery takes a little time. Set manually if speed is important. Al
|
|
86
95
|
Raises an exception if the given value is not String-convertible:
|
87
96
|
row_sep = BasicObject.new
|
88
97
|
# Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
|
89
|
-
CSV.
|
98
|
+
CSV.generate(ary, row_sep: row_sep)
|
90
99
|
# Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
|
91
100
|
CSV.parse(str, row_sep: row_sep)
|
File without changes
|
File without changes
|
@@ -1,7 +1,7 @@
|
|
1
1
|
====== Option +write_converters+
|
2
2
|
|
3
|
-
Specifies
|
4
|
-
|
3
|
+
Specifies converters to be used in generating fields.
|
4
|
+
See {Write Converters}[#class-CSV-label-Write+Converters]
|
5
5
|
|
6
6
|
Default value:
|
7
7
|
CSV::DEFAULT_OPTIONS.fetch(:write_converters) # => nil
|
@@ -11,21 +11,23 @@ With no write converter:
|
|
11
11
|
str # => "\"\na\n\",\tb\t, c \n"
|
12
12
|
|
13
13
|
With a write converter:
|
14
|
-
strip_converter =
|
14
|
+
strip_converter = proc {|field| field.strip }
|
15
15
|
str = CSV.generate_line(["\na\n", "\tb\t", " c "], write_converters: strip_converter)
|
16
16
|
str # => "a,b,c\n"
|
17
17
|
|
18
18
|
With two write converters (called in order):
|
19
|
-
upcase_converter =
|
20
|
-
downcase_converter =
|
19
|
+
upcase_converter = proc {|field| field.upcase }
|
20
|
+
downcase_converter = proc {|field| field.downcase }
|
21
21
|
write_converters = [upcase_converter, downcase_converter]
|
22
22
|
str = CSV.generate_line(['a', 'b', 'c'], write_converters: write_converters)
|
23
23
|
str # => "a,b,c\n"
|
24
24
|
|
25
|
+
See also {Write Converters}[#class-CSV-label-Write+Converters]
|
26
|
+
|
25
27
|
---
|
26
28
|
|
27
29
|
Raises an exception if the converter returns a value that is neither +nil+
|
28
30
|
nor \String-convertible:
|
29
|
-
bad_converter =
|
31
|
+
bad_converter = proc {|field| BasicObject.new }
|
30
32
|
# Raises NoMethodError (undefined method `is_a?' for #<BasicObject:>)
|
31
33
|
CSV.generate_line(['a', 'b', 'c'], write_converters: bad_converter)
|
File without changes
|
File without changes
|
@@ -0,0 +1,46 @@
|
|
1
|
+
====== Option +converters+
|
2
|
+
|
3
|
+
Specifies converters to be used in parsing fields.
|
4
|
+
See {Field Converters}[#class-CSV-label-Field+Converters]
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:converters) # => nil
|
8
|
+
|
9
|
+
The value may be a field converter name
|
10
|
+
(see {Stored Converters}[#class-CSV-label-Stored+Converters]):
|
11
|
+
str = '1,2,3'
|
12
|
+
# Without a converter
|
13
|
+
array = CSV.parse_line(str)
|
14
|
+
array # => ["1", "2", "3"]
|
15
|
+
# With built-in converter :integer
|
16
|
+
array = CSV.parse_line(str, converters: :integer)
|
17
|
+
array # => [1, 2, 3]
|
18
|
+
|
19
|
+
The value may be a converter list
|
20
|
+
(see {Converter Lists}[#class-CSV-label-Converter+Lists]):
|
21
|
+
str = '1,3.14159'
|
22
|
+
# Without converters
|
23
|
+
array = CSV.parse_line(str)
|
24
|
+
array # => ["1", "3.14159"]
|
25
|
+
# With built-in converters
|
26
|
+
array = CSV.parse_line(str, converters: [:integer, :float])
|
27
|
+
array # => [1, 3.14159]
|
28
|
+
|
29
|
+
The value may be a \Proc custom converter:
|
30
|
+
(see {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters]):
|
31
|
+
str = ' foo , bar , baz '
|
32
|
+
# Without a converter
|
33
|
+
array = CSV.parse_line(str)
|
34
|
+
array # => [" foo ", " bar ", " baz "]
|
35
|
+
# With a custom converter
|
36
|
+
array = CSV.parse_line(str, converters: proc {|field| field.strip })
|
37
|
+
array # => ["foo", "bar", "baz"]
|
38
|
+
|
39
|
+
See also {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters]
|
40
|
+
|
41
|
+
---
|
42
|
+
|
43
|
+
Raises an exception if the converter is not a converter name or a \Proc:
|
44
|
+
str = 'foo,0'
|
45
|
+
# Raises NoMethodError (undefined method `arity' for nil:NilClass)
|
46
|
+
CSV.parse(str, converters: :foo)
|
File without changes
|
File without changes
|
@@ -0,0 +1,43 @@
|
|
1
|
+
====== Option +header_converters+
|
2
|
+
|
3
|
+
Specifies converters to be used in parsing headers.
|
4
|
+
See {Header Converters}[#class-CSV-label-Header+Converters]
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:header_converters) # => nil
|
8
|
+
|
9
|
+
Identical in functionality to option {converters}[#class-CSV-label-Option+converters]
|
10
|
+
except that:
|
11
|
+
- The converters apply only to the header row.
|
12
|
+
- The built-in header converters are +:downcase+ and +:symbol+.
|
13
|
+
|
14
|
+
This section assumes prior execution of:
|
15
|
+
str = <<-EOT
|
16
|
+
Name,Value
|
17
|
+
foo,0
|
18
|
+
bar,1
|
19
|
+
baz,2
|
20
|
+
EOT
|
21
|
+
# With no header converter
|
22
|
+
table = CSV.parse(str, headers: true)
|
23
|
+
table.headers # => ["Name", "Value"]
|
24
|
+
|
25
|
+
The value may be a header converter name
|
26
|
+
(see {Stored Converters}[#class-CSV-label-Stored+Converters]):
|
27
|
+
table = CSV.parse(str, headers: true, header_converters: :downcase)
|
28
|
+
table.headers # => ["name", "value"]
|
29
|
+
|
30
|
+
The value may be a converter list
|
31
|
+
(see {Converter Lists}[#class-CSV-label-Converter+Lists]):
|
32
|
+
header_converters = [:downcase, :symbol]
|
33
|
+
table = CSV.parse(str, headers: true, header_converters: header_converters)
|
34
|
+
table.headers # => [:name, :value]
|
35
|
+
|
36
|
+
The value may be a \Proc custom converter
|
37
|
+
(see {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters]):
|
38
|
+
upcase_converter = proc {|field| field.upcase }
|
39
|
+
table = CSV.parse(str, headers: true, header_converters: upcase_converter)
|
40
|
+
table.headers # => ["NAME", "VALUE"]
|
41
|
+
|
42
|
+
See also {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters]
|
43
|
+
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/csv.rb
CHANGED
@@ -34,7 +34,7 @@
|
|
34
34
|
# I'm sure I'll miss something, but I'll try to mention most of the major
|
35
35
|
# differences I am aware of, to help others quickly get up to speed:
|
36
36
|
#
|
37
|
-
# === CSV Parsing
|
37
|
+
# === \CSV Parsing
|
38
38
|
#
|
39
39
|
# * This parser is m17n aware. See CSV for full details.
|
40
40
|
# * This library has a stricter parser and will throw MalformedCSVErrors on
|
@@ -103,82 +103,209 @@ require_relative "csv/writer"
|
|
103
103
|
|
104
104
|
using CSV::MatchP if CSV.const_defined?(:MatchP)
|
105
105
|
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
106
|
+
# == \CSV
|
107
|
+
# \CSV (comma-separated variables) data is a text representation of a table:
|
108
|
+
# - A _row_ _separator_ delimits table rows.
|
109
|
+
# A common row separator is the newline character <tt>"\n"</tt>.
|
110
|
+
# - A _column_ _separator_ delimits fields in a row.
|
111
|
+
# A common column separator is the comma character <tt>","</tt>.
|
109
112
|
#
|
110
|
-
#
|
113
|
+
# This \CSV \String, with row separator <tt>"\n"</tt>
|
114
|
+
# and column separator <tt>","</tt>,
|
115
|
+
# has three rows and two columns:
|
116
|
+
# "foo,0\nbar,1\nbaz,2\n"
|
111
117
|
#
|
112
|
-
#
|
118
|
+
# Despite the name \CSV, a \CSV representation can use different separators.
|
113
119
|
#
|
114
|
-
#
|
115
|
-
# csv.read # => array of rows
|
116
|
-
# # or
|
117
|
-
# csv.each do |row|
|
118
|
-
# # ...
|
119
|
-
# end
|
120
|
-
# # or
|
121
|
-
# row = csv.shift
|
120
|
+
# == \Class \CSV
|
122
121
|
#
|
123
|
-
#
|
124
|
-
#
|
122
|
+
# Class \CSV provides methods for:
|
123
|
+
# - Parsing \CSV data from a \String object, a \File (via its file path), or an \IO object.
|
124
|
+
# - Generating \CSV data to a \String object.
|
125
125
|
#
|
126
|
-
#
|
127
|
-
#
|
126
|
+
# To make \CSV available:
|
127
|
+
# require 'csv'
|
128
128
|
#
|
129
|
-
#
|
129
|
+
# All examples here assume that this has been done.
|
130
130
|
#
|
131
|
-
#
|
132
|
-
# separators, row separators, value quoting and so on), and for data conversion,
|
133
|
-
# see Data Conversion section for the description of the latter.
|
131
|
+
# == Keeping It Simple
|
134
132
|
#
|
135
|
-
#
|
133
|
+
# A \CSV object has dozens of instance methods that offer fine-grained control
|
134
|
+
# of parsing and generating \CSV data.
|
135
|
+
# For many needs, though, simpler approaches will do.
|
136
136
|
#
|
137
|
-
#
|
137
|
+
# This section summarizes the singleton methods in \CSV
|
138
|
+
# that allow you to parse and generate without explicitly
|
139
|
+
# creating \CSV objects.
|
140
|
+
# For details, follow the links.
|
138
141
|
#
|
139
|
-
#
|
140
|
-
# arr_of_rows = CSV.read("path/to/file.csv", **options)
|
141
|
-
# # iterator-style:
|
142
|
-
# CSV.foreach("path/to/file.csv", **options) do |row|
|
143
|
-
# # ...
|
144
|
-
# end
|
142
|
+
# === Simple Parsing
|
145
143
|
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
144
|
+
# Parsing methods commonly return either of:
|
145
|
+
# - An \Array of Arrays of Strings:
|
146
|
+
# - The outer \Array is the entire "table".
|
147
|
+
# - Each inner \Array is a row.
|
148
|
+
# - Each \String is a field.
|
149
|
+
# - A CSV::Table object. For details, see
|
150
|
+
# {\CSV with Headers}[#class-CSV-label-CSV+with+Headers].
|
152
151
|
#
|
153
|
-
#
|
152
|
+
# ==== Parsing a \String
|
154
153
|
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
154
|
+
# The input to be parsed can be a string:
|
155
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
156
|
+
#
|
157
|
+
# \Method CSV.parse returns the entire \CSV data:
|
158
|
+
# CSV.parse(string) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
159
|
+
#
|
160
|
+
# \Method CSV.parse_line returns only the first row:
|
161
|
+
# CSV.parse_line(string) # => ["foo", "0"]
|
162
|
+
#
|
163
|
+
# \CSV extends class \String with instance method String#parse_csv,
|
164
|
+
# which also returns only the first row:
|
165
|
+
# string.parse_csv # => ["foo", "0"]
|
166
|
+
#
|
167
|
+
# ==== Parsing Via a \File Path
|
168
|
+
#
|
169
|
+
# The input to be parsed can be in a file:
|
170
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
171
|
+
# path = 't.csv'
|
172
|
+
# File.write(path, string)
|
173
|
+
#
|
174
|
+
# \Method CSV.read returns the entire \CSV data:
|
175
|
+
# CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
176
|
+
#
|
177
|
+
# \Method CSV.foreach iterates, passing each row to the given block:
|
178
|
+
# CSV.foreach(path) do |row|
|
179
|
+
# p row
|
180
|
+
# end
|
181
|
+
# Output:
|
182
|
+
# ["foo", "0"]
|
183
|
+
# ["bar", "1"]
|
184
|
+
# ["baz", "2"]
|
185
|
+
#
|
186
|
+
# \Method CSV.table returns the entire \CSV data as a CSV::Table object:
|
187
|
+
# CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:3>
|
188
|
+
#
|
189
|
+
# ==== Parsing from an Open \IO Stream
|
190
|
+
#
|
191
|
+
# The input to be parsed can be in an open \IO stream:
|
192
|
+
#
|
193
|
+
# \Method CSV.read returns the entire \CSV data:
|
194
|
+
# File.open(path) do |file|
|
195
|
+
# CSV.read(file)
|
196
|
+
# end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
197
|
+
#
|
198
|
+
# As does method CSV.parse:
|
199
|
+
# File.open(path) do |file|
|
200
|
+
# CSV.parse(file)
|
201
|
+
# end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
161
202
|
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
203
|
+
# \Method CSV.parse_line returns only the first row:
|
204
|
+
# File.open(path) do |file|
|
205
|
+
# CSV.parse_line(file)
|
206
|
+
# end # => ["foo", "0"]
|
207
|
+
#
|
208
|
+
# \Method CSV.foreach iterates, passing each row to the given block:
|
209
|
+
# File.open(path) do |file|
|
210
|
+
# CSV.foreach(file) do |row|
|
211
|
+
# p row
|
212
|
+
# end
|
213
|
+
# end
|
214
|
+
# Output:
|
215
|
+
# ["foo", "0"]
|
216
|
+
# ["bar", "1"]
|
217
|
+
# ["baz", "2"]
|
218
|
+
#
|
219
|
+
# \Method CSV.table returns the entire \CSV data as a CSV::Table object:
|
220
|
+
# File.open(path) do |file|
|
221
|
+
# CSV.table(file)
|
222
|
+
# end # => #<CSV::Table mode:col_or_row row_count:3>
|
223
|
+
#
|
224
|
+
# === Simple Generating
|
225
|
+
#
|
226
|
+
# \Method CSV.generate returns a \String;
|
227
|
+
# this example uses method CSV#<< to append the rows
|
228
|
+
# that are to be generated:
|
229
|
+
# output_string = CSV.generate do |csv|
|
230
|
+
# csv << ['foo', 0]
|
231
|
+
# csv << ['bar', 1]
|
232
|
+
# csv << ['baz', 2]
|
167
233
|
# end
|
234
|
+
# output_string # => "foo,0\nbar,1\nbaz,2\n"
|
168
235
|
#
|
169
|
-
#
|
236
|
+
# \Method CSV.generate_line returns a \String containing the single row
|
237
|
+
# constructed from an \Array:
|
238
|
+
# CSV.generate_line(['foo', '0']) # => "foo,0\n"
|
170
239
|
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
240
|
+
# \CSV extends class \Array with instance method <tt>Array#to_csv</tt>,
|
241
|
+
# which forms an \Array into a \String:
|
242
|
+
# ['foo', '0'].to_csv # => "foo,0\n"
|
174
243
|
#
|
175
|
-
#
|
176
|
-
# CSV { |csv_out| csv_out << %w{my data here} } # to $stdout
|
177
|
-
# CSV(csv = "") { |csv_str| csv_str << %w{my data here} } # to a String
|
178
|
-
# CSV($stderr) { |csv_err| csv_err << %w{my data here} } # to $stderr
|
179
|
-
# CSV($stdin) { |csv_in| csv_in.each { |row| p row } } # from $stdin
|
244
|
+
# === "Filtering" \CSV
|
180
245
|
#
|
181
|
-
#
|
246
|
+
# \Method CSV.filter provides a Unix-style filter for \CSV data.
|
247
|
+
# The input data is processed to form the output data:
|
248
|
+
# in_string = "foo,0\nbar,1\nbaz,2\n"
|
249
|
+
# out_string = ''
|
250
|
+
# CSV.filter(in_string, out_string) do |row|
|
251
|
+
# row[0] = row[0].upcase
|
252
|
+
# row[1] *= 4
|
253
|
+
# end
|
254
|
+
# out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
|
255
|
+
#
|
256
|
+
# == \CSV Objects
|
257
|
+
#
|
258
|
+
# There are three ways to create a \CSV object:
|
259
|
+
# - \Method CSV.new returns a new \CSV object.
|
260
|
+
# - \Method CSV.instance returns a new or cached \CSV object.
|
261
|
+
# - \Method \CSV() also returns a new or cached \CSV object.
|
262
|
+
#
|
263
|
+
# === Instance Methods
|
264
|
+
#
|
265
|
+
# \CSV has three groups of instance methods:
|
266
|
+
# - Its own internally defined instance methods.
|
267
|
+
# - Methods included by module Enumerable.
|
268
|
+
# - Methods delegated to class IO. See below.
|
269
|
+
#
|
270
|
+
# ==== Delegated Methods
|
271
|
+
#
|
272
|
+
# For convenience, a CSV object will delegate to many methods in class IO.
|
273
|
+
# (A few have wrapper "guard code" in \CSV.) You may call:
|
274
|
+
# * IO#binmode
|
275
|
+
# * #binmode?
|
276
|
+
# * IO#close
|
277
|
+
# * IO#close_read
|
278
|
+
# * IO#close_write
|
279
|
+
# * IO#closed?
|
280
|
+
# * #eof
|
281
|
+
# * #eof?
|
282
|
+
# * IO#external_encoding
|
283
|
+
# * IO#fcntl
|
284
|
+
# * IO#fileno
|
285
|
+
# * #flock
|
286
|
+
# * IO#flush
|
287
|
+
# * IO#fsync
|
288
|
+
# * IO#internal_encoding
|
289
|
+
# * #ioctl
|
290
|
+
# * IO#isatty
|
291
|
+
# * #path
|
292
|
+
# * IO#pid
|
293
|
+
# * IO#pos
|
294
|
+
# * IO#pos=
|
295
|
+
# * IO#reopen
|
296
|
+
# * #rewind
|
297
|
+
# * IO#seek
|
298
|
+
# * #stat
|
299
|
+
# * IO#string
|
300
|
+
# * IO#sync
|
301
|
+
# * IO#sync=
|
302
|
+
# * IO#tell
|
303
|
+
# * #to_i
|
304
|
+
# * #to_io
|
305
|
+
# * IO#truncate
|
306
|
+
# * IO#tty?
|
307
|
+
#
|
308
|
+
# === Options
|
182
309
|
#
|
183
310
|
# The default values for options are:
|
184
311
|
# DEFAULT_OPTIONS = {
|
@@ -208,59 +335,90 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
208
335
|
# strip: false,
|
209
336
|
# }
|
210
337
|
#
|
211
|
-
#
|
338
|
+
# ==== Options for Parsing
|
339
|
+
#
|
340
|
+
# Options for parsing, described in detail below, include:
|
341
|
+
# - +row_sep+: Specifies the row separator; used to delimit rows.
|
342
|
+
# - +col_sep+: Specifies the column separator; used to delimit fields.
|
343
|
+
# - +quote_char+: Specifies the quote character; used to quote fields.
|
344
|
+
# - +field_size_limit+: Specifies the maximum field size allowed.
|
345
|
+
# - +converters+: Specifies the field converters to be used.
|
346
|
+
# - +unconverted_fields+: Specifies whether unconverted fields are to be available.
|
347
|
+
# - +headers+: Specifies whether data contains headers,
|
348
|
+
# or specifies the headers themselves.
|
349
|
+
# - +return_headers+: Specifies whether headers are to be returned.
|
350
|
+
# - +header_converters+: Specifies the header converters to be used.
|
351
|
+
# - +skip_blanks+: Specifies whether blanks lines are to be ignored.
|
352
|
+
# - +skip_lines+: Specifies how comments lines are to be recognized.
|
353
|
+
# - +strip+: Specifies whether leading and trailing whitespace are
|
354
|
+
# to be stripped from fields..
|
355
|
+
# - +liberal_parsing+: Specifies whether \CSV should attempt to parse
|
356
|
+
# non-compliant data.
|
357
|
+
# - +nil_value+: Specifies the object that is to be substituted for each null (no-text) field.
|
358
|
+
# - +empty_value+: Specifies the object that is to be substituted for each empty field.
|
212
359
|
#
|
213
|
-
# :include: ../doc/
|
360
|
+
# :include: ../doc/options/common/row_sep.rdoc
|
214
361
|
#
|
215
|
-
# :include: ../doc/
|
362
|
+
# :include: ../doc/options/common/col_sep.rdoc
|
216
363
|
#
|
217
|
-
# :include: ../doc/quote_char.rdoc
|
364
|
+
# :include: ../doc/options/common/quote_char.rdoc
|
218
365
|
#
|
219
|
-
# :include: ../doc/field_size_limit.rdoc
|
366
|
+
# :include: ../doc/options/parsing/field_size_limit.rdoc
|
220
367
|
#
|
221
|
-
# :include: ../doc/converters.rdoc
|
368
|
+
# :include: ../doc/options/parsing/converters.rdoc
|
222
369
|
#
|
223
|
-
# :include: ../doc/unconverted_fields.rdoc
|
370
|
+
# :include: ../doc/options/parsing/unconverted_fields.rdoc
|
224
371
|
#
|
225
|
-
# :include: ../doc/headers.rdoc
|
372
|
+
# :include: ../doc/options/parsing/headers.rdoc
|
226
373
|
#
|
227
|
-
# :include: ../doc/return_headers.rdoc
|
374
|
+
# :include: ../doc/options/parsing/return_headers.rdoc
|
228
375
|
#
|
229
|
-
# :include: ../doc/header_converters.rdoc
|
376
|
+
# :include: ../doc/options/parsing/header_converters.rdoc
|
230
377
|
#
|
231
|
-
# :include: ../doc/skip_blanks.rdoc
|
378
|
+
# :include: ../doc/options/parsing/skip_blanks.rdoc
|
232
379
|
#
|
233
|
-
# :include: ../doc/skip_lines.rdoc
|
380
|
+
# :include: ../doc/options/parsing/skip_lines.rdoc
|
234
381
|
#
|
235
|
-
# :include: ../doc/
|
382
|
+
# :include: ../doc/options/parsing/strip.rdoc
|
236
383
|
#
|
237
|
-
# :include: ../doc/
|
384
|
+
# :include: ../doc/options/parsing/liberal_parsing.rdoc
|
238
385
|
#
|
239
|
-
# :include: ../doc/
|
386
|
+
# :include: ../doc/options/parsing/nil_value.rdoc
|
240
387
|
#
|
241
|
-
#
|
388
|
+
# :include: ../doc/options/parsing/empty_value.rdoc
|
242
389
|
#
|
243
|
-
#
|
390
|
+
# ==== Options for Generating
|
244
391
|
#
|
245
|
-
#
|
392
|
+
# Options for generating, described in detail below, include:
|
393
|
+
# - +row_sep+: Specifies the row separator; used to delimit rows.
|
394
|
+
# - +col_sep+: Specifies the column separator; used to delimit fields.
|
395
|
+
# - +quote_char+: Specifies the quote character; used to quote fields.
|
396
|
+
# - +write_headers+: Specifies whether headers are to be written.
|
397
|
+
# - +force_quotes+: Specifies whether each output field is to be quoted.
|
398
|
+
# - +quote_empty+: Specifies whether each empty output field is to be quoted.
|
399
|
+
# - +write_converters+: Specifies the field converters to be used in writing.
|
400
|
+
# - +write_nil_value+: Specifies the object that is to be substituted for each +nil+-valued field.
|
401
|
+
# - +write_empty_value+: Specifies the object that is to be substituted for each empty field.
|
246
402
|
#
|
247
|
-
# :include: ../doc/
|
403
|
+
# :include: ../doc/options/common/row_sep.rdoc
|
248
404
|
#
|
249
|
-
# :include: ../doc/
|
405
|
+
# :include: ../doc/options/common/col_sep.rdoc
|
250
406
|
#
|
251
|
-
# :include: ../doc/
|
407
|
+
# :include: ../doc/options/common/quote_char.rdoc
|
252
408
|
#
|
253
|
-
# :include: ../doc/
|
409
|
+
# :include: ../doc/options/generating/write_headers.rdoc
|
254
410
|
#
|
255
|
-
# :include: ../doc/
|
411
|
+
# :include: ../doc/options/generating/force_quotes.rdoc
|
256
412
|
#
|
257
|
-
# :include: ../doc/
|
413
|
+
# :include: ../doc/options/generating/quote_empty.rdoc
|
258
414
|
#
|
259
|
-
# :include: ../doc/
|
415
|
+
# :include: ../doc/options/generating/write_converters.rdoc
|
260
416
|
#
|
261
|
-
# :include: ../doc/
|
417
|
+
# :include: ../doc/options/generating/write_nil_value.rdoc
|
262
418
|
#
|
263
|
-
#
|
419
|
+
# :include: ../doc/options/generating/write_empty_value.rdoc
|
420
|
+
#
|
421
|
+
# === \CSV with Headers
|
264
422
|
#
|
265
423
|
# CSV allows to specify column names of CSV file, whether they are in data, or
|
266
424
|
# provided separately. If headers are specified, reading methods return an instance
|
@@ -282,54 +440,188 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
282
440
|
# data = CSV.parse('Bob,Engineering,1000', headers: %i[name department salary])
|
283
441
|
# data.first #=> #<CSV::Row name:"Bob" department:"Engineering" salary:"1000">
|
284
442
|
#
|
285
|
-
#
|
286
|
-
#
|
287
|
-
# By default, each field parsed by \CSV is formed into a \String.
|
288
|
-
# You can use a _converter_
|
289
|
-
#
|
290
|
-
#
|
291
|
-
#
|
292
|
-
#
|
443
|
+
# === \Converters
|
444
|
+
#
|
445
|
+
# By default, each value (field or header) parsed by \CSV is formed into a \String.
|
446
|
+
# You can use a _field_ _converter_ or _header_ _converter_
|
447
|
+
# to intercept and modify the parsed values:
|
448
|
+
# - See {Field Converters}[#class-CSV-label-Field+Converters].
|
449
|
+
# - See {Header Converters}[#class-CSV-label-Header+Converters].
|
450
|
+
#
|
451
|
+
# Also by default, each value to be written during generation is written 'as-is'.
|
452
|
+
# You can use a _write_ _converter_ to modify values before writing.
|
453
|
+
# - See {Write Converters}[#class-CSV-label-Write+Converters].
|
454
|
+
#
|
455
|
+
# ==== Specifying \Converters
|
456
|
+
#
|
457
|
+
# You can specify converters for parsing or generating in the +options+
|
458
|
+
# argument to various \CSV methods:
|
459
|
+
# - Option +converters+ for converting parsed field values.
|
460
|
+
# - Option +header_converters+ for converting parsed header values.
|
461
|
+
# - Option +write_converters+ for converting values to be written (generated).
|
462
|
+
#
|
463
|
+
# There are three forms for specifying converters:
|
464
|
+
# - A converter proc: executable code to be used for conversion.
|
465
|
+
# - A converter name: the name of a stored converter.
|
466
|
+
# - A converter list: an array of converter procs, converter names, and converter lists.
|
467
|
+
#
|
468
|
+
# ===== Converter Procs
|
469
|
+
#
|
470
|
+
# This converter proc, +strip_converter+, accepts a value +field+
|
471
|
+
# and returns <tt>field.strip</tt>:
|
472
|
+
# strip_converter = proc {|field| field.strip }
|
473
|
+
# In this call to <tt>CSV.parse</tt>,
|
474
|
+
# the keyword argument <tt>converters: string_converter</tt>
|
475
|
+
# specifies that:
|
476
|
+
# - \Proc +string_converter+ is to be called for each parsed field.
|
477
|
+
# - The converter's return value is to replace the +field+ value.
|
478
|
+
# Example:
|
479
|
+
# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
|
480
|
+
# array = CSV.parse(string, converters: strip_converter)
|
481
|
+
# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
482
|
+
#
|
483
|
+
# A converter proc can receive a second argument, +field_info+,
|
484
|
+
# that contains details about the field.
|
485
|
+
# This modified +strip_converter+ displays its arguments:
|
486
|
+
# strip_converter = proc do |field, field_info|
|
487
|
+
# p [field, field_info]
|
488
|
+
# field.strip
|
489
|
+
# end
|
490
|
+
# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
|
491
|
+
# array = CSV.parse(string, converters: strip_converter)
|
492
|
+
# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
493
|
+
# Output:
|
494
|
+
# [" foo ", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
|
495
|
+
# [" 0 ", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
|
496
|
+
# [" bar ", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
|
497
|
+
# [" 1 ", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
|
498
|
+
# [" baz ", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
|
499
|
+
# [" 2 ", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
|
500
|
+
# Each CSV::Info object shows:
|
501
|
+
# - The 0-based field index.
|
502
|
+
# - The 1-based line index.
|
503
|
+
# - The field header, if any.
|
504
|
+
#
|
505
|
+
# ===== Stored \Converters
|
506
|
+
#
|
507
|
+
# A converter may be given a name and stored in a structure where
|
508
|
+
# the parsing methods can find it by name.
|
509
|
+
#
|
510
|
+
# The storage structure for field converters is the \Hash CSV::Converters.
|
511
|
+
# It has several built-in converter procs:
|
512
|
+
# - <tt>:integer</tt>: converts each \String-embedded integer into a true \Integer.
|
513
|
+
# - <tt>:float</tt>: converts each \String-embedded float into a true \Float.
|
514
|
+
# - <tt>:date</tt>: converts each \String-embedded date into a true \Date.
|
515
|
+
# - <tt>:date_time</tt>: converts each \String-embedded date-time into a true \DateTime
|
516
|
+
# .
|
517
|
+
# This example creates a converter proc, then stores it:
|
518
|
+
# strip_converter = proc {|field| field.strip }
|
519
|
+
# CSV::Converters[:strip] = strip_converter
|
520
|
+
# Then the parsing method call can refer to the converter
|
521
|
+
# by its name, <tt>:strip</tt>:
|
522
|
+
# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
|
523
|
+
# array = CSV.parse(string, converters: :strip)
|
524
|
+
# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
525
|
+
#
|
526
|
+
# The storage structure for header converters is the \Hash CSV::HeaderConverters,
|
527
|
+
# which works in the same way.
|
528
|
+
# It also has built-in converter procs:
|
529
|
+
# - <tt>:downcase</tt>: Downcases each header.
|
530
|
+
# - <tt>:symbol</tt>: Converts each header to a \Symbol.
|
531
|
+
#
|
532
|
+
# There is no such storage structure for write headers.
|
533
|
+
#
|
534
|
+
# ===== Converter Lists
|
535
|
+
#
|
536
|
+
# A _converter_ _list_ is an \Array that may include any assortment of:
|
537
|
+
# - Converter procs.
|
538
|
+
# - Names of stored converters.
|
539
|
+
# - Nested converter lists.
|
540
|
+
#
|
541
|
+
# Examples:
|
542
|
+
# numeric_converters = [:integer, :float]
|
543
|
+
# date_converters = [:date, :date_time]
|
544
|
+
# [numeric_converters, strip_converter]
|
545
|
+
# [strip_converter, date_converters, :float]
|
546
|
+
#
|
547
|
+
# Like a converter proc, a converter list may be named and stored in either
|
548
|
+
# \CSV::Converters or CSV::HeaderConverters:
|
549
|
+
# CSV::Converters[:custom] = [strip_converter, date_converters, :float]
|
550
|
+
# CSV::HeaderConverters[:custom] = [:downcase, :symbol]
|
551
|
+
#
|
552
|
+
# There are two built-in converter lists:
|
553
|
+
# CSV::Converters[:numeric] # => [:integer, :float]
|
554
|
+
# CSV::Converters[:all] # => [:date_time, :numeric]
|
555
|
+
#
|
556
|
+
# ==== Field \Converters
|
557
|
+
#
|
558
|
+
# With no conversion, all parsed fields in all rows become Strings:
|
559
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
560
|
+
# ary = CSV.parse(string)
|
561
|
+
# ary # => # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
562
|
+
#
|
563
|
+
# When you specify a field converter, each parsed field is passed to the converter;
|
564
|
+
# its return value becomes the stored value for the field.
|
293
565
|
# A converter might, for example, convert an integer embedded in a \String
|
294
566
|
# into a true \Integer.
|
295
567
|
# (In fact, that's what built-in field converter +:integer+ does.)
|
296
568
|
#
|
297
|
-
# There are
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
308
|
-
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
313
|
-
#
|
314
|
-
#
|
315
|
-
# csv
|
316
|
-
# csv.shift # => [
|
317
|
-
#
|
318
|
-
# Method #convert adds a field converter to a \CSV instance:
|
319
|
-
# csv = CSV.new('0,1,2')
|
569
|
+
# There are three ways to use field \converters.
|
570
|
+
#
|
571
|
+
# - Using option {converters}[#class-CSV-label-Option+converters] with a parsing method:
|
572
|
+
# ary = CSV.parse(string, converters: :integer)
|
573
|
+
# ary # => [0, 1, 2] # => [["foo", 0], ["bar", 1], ["baz", 2]]
|
574
|
+
# - Using option {converters}[#class-CSV-label-Option+converters] with a new \CSV instance:
|
575
|
+
# csv = CSV.new(string, converters: :integer)
|
576
|
+
# # Field converters in effect:
|
577
|
+
# csv.converters # => [:integer]
|
578
|
+
# csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
|
579
|
+
# - Using method #convert to add a field converter to a \CSV instance:
|
580
|
+
# csv = CSV.new(string)
|
581
|
+
# # Add a converter.
|
582
|
+
# csv.convert(:integer)
|
583
|
+
# csv.converters # => [:integer]
|
584
|
+
# csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
|
585
|
+
#
|
586
|
+
# Installing a field converter does not affect already-read rows:
|
587
|
+
# csv = CSV.new(string)
|
588
|
+
# csv.shift # => ["foo", "0"]
|
320
589
|
# # Add a converter.
|
321
590
|
# csv.convert(:integer)
|
322
591
|
# csv.converters # => [:integer]
|
323
|
-
# csv.
|
592
|
+
# csv.read # => [["bar", 1], ["baz", 2]]
|
324
593
|
#
|
325
|
-
#
|
326
|
-
#
|
327
|
-
# The built-in field \converters are in \Hash CSV::Converters.
|
328
|
-
# The \Symbol keys there are the names of the \converters:
|
329
|
-
#
|
330
|
-
# CSV::Converters.keys # => [:integer, :float, :numeric, :date, :date_time, :all]
|
594
|
+
# There are additional built-in \converters, and custom \converters are also supported.
|
331
595
|
#
|
332
|
-
#
|
596
|
+
# ===== Built-In Field \Converters
|
597
|
+
#
|
598
|
+
# The built-in field converters are in \Hash CSV::Converters:
|
599
|
+
# - Each key is a field converter name.
|
600
|
+
# - Each value is one of:
|
601
|
+
# - A \Proc field converter.
|
602
|
+
# - An \Array of field converter names.
|
603
|
+
#
|
604
|
+
# Display:
|
605
|
+
# CSV::Converters.each_pair do |name, value|
|
606
|
+
# if value.kind_of?(Proc)
|
607
|
+
# p [name, value.class]
|
608
|
+
# else
|
609
|
+
# p [name, value]
|
610
|
+
# end
|
611
|
+
# end
|
612
|
+
# Output:
|
613
|
+
# [:integer, Proc]
|
614
|
+
# [:float, Proc]
|
615
|
+
# [:numeric, [:integer, :float]]
|
616
|
+
# [:date, Proc]
|
617
|
+
# [:date_time, Proc]
|
618
|
+
# [:all, [:date_time, :numeric]]
|
619
|
+
#
|
620
|
+
# Each of these converters transcodes values to UTF-8 before attempting conversion.
|
621
|
+
# If a value cannot be transcoded to UTF-8 the conversion will
|
622
|
+
# fail and the value will remain unconverted.
|
623
|
+
#
|
624
|
+
# Converter +:integer+ converts each field that Integer() accepts:
|
333
625
|
# data = '0,1,2,x'
|
334
626
|
# # Without the converter
|
335
627
|
# csv = CSV.parse_line(data)
|
@@ -338,7 +630,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
338
630
|
# csv = CSV.parse_line(data, converters: :integer)
|
339
631
|
# csv # => [0, 1, 2, "x"]
|
340
632
|
#
|
341
|
-
# Converter +:float+ converts each field that
|
633
|
+
# Converter +:float+ converts each field that Float() accepts:
|
342
634
|
# data = '1.0,3.14159,x'
|
343
635
|
# # Without the converter
|
344
636
|
# csv = CSV.parse_line(data)
|
@@ -349,7 +641,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
349
641
|
#
|
350
642
|
# Converter +:numeric+ converts with both +:integer+ and +:float+..
|
351
643
|
#
|
352
|
-
# Converter +:date+ converts each field that
|
644
|
+
# Converter +:date+ converts each field that Date::parse accepts:
|
353
645
|
# data = '2001-02-03,x'
|
354
646
|
# # Without the converter
|
355
647
|
# csv = CSV.parse_line(data)
|
@@ -358,7 +650,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
358
650
|
# csv = CSV.parse_line(data, converters: :date)
|
359
651
|
# csv # => [#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>, "x"]
|
360
652
|
#
|
361
|
-
# Converter +:date_time+ converts each field that
|
653
|
+
# Converter +:date_time+ converts each field that DateTime::parse accepts:
|
362
654
|
# data = '2020-05-07T14:59:00-05:00,x'
|
363
655
|
# # Without the converter
|
364
656
|
# csv = CSV.parse_line(data)
|
@@ -378,19 +670,18 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
378
670
|
# csv.convert(:date)
|
379
671
|
# csv.converters # => [:integer, :date]
|
380
672
|
#
|
381
|
-
#
|
382
|
-
# strip_converter = proc {|field| field.strip}
|
383
|
-
# CSV::Converters[:strip] = strip_converter
|
384
|
-
# CSV::Converters.keys # => [:integer, :float, :numeric, :date, :date_time, :all, :strip]
|
385
|
-
#
|
386
|
-
# Then use it to convert fields:
|
387
|
-
# str = ' foo , 0 '
|
388
|
-
# ary = CSV.parse_line(str, converters: :strip)
|
389
|
-
# ary # => ["foo", "0"]
|
673
|
+
# ===== Custom Field \Converters
|
390
674
|
#
|
391
|
-
#
|
675
|
+
# You can define a custom field converter:
|
676
|
+
# strip_converter = proc {|field| field.strip }
|
677
|
+
# Add it to the \Converters \Hash:
|
678
|
+
# CSV::Converters[:strip] = strip_converter
|
679
|
+
# Use it by name:
|
680
|
+
# string = " foo , 0 \n bar , 1 \n baz , 2 \n"
|
681
|
+
# array = CSV.parse(string, converters: strip_converter)
|
682
|
+
# array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
392
683
|
#
|
393
|
-
#
|
684
|
+
# ==== Header \Converters
|
394
685
|
#
|
395
686
|
# Header converters operate only on headers (and not on other rows).
|
396
687
|
#
|
@@ -398,43 +689,42 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
398
689
|
# these examples use built-in header converter +:dowhcase+,
|
399
690
|
# which downcases each parsed header.
|
400
691
|
#
|
401
|
-
# Option +header_converters+ with a singleton parsing method:
|
402
|
-
#
|
403
|
-
#
|
404
|
-
#
|
405
|
-
#
|
406
|
-
#
|
407
|
-
# Option +header_converters+ with a new \CSV instance:
|
408
|
-
#
|
409
|
-
#
|
410
|
-
#
|
411
|
-
#
|
412
|
-
#
|
413
|
-
#
|
414
|
-
# Method #header_convert adds a header converter to a \CSV instance:
|
415
|
-
#
|
416
|
-
#
|
417
|
-
#
|
418
|
-
#
|
419
|
-
#
|
420
|
-
#
|
421
|
-
#
|
422
|
-
#
|
423
|
-
#
|
424
|
-
# The built-in header \converters are in \Hash CSV::
|
425
|
-
# The
|
426
|
-
#
|
692
|
+
# - Option +header_converters+ with a singleton parsing method:
|
693
|
+
# string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
|
694
|
+
# tbl = CSV.parse(string, headers: true, header_converters: :downcase)
|
695
|
+
# tbl.class # => CSV::Table
|
696
|
+
# tbl.headers # => ["name", "count"]
|
697
|
+
#
|
698
|
+
# - Option +header_converters+ with a new \CSV instance:
|
699
|
+
# csv = CSV.new(string, header_converters: :downcase)
|
700
|
+
# # Header converters in effect:
|
701
|
+
# csv.header_converters # => [:downcase]
|
702
|
+
# tbl = CSV.parse(string, headers: true)
|
703
|
+
# tbl.headers # => ["Name", "Count"]
|
704
|
+
#
|
705
|
+
# - Method #header_convert adds a header converter to a \CSV instance:
|
706
|
+
# csv = CSV.new(string)
|
707
|
+
# # Add a header converter.
|
708
|
+
# csv.header_convert(:downcase)
|
709
|
+
# csv.header_converters # => [:downcase]
|
710
|
+
# tbl = CSV.parse(string, headers: true)
|
711
|
+
# tbl.headers # => ["Name", "Count"]
|
712
|
+
#
|
713
|
+
# ===== Built-In Header \Converters
|
714
|
+
#
|
715
|
+
# The built-in header \converters are in \Hash CSV::HeaderConverters.
|
716
|
+
# The keys there are the names of the \converters:
|
427
717
|
# CSV::HeaderConverters.keys # => [:downcase, :symbol]
|
428
718
|
#
|
429
719
|
# Converter +:downcase+ converts each header by downcasing it:
|
430
|
-
#
|
431
|
-
# tbl = CSV.parse(
|
720
|
+
# string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
|
721
|
+
# tbl = CSV.parse(string, headers: true, header_converters: :downcase)
|
432
722
|
# tbl.class # => CSV::Table
|
433
723
|
# tbl.headers # => ["name", "count"]
|
434
724
|
#
|
435
|
-
# Converter +:symbol+ by making it into a \Symbol:
|
436
|
-
#
|
437
|
-
# tbl = CSV.parse(
|
725
|
+
# Converter +:symbol+ converts each header by making it into a \Symbol:
|
726
|
+
# string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
|
727
|
+
# tbl = CSV.parse(string, headers: true, header_converters: :symbol)
|
438
728
|
# tbl.headers # => [:name, :count]
|
439
729
|
# Details:
|
440
730
|
# - Strips leading and trailing whitespace.
|
@@ -443,46 +733,44 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
443
733
|
# - Removes non-word characters.
|
444
734
|
# - Makes the string into a \Symbol.
|
445
735
|
#
|
446
|
-
#
|
447
|
-
# strip_converter = proc {|field| field.strip}
|
448
|
-
# CSV::HeaderConverters[:strip] = strip_converter
|
449
|
-
# CSV::HeaderConverters.keys # => [:downcase, :symbol, :strip]
|
450
|
-
#
|
451
|
-
# Then use it to convert headers:
|
452
|
-
# str = " Name , Value \nfoo,0\nbar,1\nbaz,2"
|
453
|
-
# tbl = CSV.parse(str, headers: true, header_converters: :strip)
|
454
|
-
# tbl.headers # => ["Name", "Value"]
|
455
|
-
#
|
456
|
-
# See {Custom Converters}[#class-CSV-label-Custom+Converters].
|
457
|
-
#
|
458
|
-
# === Custom \Converters
|
459
|
-
#
|
460
|
-
# You can define custom \converters.
|
461
|
-
#
|
462
|
-
# The \converter is a \Proc that is called with two arguments,
|
463
|
-
# \String +field+ and CSV::FieldInfo +field_info+;
|
464
|
-
# it returns a \String that will become the field value:
|
465
|
-
# converter = proc {|field, field_info| <some_string> }
|
736
|
+
# ===== Custom Header \Converters
|
466
737
|
#
|
467
|
-
#
|
468
|
-
#
|
469
|
-
#
|
738
|
+
# You can define a custom header converter:
|
739
|
+
# upcase_converter = proc {|header| header.upcase }
|
740
|
+
# Add it to the \HeaderConverters \Hash:
|
741
|
+
# CSV::HeaderConverters[:upcase] = upcase_converter
|
742
|
+
# Use it by name:
|
743
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
744
|
+
# table = CSV.parse(string, headers: true, converters: upcase_converter)
|
745
|
+
# table # => #<CSV::Table mode:col_or_row row_count:4>
|
746
|
+
# table.headers # => ["Name", "Value"]
|
470
747
|
#
|
471
|
-
#
|
472
|
-
# ["foo", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
|
473
|
-
# ["0", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
|
748
|
+
# ===== Write \Converters
|
474
749
|
#
|
475
|
-
#
|
476
|
-
#
|
477
|
-
#
|
478
|
-
#
|
479
|
-
#
|
480
|
-
#
|
481
|
-
#
|
482
|
-
#
|
483
|
-
#
|
484
|
-
#
|
485
|
-
#
|
750
|
+
# When you specify a write converter for generating \CSV,
|
751
|
+
# each field to be written is passed to the converter;
|
752
|
+
# its return value becomes the new value for the field.
|
753
|
+
# A converter might, for example, strip whitespace from a field.
|
754
|
+
#
|
755
|
+
# - Using no write converter (all fields unmodified):
|
756
|
+
# output_string = CSV.generate do |csv|
|
757
|
+
# csv << [' foo ', 0]
|
758
|
+
# csv << [' bar ', 1]
|
759
|
+
# csv << [' baz ', 2]
|
760
|
+
# end
|
761
|
+
# output_string # => " foo ,0\n bar ,1\n baz ,2\n"
|
762
|
+
# - Using option +write_converters+:
|
763
|
+
# strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field }
|
764
|
+
# upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field }
|
765
|
+
# converters = [strip_converter, upcase_converter]
|
766
|
+
# output_string = CSV.generate(write_converters: converters) do |csv|
|
767
|
+
# csv << [' foo ', 0]
|
768
|
+
# csv << [' bar ', 1]
|
769
|
+
# csv << [' baz ', 2]
|
770
|
+
# end
|
771
|
+
# output_string # => "FOO,0\nBAR,1\nBAZ,2\n"
|
772
|
+
#
|
773
|
+
# === Character Encodings (M17n or Multilingualization)
|
486
774
|
#
|
487
775
|
# This new CSV parser is m17n savvy. The parser works in the Encoding of the IO
|
488
776
|
# or String object being read from or written to. Your data is never transcoded
|
@@ -563,30 +851,12 @@ class CSV
|
|
563
851
|
# The encoding used by all converters.
|
564
852
|
ConverterEncoding = Encoding.find("UTF-8")
|
565
853
|
|
854
|
+
# A \Hash containing the names and \Procs for the built-in field converters.
|
855
|
+
# See {Built-In Field Converters}[#class-CSV-label-Built-In+Field+Converters].
|
566
856
|
#
|
567
|
-
# This Hash
|
568
|
-
#
|
569
|
-
#
|
570
|
-
#
|
571
|
-
# <b><tt>:integer</tt></b>:: Converts any field Integer() accepts.
|
572
|
-
# <b><tt>:float</tt></b>:: Converts any field Float() accepts.
|
573
|
-
# <b><tt>:numeric</tt></b>:: A combination of <tt>:integer</tt>
|
574
|
-
# and <tt>:float</tt>.
|
575
|
-
# <b><tt>:date</tt></b>:: Converts any field Date::parse() accepts.
|
576
|
-
# <b><tt>:date_time</tt></b>:: Converts any field DateTime::parse() accepts.
|
577
|
-
# <b><tt>:all</tt></b>:: All built-in converters. A combination of
|
578
|
-
# <tt>:date_time</tt> and <tt>:numeric</tt>.
|
579
|
-
#
|
580
|
-
# All built-in converters transcode field data to UTF-8 before attempting a
|
581
|
-
# conversion. If your data cannot be transcoded to UTF-8 the conversion will
|
582
|
-
# fail and the field will remain unchanged.
|
583
|
-
#
|
584
|
-
# This Hash is intentionally left unfrozen and users should feel free to add
|
585
|
-
# values to it that can be accessed by all CSV objects.
|
586
|
-
#
|
587
|
-
# To add a combo field, the value should be an Array of names. Combo fields
|
588
|
-
# can be nested with other combo fields.
|
589
|
-
#
|
857
|
+
# This \Hash is intentionally left unfrozen, and may be extended with
|
858
|
+
# custom field converters.
|
859
|
+
# See {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters].
|
590
860
|
Converters = {
|
591
861
|
integer: lambda { |f|
|
592
862
|
Integer(f.encode(ConverterEncoding)) rescue f
|
@@ -614,27 +884,12 @@ class CSV
|
|
614
884
|
all: [:date_time, :numeric],
|
615
885
|
}
|
616
886
|
|
887
|
+
# A \Hash containing the names and \Procs for the built-in header converters.
|
888
|
+
# See {Built-In Header Converters}[#class-CSV-label-Built-In+Header+Converters].
|
617
889
|
#
|
618
|
-
# This Hash
|
619
|
-
#
|
620
|
-
#
|
621
|
-
#
|
622
|
-
# <b><tt>:downcase</tt></b>:: Calls downcase() on the header String.
|
623
|
-
# <b><tt>:symbol</tt></b>:: Leading/trailing spaces are dropped, string is
|
624
|
-
# downcased, remaining spaces are replaced with
|
625
|
-
# underscores, non-word characters are dropped,
|
626
|
-
# and finally to_sym() is called.
|
627
|
-
#
|
628
|
-
# All built-in header converters transcode header data to UTF-8 before
|
629
|
-
# attempting a conversion. If your data cannot be transcoded to UTF-8 the
|
630
|
-
# conversion will fail and the header will remain unchanged.
|
631
|
-
#
|
632
|
-
# This Hash is intentionally left unfrozen and users should feel free to add
|
633
|
-
# values to it that can be accessed by all CSV objects.
|
634
|
-
#
|
635
|
-
# To add a combo field, the value should be an Array of names. Combo fields
|
636
|
-
# can be nested with other combo fields.
|
637
|
-
#
|
890
|
+
# This \Hash is intentionally left unfrozen, and may be extended with
|
891
|
+
# custom field converters.
|
892
|
+
# See {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters].
|
638
893
|
HeaderConverters = {
|
639
894
|
downcase: lambda { |h| h.encode(ConverterEncoding).downcase },
|
640
895
|
symbol: lambda { |h|
|
@@ -671,18 +926,47 @@ class CSV
|
|
671
926
|
}.freeze
|
672
927
|
|
673
928
|
class << self
|
929
|
+
# :call-seq:
|
930
|
+
# instance(string, **options)
|
931
|
+
# instance(io = $stdout, **options)
|
932
|
+
# instance(string, **options) {|csv| ... }
|
933
|
+
# instance(io = $stdout, **options) {|csv| ... }
|
674
934
|
#
|
675
|
-
#
|
676
|
-
#
|
677
|
-
# the same +data+ object (tested by Object#object_id()) with the same
|
678
|
-
# +options+.
|
935
|
+
# Creates or retrieves cached \CSV objects.
|
936
|
+
# For arguments and options, see CSV.new.
|
679
937
|
#
|
680
|
-
#
|
681
|
-
# and {Options for Generating}[#class-CSV-label-Options+for+Generating].
|
938
|
+
# ---
|
682
939
|
#
|
683
|
-
#
|
684
|
-
# value becomes the return value of the block.
|
940
|
+
# With no block given, returns a \CSV object.
|
685
941
|
#
|
942
|
+
# The first call to +instance+ creates and caches a \CSV object:
|
943
|
+
# s0 = 's0'
|
944
|
+
# csv0 = CSV.instance(s0)
|
945
|
+
# csv0.class # => CSV
|
946
|
+
#
|
947
|
+
# Subsequent calls to +instance+ with that _same_ +string+ or +io+
|
948
|
+
# retrieve that same cached object:
|
949
|
+
# csv1 = CSV.instance(s0)
|
950
|
+
# csv1.class # => CSV
|
951
|
+
# csv1.equal?(csv0) # => true # Same CSV object
|
952
|
+
#
|
953
|
+
# A subsequent call to +instance+ with a _different_ +string+ or +io+
|
954
|
+
# creates and caches a _different_ \CSV object.
|
955
|
+
# s1 = 's1'
|
956
|
+
# csv2 = CSV.instance(s1)
|
957
|
+
# csv2.equal?(csv0) # => false # Different CSV object
|
958
|
+
#
|
959
|
+
# All the cached objects remains available:
|
960
|
+
# csv3 = CSV.instance(s0)
|
961
|
+
# csv3.equal?(csv0) # true # Same CSV object
|
962
|
+
# csv4 = CSV.instance(s1)
|
963
|
+
# csv4.equal?(csv2) # true # Same CSV object
|
964
|
+
#
|
965
|
+
# ---
|
966
|
+
#
|
967
|
+
# When a block is given, calls the block with the created or retrieved
|
968
|
+
# \CSV object; returns the block's return value:
|
969
|
+
# CSV.instance(s0) {|csv| :foo } # => :foo
|
686
970
|
def instance(data = $stdout, **options)
|
687
971
|
# create a _signature_ for this method call, data object and options
|
688
972
|
sig = [data.object_id] +
|
@@ -699,33 +983,61 @@ class CSV
|
|
699
983
|
end
|
700
984
|
end
|
701
985
|
|
702
|
-
#
|
703
986
|
# :call-seq:
|
704
|
-
# filter(
|
705
|
-
# filter(
|
706
|
-
# filter(
|
987
|
+
# filter(**options) {|row| ... }
|
988
|
+
# filter(in_string, **options) {|row| ... }
|
989
|
+
# filter(in_io, **options) {|row| ... }
|
990
|
+
# filter(in_string, out_string, **options) {|row| ... }
|
991
|
+
# filter(in_string, out_io, **options) {|row| ... }
|
992
|
+
# filter(in_io, out_string, **options) {|row| ... }
|
993
|
+
# filter(in_io, out_io, **options) {|row| ... }
|
707
994
|
#
|
708
|
-
#
|
709
|
-
# Each row is yielded to the provided block which can alter it as needed.
|
710
|
-
# After the block returns, the row is appended to +output+ altered or not.
|
995
|
+
# Reads \CSV input and writes \CSV output.
|
711
996
|
#
|
712
|
-
#
|
713
|
-
#
|
714
|
-
#
|
997
|
+
# For each input row:
|
998
|
+
# - Forms the data into:
|
999
|
+
# - A CSV::Row object, if headers are in use.
|
1000
|
+
# - An \Array of Arrays, otherwise.
|
1001
|
+
# - Calls the block with that object.
|
1002
|
+
# - Appends the block's return value to the output.
|
715
1003
|
#
|
716
|
-
#
|
717
|
-
#
|
718
|
-
#
|
719
|
-
# be
|
720
|
-
#
|
721
|
-
#
|
722
|
-
#
|
723
|
-
#
|
724
|
-
#
|
725
|
-
#
|
726
|
-
#
|
727
|
-
#
|
1004
|
+
# Arguments:
|
1005
|
+
# * \CSV source:
|
1006
|
+
# * Argument +in_string+, if given, should be a \String object;
|
1007
|
+
# it will be put into a new StringIO object positioned at the beginning.
|
1008
|
+
# * Argument +in_io+, if given, should be an IO object that is
|
1009
|
+
# open for reading; on return, the IO object will be closed.
|
1010
|
+
# * If neither +in_string+ nor +in_io+ is given,
|
1011
|
+
# the input stream defaults to {ARGF}[https://ruby-doc.org/core/ARGF.html].
|
1012
|
+
# * \CSV output:
|
1013
|
+
# * Argument +out_string+, if given, should be a \String object;
|
1014
|
+
# it will be put into a new StringIO object positioned at the beginning.
|
1015
|
+
# * Argument +out_io+, if given, should be an IO object that is
|
1016
|
+
# ppen for writing; on return, the IO object will be closed.
|
1017
|
+
# * If neither +out_string+ nor +out_io+ is given,
|
1018
|
+
# the output stream defaults to <tt>$stdout</tt>.
|
1019
|
+
# * Argument +options+ should be keyword arguments.
|
1020
|
+
# - Each argument name that is prefixed with +in_+ or +input_+
|
1021
|
+
# is stripped of its prefix and is treated as an option
|
1022
|
+
# for parsing the input.
|
1023
|
+
# Option +input_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
|
1024
|
+
# - Each argument name that is prefixed with +out_+ or +output_+
|
1025
|
+
# is stripped of its prefix and is treated as an option
|
1026
|
+
# for generating the output.
|
1027
|
+
# Option +output_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
|
1028
|
+
# - Each argument not prefixed as above is treated as an option
|
1029
|
+
# both for parsing the input and for generating the output.
|
1030
|
+
# - See {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
|
1031
|
+
# and {Options for Generating}[#class-CSV-label-Options+for+Generating].
|
728
1032
|
#
|
1033
|
+
# Example:
|
1034
|
+
# in_string = "foo,0\nbar,1\nbaz,2\n"
|
1035
|
+
# out_string = ''
|
1036
|
+
# CSV.filter(in_string, out_string) do |row|
|
1037
|
+
# row[0] = row[0].upcase
|
1038
|
+
# row[1] *= 4
|
1039
|
+
# end
|
1040
|
+
# out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
|
729
1041
|
def filter(input=nil, output=nil, **options)
|
730
1042
|
# parse options for input, output, or both
|
731
1043
|
in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR}
|
@@ -752,20 +1064,111 @@ class CSV
|
|
752
1064
|
end
|
753
1065
|
|
754
1066
|
#
|
755
|
-
#
|
756
|
-
#
|
757
|
-
#
|
1067
|
+
# :call-seq:
|
1068
|
+
# foreach(path, mode='r', **options) {|row| ... )
|
1069
|
+
# foreach(io, mode='r', **options {|row| ... )
|
1070
|
+
# foreach(path, mode='r', headers: ..., **options) {|row| ... )
|
1071
|
+
# foreach(io, mode='r', headers: ..., **options {|row| ... )
|
1072
|
+
# foreach(path, mode='r', **options) -> new_enumerator
|
1073
|
+
# foreach(io, mode='r', **options -> new_enumerator
|
1074
|
+
#
|
1075
|
+
# Calls the block with each row read from source +path+ or +io+.
|
1076
|
+
#
|
1077
|
+
# * Argument +path+, if given, must be the path to a file.
|
1078
|
+
# :include: ../doc/arguments/io.rdoc
|
1079
|
+
# * Argument +mode+, if given, must be a \File mode
|
1080
|
+
# See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
|
1081
|
+
# * Arguments <tt>**options</tt> must be keyword options.
|
1082
|
+
# See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
|
1083
|
+
# * This method optionally accepts an additional <tt>:encoding</tt> option
|
1084
|
+
# that you can use to specify the Encoding of the data read from +path+ or +io+.
|
1085
|
+
# You must provide this unless your data is in the encoding
|
1086
|
+
# given by <tt>Encoding::default_external</tt>.
|
1087
|
+
# Parsing will use this to determine how to parse the data.
|
1088
|
+
# You may provide a second Encoding to
|
1089
|
+
# have the data transcoded as it is read. For example,
|
1090
|
+
# encoding: 'UTF-32BE:UTF-8'
|
1091
|
+
# would read +UTF-32BE+ data from the file
|
1092
|
+
# but transcode it to +UTF-8+ before parsing.
|
1093
|
+
#
|
1094
|
+
# ====== Without Option +headers+
|
1095
|
+
#
|
1096
|
+
# Without option +headers+, returns each row as an \Array object.
|
1097
|
+
#
|
1098
|
+
# These examples assume prior execution of:
|
1099
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
1100
|
+
# path = 't.csv'
|
1101
|
+
# File.write(path, string)
|
1102
|
+
#
|
1103
|
+
# Read rows from a file at +path+:
|
1104
|
+
# CSV.foreach(path) {|row| p row }
|
1105
|
+
# Output:
|
1106
|
+
# ["foo", "0"]
|
1107
|
+
# ["bar", "1"]
|
1108
|
+
# ["baz", "2"]
|
1109
|
+
#
|
1110
|
+
# Read rows from an \IO object:
|
1111
|
+
# File.open(path) do |file|
|
1112
|
+
# CSV.foreach(file) {|row| p row }
|
1113
|
+
# end
|
1114
|
+
#
|
1115
|
+
# Output:
|
1116
|
+
# ["foo", "0"]
|
1117
|
+
# ["bar", "1"]
|
1118
|
+
# ["baz", "2"]
|
1119
|
+
#
|
1120
|
+
# Returns a new \Enumerator if no block given:
|
1121
|
+
# CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
|
1122
|
+
# CSV.foreach(File.open(path)) # => #<Enumerator: CSV:foreach(#<File:t.csv>, "r")>
|
1123
|
+
#
|
1124
|
+
# Issues a warning if an encoding is unsupported:
|
1125
|
+
# CSV.foreach(File.open(path), encoding: 'foo:bar') {|row| }
|
1126
|
+
# Output:
|
1127
|
+
# warning: Unsupported encoding foo ignored
|
1128
|
+
# warning: Unsupported encoding bar ignored
|
1129
|
+
#
|
1130
|
+
# ====== With Option +headers+
|
1131
|
+
#
|
1132
|
+
# With {option +headers+}[#class-CSV-label-Option+headers],
|
1133
|
+
# returns each row as a CSV::Row object.
|
758
1134
|
#
|
759
|
-
#
|
1135
|
+
# These examples assume prior execution of:
|
1136
|
+
# string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
|
1137
|
+
# path = 't.csv'
|
1138
|
+
# File.write(path, string)
|
760
1139
|
#
|
761
|
-
#
|
762
|
-
#
|
763
|
-
#
|
764
|
-
#
|
765
|
-
#
|
766
|
-
#
|
767
|
-
#
|
768
|
-
#
|
1140
|
+
# Read rows from a file at +path+:
|
1141
|
+
# CSV.foreach(path, headers: true) {|row| p row }
|
1142
|
+
#
|
1143
|
+
# Output:
|
1144
|
+
# #<CSV::Row "Name":"foo" "Count":"0">
|
1145
|
+
# #<CSV::Row "Name":"bar" "Count":"1">
|
1146
|
+
# #<CSV::Row "Name":"baz" "Count":"2">
|
1147
|
+
#
|
1148
|
+
# Read rows from an \IO object:
|
1149
|
+
# File.open(path) do |file|
|
1150
|
+
# CSV.foreach(file, headers: true) {|row| p row }
|
1151
|
+
# end
|
1152
|
+
#
|
1153
|
+
# Output:
|
1154
|
+
# #<CSV::Row "Name":"foo" "Count":"0">
|
1155
|
+
# #<CSV::Row "Name":"bar" "Count":"1">
|
1156
|
+
# #<CSV::Row "Name":"baz" "Count":"2">
|
1157
|
+
#
|
1158
|
+
# ---
|
1159
|
+
#
|
1160
|
+
# Raises an exception if +path+ is a \String, but not the path to a readable file:
|
1161
|
+
# # Raises Errno::ENOENT (No such file or directory @ rb_sysopen - nosuch.csv):
|
1162
|
+
# CSV.foreach('nosuch.csv') {|row| }
|
1163
|
+
#
|
1164
|
+
# Raises an exception if +io+ is an \IO object, but not open for reading:
|
1165
|
+
# io = File.open(path, 'w') {|row| }
|
1166
|
+
# # Raises TypeError (no implicit conversion of nil into String):
|
1167
|
+
# CSV.foreach(io) {|row| }
|
1168
|
+
#
|
1169
|
+
# Raises an exception if +mode+ is invalid:
|
1170
|
+
# # Raises ArgumentError (invalid access mode nosuch):
|
1171
|
+
# CSV.foreach(path, 'nosuch') {|row| }
|
769
1172
|
#
|
770
1173
|
def foreach(path, mode="r", **options, &block)
|
771
1174
|
return to_enum(__method__, path, mode, **options) unless block_given?
|
@@ -776,23 +1179,63 @@ class CSV
|
|
776
1179
|
|
777
1180
|
#
|
778
1181
|
# :call-seq:
|
779
|
-
# generate(
|
780
|
-
# generate(
|
1182
|
+
# generate(csv_string, **options) {|csv| ... }
|
1183
|
+
# generate(**options) {|csv| ... }
|
781
1184
|
#
|
782
|
-
#
|
783
|
-
#
|
784
|
-
#
|
785
|
-
#
|
1185
|
+
# * Argument +csv_string+, if given, must be a \String object;
|
1186
|
+
# defaults to a new empty \String.
|
1187
|
+
# * Arguments +options+, if given, should be generating options.
|
1188
|
+
# See {Options for Generating}[#class-CSV-label-Options+for+Generating].
|
786
1189
|
#
|
787
|
-
#
|
788
|
-
#
|
1190
|
+
# ---
|
1191
|
+
#
|
1192
|
+
# Creates a new \CSV object via <tt>CSV.new(csv_string, **options)</tt>;
|
1193
|
+
# calls the block with the \CSV object, which the block may modify;
|
1194
|
+
# returns the \String generated from the \CSV object.
|
789
1195
|
#
|
790
|
-
#
|
1196
|
+
# Note that a passed \String *is* modified by this method.
|
1197
|
+
# Pass <tt>csv_string</tt>.dup if the \String must be preserved.
|
791
1198
|
#
|
792
1199
|
# This method has one additional option: <tt>:encoding</tt>,
|
793
1200
|
# which sets the base Encoding for the output if no no +str+ is specified.
|
794
1201
|
# CSV needs this hint if you plan to output non-ASCII compatible data.
|
795
1202
|
#
|
1203
|
+
# ---
|
1204
|
+
#
|
1205
|
+
# Add lines:
|
1206
|
+
# input_string = "foo,0\nbar,1\nbaz,2\n"
|
1207
|
+
# output_string = CSV.generate(input_string) do |csv|
|
1208
|
+
# csv << ['bat', 3]
|
1209
|
+
# csv << ['bam', 4]
|
1210
|
+
# end
|
1211
|
+
# output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
|
1212
|
+
# input_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
|
1213
|
+
# output_string.equal?(input_string) # => true # Same string, modified
|
1214
|
+
#
|
1215
|
+
# Add lines into new string, preserving old string:
|
1216
|
+
# input_string = "foo,0\nbar,1\nbaz,2\n"
|
1217
|
+
# output_string = CSV.generate(input_string.dup) do |csv|
|
1218
|
+
# csv << ['bat', 3]
|
1219
|
+
# csv << ['bam', 4]
|
1220
|
+
# end
|
1221
|
+
# output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
|
1222
|
+
# input_string # => "foo,0\nbar,1\nbaz,2\n"
|
1223
|
+
# output_string.equal?(input_string) # => false # Different strings
|
1224
|
+
#
|
1225
|
+
# Create lines from nothing:
|
1226
|
+
# output_string = CSV.generate do |csv|
|
1227
|
+
# csv << ['foo', 0]
|
1228
|
+
# csv << ['bar', 1]
|
1229
|
+
# csv << ['baz', 2]
|
1230
|
+
# end
|
1231
|
+
# output_string # => "foo,0\nbar,1\nbaz,2\n"
|
1232
|
+
#
|
1233
|
+
# ---
|
1234
|
+
#
|
1235
|
+
# Raises an exception if +csv_string+ is not a \String object:
|
1236
|
+
# # Raises TypeError (no implicit conversion of Integer into String)
|
1237
|
+
# CSV.generate(0)
|
1238
|
+
#
|
796
1239
|
def generate(str=nil, **options)
|
797
1240
|
encoding = options[:encoding]
|
798
1241
|
# add a default empty String, if none was given
|
@@ -846,80 +1289,103 @@ class CSV
|
|
846
1289
|
str = +""
|
847
1290
|
if options[:encoding]
|
848
1291
|
str.force_encoding(options[:encoding])
|
849
|
-
|
850
|
-
|
1292
|
+
else
|
1293
|
+
fallback_encoding = nil
|
1294
|
+
output_encoding = nil
|
1295
|
+
row.each do |field|
|
1296
|
+
next unless field.is_a?(String)
|
1297
|
+
fallback_encoding ||= field.encoding
|
1298
|
+
next if field.ascii_only?
|
1299
|
+
output_encoding = field.encoding
|
1300
|
+
break
|
1301
|
+
end
|
1302
|
+
output_encoding ||= fallback_encoding
|
1303
|
+
if output_encoding
|
1304
|
+
str.force_encoding(output_encoding)
|
1305
|
+
end
|
851
1306
|
end
|
852
1307
|
(new(str, **options) << row).string
|
853
1308
|
end
|
854
1309
|
|
855
1310
|
#
|
856
1311
|
# :call-seq:
|
857
|
-
# open(
|
858
|
-
# open(
|
859
|
-
# open(
|
860
|
-
# open(
|
861
|
-
#
|
862
|
-
#
|
863
|
-
#
|
864
|
-
#
|
865
|
-
#
|
866
|
-
#
|
867
|
-
#
|
868
|
-
#
|
869
|
-
#
|
870
|
-
#
|
871
|
-
#
|
872
|
-
#
|
873
|
-
#
|
874
|
-
#
|
875
|
-
#
|
876
|
-
#
|
877
|
-
#
|
878
|
-
#
|
879
|
-
#
|
880
|
-
#
|
881
|
-
#
|
882
|
-
#
|
883
|
-
#
|
884
|
-
#
|
885
|
-
#
|
886
|
-
#
|
887
|
-
#
|
888
|
-
#
|
889
|
-
#
|
890
|
-
#
|
891
|
-
#
|
892
|
-
#
|
893
|
-
#
|
894
|
-
#
|
895
|
-
#
|
896
|
-
# * fcntl()
|
897
|
-
# * fileno()
|
898
|
-
# * flock()
|
899
|
-
# * flush()
|
900
|
-
# * fsync()
|
901
|
-
# * internal_encoding()
|
902
|
-
# * ioctl()
|
903
|
-
# * isatty()
|
904
|
-
# * path()
|
905
|
-
# * pid()
|
906
|
-
# * pos()
|
907
|
-
# * pos=()
|
908
|
-
# * reopen()
|
909
|
-
# * seek()
|
910
|
-
# * stat()
|
911
|
-
# * sync()
|
912
|
-
# * sync=()
|
913
|
-
# * tell()
|
914
|
-
# * to_i()
|
915
|
-
# * to_io()
|
916
|
-
# * truncate()
|
917
|
-
# * tty?()
|
1312
|
+
# open(file_path, mode = "rb", **options ) -> new_csv
|
1313
|
+
# open(io, mode = "rb", **options ) -> new_csv
|
1314
|
+
# open(file_path, mode = "rb", **options ) { |csv| ... } -> object
|
1315
|
+
# open(io, mode = "rb", **options ) { |csv| ... } -> object
|
1316
|
+
#
|
1317
|
+
# possible options elements:
|
1318
|
+
# hash form:
|
1319
|
+
# :invalid => nil # raise error on invalid byte sequence (default)
|
1320
|
+
# :invalid => :replace # replace invalid byte sequence
|
1321
|
+
# :undef => :replace # replace undefined conversion
|
1322
|
+
# :replace => string # replacement string ("?" or "\uFFFD" if not specified)
|
1323
|
+
#
|
1324
|
+
# * Argument +path+, if given, must be the path to a file.
|
1325
|
+
# :include: ../doc/arguments/io.rdoc
|
1326
|
+
# * Argument +mode+, if given, must be a \File mode
|
1327
|
+
# See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
|
1328
|
+
# * Arguments <tt>**options</tt> must be keyword options.
|
1329
|
+
# See {Options for Generating}[#class-CSV-label-Options+for+Generating].
|
1330
|
+
# * This method optionally accepts an additional <tt>:encoding</tt> option
|
1331
|
+
# that you can use to specify the Encoding of the data read from +path+ or +io+.
|
1332
|
+
# You must provide this unless your data is in the encoding
|
1333
|
+
# given by <tt>Encoding::default_external</tt>.
|
1334
|
+
# Parsing will use this to determine how to parse the data.
|
1335
|
+
# You may provide a second Encoding to
|
1336
|
+
# have the data transcoded as it is read. For example,
|
1337
|
+
# encoding: 'UTF-32BE:UTF-8'
|
1338
|
+
# would read +UTF-32BE+ data from the file
|
1339
|
+
# but transcode it to +UTF-8+ before parsing.
|
1340
|
+
#
|
1341
|
+
# ---
|
1342
|
+
#
|
1343
|
+
# These examples assume prior execution of:
|
1344
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
1345
|
+
# path = 't.csv'
|
1346
|
+
# File.write(path, string)
|
1347
|
+
#
|
1348
|
+
# ---
|
1349
|
+
#
|
1350
|
+
# With no block given, returns a new \CSV object.
|
918
1351
|
#
|
1352
|
+
# Create a \CSV object using a file path:
|
1353
|
+
# csv = CSV.open(path)
|
1354
|
+
# csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1355
|
+
#
|
1356
|
+
# Create a \CSV object using an open \File:
|
1357
|
+
# csv = CSV.open(File.open(path))
|
1358
|
+
# csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1359
|
+
#
|
1360
|
+
# ---
|
1361
|
+
#
|
1362
|
+
# With a block given, calls the block with the created \CSV object;
|
1363
|
+
# returns the block's return value:
|
1364
|
+
#
|
1365
|
+
# Using a file path:
|
1366
|
+
# csv = CSV.open(path) {|csv| p csv}
|
1367
|
+
# csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1368
|
+
# Output:
|
1369
|
+
# #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1370
|
+
#
|
1371
|
+
# Using an open \File:
|
1372
|
+
# csv = CSV.open(File.open(path)) {|csv| p csv}
|
1373
|
+
# csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1374
|
+
# Output:
|
1375
|
+
# #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1376
|
+
#
|
1377
|
+
# ---
|
1378
|
+
#
|
1379
|
+
# Raises an exception if the argument is not a \String object or \IO object:
|
1380
|
+
# # Raises TypeError (no implicit conversion of Symbol into String)
|
1381
|
+
# CSV.open(:foo)
|
919
1382
|
def open(filename, mode="r", **options)
|
920
1383
|
# wrap a File opened with the remaining +args+ with no newline
|
921
1384
|
# decorator
|
922
1385
|
file_opts = {universal_newline: false}.merge(options)
|
1386
|
+
options.delete(:invalid)
|
1387
|
+
options.delete(:undef)
|
1388
|
+
options.delete(:replace)
|
923
1389
|
|
924
1390
|
begin
|
925
1391
|
f = File.open(filename, mode, **file_opts)
|
@@ -950,16 +1416,116 @@ class CSV
|
|
950
1416
|
|
951
1417
|
#
|
952
1418
|
# :call-seq:
|
953
|
-
# parse(
|
954
|
-
# parse(
|
1419
|
+
# parse(string) -> array_of_arrays
|
1420
|
+
# parse(io) -> array_of_arrays
|
1421
|
+
# parse(string, headers: ..., **options) -> csv_table
|
1422
|
+
# parse(io, headers: ..., **options) -> csv_table
|
1423
|
+
# parse(string, **options) {|row| ... }
|
1424
|
+
# parse(io, **options) {|row| ... }
|
1425
|
+
#
|
1426
|
+
# Parses +string+ or +io+ using the specified +options+.
|
1427
|
+
#
|
1428
|
+
# - Argument +string+ should be a \String object;
|
1429
|
+
# it will be put into a new StringIO object positioned at the beginning.
|
1430
|
+
# :include: ../doc/arguments/io.rdoc
|
1431
|
+
# - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
|
1432
|
+
#
|
1433
|
+
# ====== Without Option +headers+
|
1434
|
+
#
|
1435
|
+
# Without {option +headers+}[#class-CSV-label-Option+headers] case.
|
1436
|
+
#
|
1437
|
+
# These examples assume prior execution of:
|
1438
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
1439
|
+
# path = 't.csv'
|
1440
|
+
# File.write(path, string)
|
1441
|
+
#
|
1442
|
+
# ---
|
1443
|
+
#
|
1444
|
+
# With no block given, returns an \Array of Arrays formed from the source.
|
1445
|
+
#
|
1446
|
+
# Parse a \String:
|
1447
|
+
# a_of_a = CSV.parse(string)
|
1448
|
+
# a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
1449
|
+
#
|
1450
|
+
# Parse an open \File:
|
1451
|
+
# a_of_a = File.open(path) do |file|
|
1452
|
+
# CSV.parse(file)
|
1453
|
+
# end
|
1454
|
+
# a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
1455
|
+
#
|
1456
|
+
# ---
|
1457
|
+
#
|
1458
|
+
# With a block given, calls the block with each parsed row:
|
1459
|
+
#
|
1460
|
+
# Parse a \String:
|
1461
|
+
# CSV.parse(string) {|row| p row }
|
955
1462
|
#
|
956
|
-
#
|
957
|
-
#
|
958
|
-
#
|
1463
|
+
# Output:
|
1464
|
+
# ["foo", "0"]
|
1465
|
+
# ["bar", "1"]
|
1466
|
+
# ["baz", "2"]
|
1467
|
+
#
|
1468
|
+
# Parse an open \File:
|
1469
|
+
# File.open(path) do |file|
|
1470
|
+
# CSV.parse(file) {|row| p row }
|
1471
|
+
# end
|
1472
|
+
#
|
1473
|
+
# Output:
|
1474
|
+
# ["foo", "0"]
|
1475
|
+
# ["bar", "1"]
|
1476
|
+
# ["baz", "2"]
|
1477
|
+
#
|
1478
|
+
# ====== With Option +headers+
|
1479
|
+
#
|
1480
|
+
# With {option +headers+}[#class-CSV-label-Option+headers] case.
|
1481
|
+
#
|
1482
|
+
# These examples assume prior execution of:
|
1483
|
+
# string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
|
1484
|
+
# path = 't.csv'
|
1485
|
+
# File.write(path, string)
|
1486
|
+
#
|
1487
|
+
# ---
|
959
1488
|
#
|
960
|
-
#
|
961
|
-
# See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
|
1489
|
+
# With no block given, returns a CSV::Table object formed from the source.
|
962
1490
|
#
|
1491
|
+
# Parse a \String:
|
1492
|
+
# csv_table = CSV.parse(string, headers: ['Name', 'Count'])
|
1493
|
+
# csv_table # => #<CSV::Table mode:col_or_row row_count:5>
|
1494
|
+
#
|
1495
|
+
# Parse an open \File:
|
1496
|
+
# csv_table = File.open(path) do |file|
|
1497
|
+
# CSV.parse(file, headers: ['Name', 'Count'])
|
1498
|
+
# end
|
1499
|
+
# csv_table # => #<CSV::Table mode:col_or_row row_count:4>
|
1500
|
+
#
|
1501
|
+
# ---
|
1502
|
+
#
|
1503
|
+
# With a block given, calls the block with each parsed row,
|
1504
|
+
# which has been formed into a CSV::Row object:
|
1505
|
+
#
|
1506
|
+
# Parse a \String:
|
1507
|
+
# CSV.parse(string, headers: ['Name', 'Count']) {|row| p row }
|
1508
|
+
#
|
1509
|
+
# Output:
|
1510
|
+
# # <CSV::Row "Name":"foo" "Count":"0">
|
1511
|
+
# # <CSV::Row "Name":"bar" "Count":"1">
|
1512
|
+
# # <CSV::Row "Name":"baz" "Count":"2">
|
1513
|
+
#
|
1514
|
+
# Parse an open \File:
|
1515
|
+
# File.open(path) do |file|
|
1516
|
+
# CSV.parse(file, headers: ['Name', 'Count']) {|row| p row }
|
1517
|
+
# end
|
1518
|
+
#
|
1519
|
+
# Output:
|
1520
|
+
# # <CSV::Row "Name":"foo" "Count":"0">
|
1521
|
+
# # <CSV::Row "Name":"bar" "Count":"1">
|
1522
|
+
# # <CSV::Row "Name":"baz" "Count":"2">
|
1523
|
+
#
|
1524
|
+
# ---
|
1525
|
+
#
|
1526
|
+
# Raises an exception if the argument is not a \String object or \IO object:
|
1527
|
+
# # Raises NoMethodError (undefined method `close' for :foo:Symbol)
|
1528
|
+
# CSV.parse(:foo)
|
963
1529
|
def parse(str, **options, &block)
|
964
1530
|
csv = new(str, **options)
|
965
1531
|
|
@@ -974,35 +1540,59 @@ class CSV
|
|
974
1540
|
end
|
975
1541
|
|
976
1542
|
# :call-seq:
|
977
|
-
# CSV.parse_line(string)
|
978
|
-
# CSV.parse_line(io)
|
979
|
-
# CSV.parse_line(string, **options)
|
980
|
-
# CSV.parse_line(io, **options)
|
1543
|
+
# CSV.parse_line(string) -> new_array or nil
|
1544
|
+
# CSV.parse_line(io) -> new_array or nil
|
1545
|
+
# CSV.parse_line(string, **options) -> new_array or nil
|
1546
|
+
# CSV.parse_line(io, **options) -> new_array or nil
|
1547
|
+
# CSV.parse_line(string, headers: true, **options) -> csv_row or nil
|
1548
|
+
# CSV.parse_line(io, headers: true, **options) -> csv_row or nil
|
981
1549
|
#
|
982
|
-
# Returns the
|
1550
|
+
# Returns the data created by parsing the first line of +string+ or +io+
|
983
1551
|
# using the specified +options+.
|
984
1552
|
#
|
985
|
-
# Argument +string+ should be a \String object;
|
986
|
-
#
|
1553
|
+
# - Argument +string+ should be a \String object;
|
1554
|
+
# it will be put into a new StringIO object positioned at the beginning.
|
1555
|
+
# :include: ../doc/arguments/io.rdoc
|
1556
|
+
# - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
|
987
1557
|
#
|
988
|
-
#
|
1558
|
+
# ====== Without Option +headers+
|
989
1559
|
#
|
990
|
-
#
|
1560
|
+
# Without option +headers+, returns the first row as a new \Array.
|
991
1561
|
#
|
992
|
-
#
|
993
|
-
#
|
994
|
-
#
|
1562
|
+
# These examples assume prior execution of:
|
1563
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
1564
|
+
# path = 't.csv'
|
1565
|
+
# File.write(path, string)
|
995
1566
|
#
|
996
|
-
#
|
997
|
-
#
|
998
|
-
# CSV.parse_line(File.open('t.csv')) # => ["foo", "0"]
|
1567
|
+
# Parse the first line from a \String object:
|
1568
|
+
# CSV.parse_line(string) # => ["foo", "0"]
|
999
1569
|
#
|
1000
|
-
#
|
1001
|
-
#
|
1570
|
+
# Parse the first line from a File object:
|
1571
|
+
# File.open(path) do |file|
|
1572
|
+
# CSV.parse_line(file) # => ["foo", "0"]
|
1573
|
+
# end # => ["foo", "0"]
|
1002
1574
|
#
|
1003
1575
|
# Returns +nil+ if the argument is an empty \String:
|
1004
1576
|
# CSV.parse_line('') # => nil
|
1005
1577
|
#
|
1578
|
+
# ====== With Option +headers+
|
1579
|
+
#
|
1580
|
+
# With {option +headers+}[#class-CSV-label-Option+headers],
|
1581
|
+
# returns the first row as a CSV::Row object.
|
1582
|
+
#
|
1583
|
+
# These examples assume prior execution of:
|
1584
|
+
# string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
|
1585
|
+
# path = 't.csv'
|
1586
|
+
# File.write(path, string)
|
1587
|
+
#
|
1588
|
+
# Parse the first line from a \String object:
|
1589
|
+
# CSV.parse_line(string, headers: true) # => #<CSV::Row "Name":"foo" "Count":"0">
|
1590
|
+
#
|
1591
|
+
# Parse the first line from a File object:
|
1592
|
+
# File.open(path) do |file|
|
1593
|
+
# CSV.parse_line(file, headers: true)
|
1594
|
+
# end # => #<CSV::Row "Name":"foo" "Count":"0">
|
1595
|
+
#
|
1006
1596
|
# ---
|
1007
1597
|
#
|
1008
1598
|
# Raises an exception if the argument is +nil+:
|
@@ -1014,36 +1604,52 @@ class CSV
|
|
1014
1604
|
end
|
1015
1605
|
|
1016
1606
|
#
|
1017
|
-
#
|
1018
|
-
#
|
1019
|
-
#
|
1607
|
+
# :call-seq:
|
1608
|
+
# read(source, **options) -> array_of_arrays
|
1609
|
+
# read(source, headers: true, **options) -> csv_table
|
1610
|
+
#
|
1611
|
+
# Opens the given +source+ with the given +options+ (see CSV.open),
|
1612
|
+
# reads the source (see CSV#read), and returns the result,
|
1613
|
+
# which will be either an \Array of Arrays or a CSV::Table.
|
1020
1614
|
#
|
1021
|
-
#
|
1022
|
-
#
|
1023
|
-
#
|
1024
|
-
#
|
1025
|
-
#
|
1026
|
-
# transcoded as it is read. For example,
|
1027
|
-
# <tt>encoding: "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file
|
1028
|
-
# but transcode it to UTF-8 before CSV parses it.
|
1615
|
+
# Without headers:
|
1616
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
1617
|
+
# path = 't.csv'
|
1618
|
+
# File.write(path, string)
|
1619
|
+
# CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
1029
1620
|
#
|
1621
|
+
# With headers:
|
1622
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
1623
|
+
# path = 't.csv'
|
1624
|
+
# File.write(path, string)
|
1625
|
+
# CSV.read(path, headers: true) # => #<CSV::Table mode:col_or_row row_count:4>
|
1030
1626
|
def read(path, **options)
|
1031
1627
|
open(path, **options) { |csv| csv.read }
|
1032
1628
|
end
|
1033
1629
|
|
1034
|
-
#
|
1630
|
+
# :call-seq:
|
1631
|
+
# CSV.readlines(source, **options)
|
1632
|
+
#
|
1633
|
+
# Alias for CSV.read.
|
1035
1634
|
def readlines(path, **options)
|
1036
1635
|
read(path, **options)
|
1037
1636
|
end
|
1038
1637
|
|
1638
|
+
# :call-seq:
|
1639
|
+
# CSV.table(source, **options)
|
1039
1640
|
#
|
1040
|
-
#
|
1641
|
+
# Calls CSV.read with +source+, +options+, and certain default options:
|
1642
|
+
# - +headers+: +true+
|
1643
|
+
# - +converbers+: +:numeric+
|
1644
|
+
# - +header_converters+: +:symbol+
|
1041
1645
|
#
|
1042
|
-
#
|
1043
|
-
# converters: :numeric,
|
1044
|
-
# header_converters: :symbol }.merge(options) )
|
1646
|
+
# Returns a CSV::Table object.
|
1045
1647
|
#
|
1046
|
-
#
|
1648
|
+
# Example:
|
1649
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
1650
|
+
# path = 't.csv'
|
1651
|
+
# File.write(path, string)
|
1652
|
+
# CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:4>
|
1047
1653
|
def table(path, **options)
|
1048
1654
|
default_options = {
|
1049
1655
|
headers: true,
|
@@ -1064,23 +1670,17 @@ class CSV
|
|
1064
1670
|
# Returns the new \CSV object created using +string+ or +io+
|
1065
1671
|
# and the specified +options+.
|
1066
1672
|
#
|
1067
|
-
# Argument +string+ should be a \String object;
|
1068
|
-
#
|
1673
|
+
# - Argument +string+ should be a \String object;
|
1674
|
+
# it will be put into a new StringIO object positioned at the beginning.
|
1675
|
+
# :include: ../doc/arguments/io.rdoc
|
1676
|
+
# - Argument +options+: See:
|
1677
|
+
# * {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
|
1678
|
+
# * {Options for Generating}[#class-CSV-label-Options+for+Generating]
|
1679
|
+
# For performance reasons, the options cannot be overridden
|
1680
|
+
# in a \CSV object, so those specified here will endure.
|
1069
1681
|
#
|
1070
|
-
#
|
1071
|
-
#
|
1072
|
-
# To position at the end, for appending, use method CSV.generate.
|
1073
|
-
# For any other positioning, pass a preset StringIO object instead.
|
1074
|
-
#
|
1075
|
-
# In addition to the \CSV instance methods, several \IO
|
1076
|
-
# methods are delegated. See CSV::open for a complete list.
|
1077
|
-
#
|
1078
|
-
# For +options+, see:
|
1079
|
-
# * {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
|
1080
|
-
# * {Options for Generating}[#class-CSV-label-Options+for+Generating]
|
1081
|
-
#
|
1082
|
-
# For performance reasons, the options cannot be overridden
|
1083
|
-
# in a \CSV object, so the options specified here will endure.
|
1682
|
+
# In addition to the \CSV instance methods, several \IO methods are delegated.
|
1683
|
+
# See {Delegated Methods}[#class-CSV-label-Delegated+Methods].
|
1084
1684
|
#
|
1085
1685
|
# ---
|
1086
1686
|
#
|
@@ -1182,51 +1782,67 @@ class CSV
|
|
1182
1782
|
writer if @writer_options[:write_headers]
|
1183
1783
|
end
|
1184
1784
|
|
1785
|
+
# :call-seq:
|
1786
|
+
# csv.col_sep -> string
|
1185
1787
|
#
|
1186
|
-
#
|
1187
|
-
#
|
1188
|
-
#
|
1788
|
+
# Returns the encoded column separator; used for parsing and writing;
|
1789
|
+
# see {Option +col_sep+}[#class-CSV-label-Option+col_sep]:
|
1790
|
+
# CSV.new('').col_sep # => ","
|
1189
1791
|
def col_sep
|
1190
1792
|
parser.column_separator
|
1191
1793
|
end
|
1192
1794
|
|
1795
|
+
# :call-seq:
|
1796
|
+
# csv.row_sep -> string
|
1193
1797
|
#
|
1194
|
-
#
|
1195
|
-
#
|
1196
|
-
#
|
1798
|
+
# Returns the encoded row separator; used for parsing and writing;
|
1799
|
+
# see {Option +row_sep+}[#class-CSV-label-Option+row_sep]:
|
1800
|
+
# CSV.new('').row_sep # => "\n"
|
1197
1801
|
def row_sep
|
1198
1802
|
parser.row_separator
|
1199
1803
|
end
|
1200
1804
|
|
1805
|
+
# :call-seq:
|
1806
|
+
# csv.quote_char -> character
|
1201
1807
|
#
|
1202
|
-
#
|
1203
|
-
#
|
1204
|
-
#
|
1808
|
+
# Returns the encoded quote character; used for parsing and writing;
|
1809
|
+
# see {Option +quote_char+}[#class-CSV-label-Option+quote_char]:
|
1810
|
+
# CSV.new('').quote_char # => "\""
|
1205
1811
|
def quote_char
|
1206
1812
|
parser.quote_character
|
1207
1813
|
end
|
1208
1814
|
|
1815
|
+
# :call-seq:
|
1816
|
+
# csv.field_size_limit -> integer or nil
|
1209
1817
|
#
|
1210
|
-
#
|
1211
|
-
#
|
1212
|
-
#
|
1818
|
+
# Returns the limit for field size; used for parsing;
|
1819
|
+
# see {Option +field_size_limit+}[#class-CSV-label-Option+field_size_limit]:
|
1820
|
+
# CSV.new('').field_size_limit # => nil
|
1213
1821
|
def field_size_limit
|
1214
1822
|
parser.field_size_limit
|
1215
1823
|
end
|
1216
1824
|
|
1825
|
+
# :call-seq:
|
1826
|
+
# csv.skip_lines -> regexp or nil
|
1217
1827
|
#
|
1218
|
-
#
|
1219
|
-
#
|
1220
|
-
#
|
1828
|
+
# Returns the \Regexp used to identify comment lines; used for parsing;
|
1829
|
+
# see {Option +skip_lines+}[#class-CSV-label-Option+skip_lines]:
|
1830
|
+
# CSV.new('').skip_lines # => nil
|
1221
1831
|
def skip_lines
|
1222
1832
|
parser.skip_lines
|
1223
1833
|
end
|
1224
1834
|
|
1225
|
-
#
|
1226
|
-
#
|
1227
|
-
#
|
1228
|
-
#
|
1229
|
-
#
|
1835
|
+
# :call-seq:
|
1836
|
+
# csv.converters -> array
|
1837
|
+
#
|
1838
|
+
# Returns an \Array containing field converters;
|
1839
|
+
# see {Field Converters}[#class-CSV-label-Field+Converters]:
|
1840
|
+
# csv = CSV.new('')
|
1841
|
+
# csv.converters # => []
|
1842
|
+
# csv.convert(:integer)
|
1843
|
+
# csv.converters # => [:integer]
|
1844
|
+
# csv.convert(proc {|x| x.to_s })
|
1845
|
+
# csv.converters
|
1230
1846
|
def converters
|
1231
1847
|
parser_fields_converter.map do |converter|
|
1232
1848
|
name = Converters.rassoc(converter)
|
@@ -1234,19 +1850,23 @@ class CSV
|
|
1234
1850
|
end
|
1235
1851
|
end
|
1236
1852
|
|
1853
|
+
# :call-seq:
|
1854
|
+
# csv.unconverted_fields? -> object
|
1237
1855
|
#
|
1238
|
-
# Returns
|
1239
|
-
#
|
1240
|
-
#
|
1856
|
+
# Returns the value that determines whether unconverted fields are to be
|
1857
|
+
# available; used for parsing;
|
1858
|
+
# see {Option +unconverted_fields+}[#class-CSV-label-Option+unconverted_fields]:
|
1859
|
+
# CSV.new('').unconverted_fields? # => nil
|
1241
1860
|
def unconverted_fields?
|
1242
1861
|
parser.unconverted_fields?
|
1243
1862
|
end
|
1244
1863
|
|
1864
|
+
# :call-seq:
|
1865
|
+
# csv.headers -> object
|
1245
1866
|
#
|
1246
|
-
# Returns
|
1247
|
-
#
|
1248
|
-
#
|
1249
|
-
#
|
1867
|
+
# Returns the value that determines whether headers are used; used for parsing;
|
1868
|
+
# see {Option +headers+}[#class-CSV-label-Option+headers]:
|
1869
|
+
# CSV.new('').headers # => nil
|
1250
1870
|
def headers
|
1251
1871
|
if @writer
|
1252
1872
|
@writer.headers
|
@@ -1258,27 +1878,33 @@ class CSV
|
|
1258
1878
|
raw_headers
|
1259
1879
|
end
|
1260
1880
|
end
|
1881
|
+
|
1882
|
+
# :call-seq:
|
1883
|
+
# csv.return_headers? -> true or false
|
1261
1884
|
#
|
1262
|
-
# Returns
|
1263
|
-
#
|
1264
|
-
#
|
1885
|
+
# Returns the value that determines whether headers are to be returned; used for parsing;
|
1886
|
+
# see {Option +return_headers+}[#class-CSV-label-Option+return_headers]:
|
1887
|
+
# CSV.new('').return_headers? # => false
|
1265
1888
|
def return_headers?
|
1266
1889
|
parser.return_headers?
|
1267
1890
|
end
|
1268
1891
|
|
1892
|
+
# :call-seq:
|
1893
|
+
# csv.write_headers? -> true or false
|
1269
1894
|
#
|
1270
|
-
# Returns
|
1271
|
-
#
|
1272
|
-
#
|
1895
|
+
# Returns the value that determines whether headers are to be written; used for generating;
|
1896
|
+
# see {Option +write_headers+}[#class-CSV-label-Option+write_headers]:
|
1897
|
+
# CSV.new('').write_headers? # => nil
|
1273
1898
|
def write_headers?
|
1274
1899
|
@writer_options[:write_headers]
|
1275
1900
|
end
|
1276
1901
|
|
1902
|
+
# :call-seq:
|
1903
|
+
# csv.header_converters -> array
|
1277
1904
|
#
|
1278
|
-
# Returns
|
1279
|
-
#
|
1280
|
-
#
|
1281
|
-
#
|
1905
|
+
# Returns an \Array containing header converters; used for parsing;
|
1906
|
+
# see {Header Converters}[#class-CSV-label-Header+Converters]:
|
1907
|
+
# CSV.new('').header_converters # => []
|
1282
1908
|
def header_converters
|
1283
1909
|
header_fields_converter.map do |converter|
|
1284
1910
|
name = HeaderConverters.rassoc(converter)
|
@@ -1286,34 +1912,74 @@ class CSV
|
|
1286
1912
|
end
|
1287
1913
|
end
|
1288
1914
|
|
1915
|
+
# :call-seq:
|
1916
|
+
# csv.skip_blanks? -> true or false
|
1289
1917
|
#
|
1290
|
-
# Returns
|
1291
|
-
#
|
1292
|
-
#
|
1918
|
+
# Returns the value that determines whether blank lines are to be ignored; used for parsing;
|
1919
|
+
# see {Option +skip_blanks+}[#class-CSV-label-Option+skip_blanks]:
|
1920
|
+
# CSV.new('').skip_blanks? # => false
|
1293
1921
|
def skip_blanks?
|
1294
1922
|
parser.skip_blanks?
|
1295
1923
|
end
|
1296
1924
|
|
1297
|
-
#
|
1925
|
+
# :call-seq:
|
1926
|
+
# csv.force_quotes? -> true or false
|
1927
|
+
#
|
1928
|
+
# Returns the value that determines whether all output fields are to be quoted;
|
1929
|
+
# used for generating;
|
1930
|
+
# see {Option +force_quotes+}[#class-CSV-label-Option+force_quotes]:
|
1931
|
+
# CSV.new('').force_quotes? # => false
|
1298
1932
|
def force_quotes?
|
1299
1933
|
@writer_options[:force_quotes]
|
1300
1934
|
end
|
1301
1935
|
|
1302
|
-
#
|
1936
|
+
# :call-seq:
|
1937
|
+
# csv.liberal_parsing? -> true or false
|
1938
|
+
#
|
1939
|
+
# Returns the value that determines whether illegal input is to be handled; used for parsing;
|
1940
|
+
# see {Option +liberal_parsing+}[#class-CSV-label-Option+liberal_parsing]:
|
1941
|
+
# CSV.new('').liberal_parsing? # => false
|
1303
1942
|
def liberal_parsing?
|
1304
1943
|
parser.liberal_parsing?
|
1305
1944
|
end
|
1306
1945
|
|
1946
|
+
# :call-seq:
|
1947
|
+
# csv.encoding -> endcoding
|
1307
1948
|
#
|
1308
|
-
#
|
1309
|
-
#
|
1310
|
-
#
|
1949
|
+
# Returns the encoding used for parsing and generating;
|
1950
|
+
# see {Character Encodings (M17n or Multilingualization)}[#class-CSV-label-Character+Encodings+-28M17n+or+Multilingualization-29]:
|
1951
|
+
# CSV.new('').encoding # => #<Encoding:UTF-8>
|
1311
1952
|
attr_reader :encoding
|
1312
1953
|
|
1313
|
-
#
|
1314
|
-
#
|
1315
|
-
#
|
1316
|
-
#
|
1954
|
+
# :call-seq:
|
1955
|
+
# csv.line_no -> integer
|
1956
|
+
#
|
1957
|
+
# Returns the count of the rows parsed or generated.
|
1958
|
+
#
|
1959
|
+
# Parsing:
|
1960
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
1961
|
+
# path = 't.csv'
|
1962
|
+
# File.write(path, string)
|
1963
|
+
# CSV.open(path) do |csv|
|
1964
|
+
# csv.each do |row|
|
1965
|
+
# p [csv.lineno, row]
|
1966
|
+
# end
|
1967
|
+
# end
|
1968
|
+
# Output:
|
1969
|
+
# [1, ["foo", "0"]]
|
1970
|
+
# [2, ["bar", "1"]]
|
1971
|
+
# [3, ["baz", "2"]]
|
1972
|
+
#
|
1973
|
+
# Generating:
|
1974
|
+
# CSV.generate do |csv|
|
1975
|
+
# p csv.lineno; csv << ['foo', 0]
|
1976
|
+
# p csv.lineno; csv << ['bar', 1]
|
1977
|
+
# p csv.lineno; csv << ['baz', 2]
|
1978
|
+
# end
|
1979
|
+
# Output:
|
1980
|
+
# 0
|
1981
|
+
# 1
|
1982
|
+
# 2
|
1317
1983
|
def lineno
|
1318
1984
|
if @writer
|
1319
1985
|
@writer.lineno
|
@@ -1322,9 +1988,22 @@ class CSV
|
|
1322
1988
|
end
|
1323
1989
|
end
|
1324
1990
|
|
1325
|
-
#
|
1326
|
-
#
|
1327
|
-
#
|
1991
|
+
# :call-seq:
|
1992
|
+
# csv.line -> array
|
1993
|
+
#
|
1994
|
+
# Returns the line most recently read:
|
1995
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
1996
|
+
# path = 't.csv'
|
1997
|
+
# File.write(path, string)
|
1998
|
+
# CSV.open(path) do |csv|
|
1999
|
+
# csv.each do |row|
|
2000
|
+
# p [csv.lineno, csv.line]
|
2001
|
+
# end
|
2002
|
+
# end
|
2003
|
+
# Output:
|
2004
|
+
# [1, "foo,0\n"]
|
2005
|
+
# [2, "bar,1\n"]
|
2006
|
+
# [3, "baz,2\n"]
|
1328
2007
|
def line
|
1329
2008
|
parser.line
|
1330
2009
|
end
|
@@ -1400,13 +2079,56 @@ class CSV
|
|
1400
2079
|
|
1401
2080
|
### End Delegation ###
|
1402
2081
|
|
2082
|
+
# :call-seq:
|
2083
|
+
# csv.<< row
|
1403
2084
|
#
|
1404
|
-
#
|
1405
|
-
# CSV::Row) is converted to CSV and appended to the data source. When a
|
1406
|
-
# CSV::Row is passed, only the row's fields() are appended to the output.
|
2085
|
+
# Appends a row to +self+.
|
1407
2086
|
#
|
1408
|
-
#
|
2087
|
+
# - Argument +row+ must be an \Array object or a CSV::Row object.
|
2088
|
+
# - The output stream must be open for writing.
|
2089
|
+
#
|
2090
|
+
# ---
|
1409
2091
|
#
|
2092
|
+
# Append Arrays:
|
2093
|
+
# CSV.generate do |csv|
|
2094
|
+
# csv << ['foo', 0]
|
2095
|
+
# csv << ['bar', 1]
|
2096
|
+
# csv << ['baz', 2]
|
2097
|
+
# end # => "foo,0\nbar,1\nbaz,2\n"
|
2098
|
+
#
|
2099
|
+
# Append CSV::Rows:
|
2100
|
+
# headers = []
|
2101
|
+
# CSV.generate do |csv|
|
2102
|
+
# csv << CSV::Row.new(headers, ['foo', 0])
|
2103
|
+
# csv << CSV::Row.new(headers, ['bar', 1])
|
2104
|
+
# csv << CSV::Row.new(headers, ['baz', 2])
|
2105
|
+
# end # => "foo,0\nbar,1\nbaz,2\n"
|
2106
|
+
#
|
2107
|
+
# Headers in CSV::Row objects are not appended:
|
2108
|
+
# headers = ['Name', 'Count']
|
2109
|
+
# CSV.generate do |csv|
|
2110
|
+
# csv << CSV::Row.new(headers, ['foo', 0])
|
2111
|
+
# csv << CSV::Row.new(headers, ['bar', 1])
|
2112
|
+
# csv << CSV::Row.new(headers, ['baz', 2])
|
2113
|
+
# end # => "foo,0\nbar,1\nbaz,2\n"
|
2114
|
+
#
|
2115
|
+
# ---
|
2116
|
+
#
|
2117
|
+
# Raises an exception if +row+ is not an \Array or \CSV::Row:
|
2118
|
+
# CSV.generate do |csv|
|
2119
|
+
# # Raises NoMethodError (undefined method `collect' for :foo:Symbol)
|
2120
|
+
# csv << :foo
|
2121
|
+
# end
|
2122
|
+
#
|
2123
|
+
# Raises an exception if the output stream is not open for writing:
|
2124
|
+
# path = 't.csv'
|
2125
|
+
# File.write(path, '')
|
2126
|
+
# File.open(path) do |file|
|
2127
|
+
# CSV.open(file) do |csv|
|
2128
|
+
# # Raises IOError (not opened for writing)
|
2129
|
+
# csv << ['foo', 0]
|
2130
|
+
# end
|
2131
|
+
# end
|
1410
2132
|
def <<(row)
|
1411
2133
|
writer << row
|
1412
2134
|
self
|
@@ -1414,36 +2136,136 @@ class CSV
|
|
1414
2136
|
alias_method :add_row, :<<
|
1415
2137
|
alias_method :puts, :<<
|
1416
2138
|
|
1417
|
-
#
|
1418
2139
|
# :call-seq:
|
1419
|
-
# convert(
|
1420
|
-
# convert {
|
1421
|
-
# convert { |field, field_info| ... }
|
2140
|
+
# convert(converter_name) -> array_of_procs
|
2141
|
+
# convert {|field, field_info| ... } -> array_of_procs
|
1422
2142
|
#
|
1423
|
-
#
|
1424
|
-
# block
|
2143
|
+
# - With no block, installs a field converter (a \Proc).
|
2144
|
+
# - With a block, defines and installs a custom field converter.
|
2145
|
+
# - Returns the \Array of installed field converters.
|
2146
|
+
#
|
2147
|
+
# - Argument +converter_name+, if given, should be the name
|
2148
|
+
# of an existing field converter.
|
2149
|
+
#
|
2150
|
+
# See {Field Converters}[#class-CSV-label-Field+Converters].
|
2151
|
+
# ---
|
1425
2152
|
#
|
1426
|
-
#
|
1427
|
-
#
|
1428
|
-
#
|
1429
|
-
#
|
1430
|
-
#
|
2153
|
+
# With no block, installs a field converter:
|
2154
|
+
# csv = CSV.new('')
|
2155
|
+
# csv.convert(:integer)
|
2156
|
+
# csv.convert(:float)
|
2157
|
+
# csv.convert(:date)
|
2158
|
+
# csv.converters # => [:integer, :float, :date]
|
1431
2159
|
#
|
2160
|
+
# ---
|
2161
|
+
#
|
2162
|
+
# The block, if given, is called for each field:
|
2163
|
+
# - Argument +field+ is the field value.
|
2164
|
+
# - Argument +field_info+ is a CSV::FieldInfo object
|
2165
|
+
# containing details about the field.
|
2166
|
+
#
|
2167
|
+
# The examples here assume the prior execution of:
|
2168
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2169
|
+
# path = 't.csv'
|
2170
|
+
# File.write(path, string)
|
2171
|
+
#
|
2172
|
+
# Example giving a block:
|
2173
|
+
# csv = CSV.open(path)
|
2174
|
+
# csv.convert {|field, field_info| p [field, field_info]; field.upcase }
|
2175
|
+
# csv.read # => [["FOO", "0"], ["BAR", "1"], ["BAZ", "2"]]
|
2176
|
+
#
|
2177
|
+
# Output:
|
2178
|
+
# ["foo", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
|
2179
|
+
# ["0", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
|
2180
|
+
# ["bar", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
|
2181
|
+
# ["1", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
|
2182
|
+
# ["baz", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
|
2183
|
+
# ["2", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
|
2184
|
+
#
|
2185
|
+
# The block need not return a \String object:
|
2186
|
+
# csv = CSV.open(path)
|
2187
|
+
# csv.convert {|field, field_info| field.to_sym }
|
2188
|
+
# csv.read # => [[:foo, :"0"], [:bar, :"1"], [:baz, :"2"]]
|
2189
|
+
#
|
2190
|
+
# If +converter_name+ is given, the block is not called:
|
2191
|
+
# csv = CSV.open(path)
|
2192
|
+
# csv.convert(:integer) {|field, field_info| fail 'Cannot happen' }
|
2193
|
+
# csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
|
2194
|
+
#
|
2195
|
+
# ---
|
2196
|
+
#
|
2197
|
+
# Raises a parse-time exception if +converter_name+ is not the name of a built-in
|
2198
|
+
# field converter:
|
2199
|
+
# csv = CSV.open(path)
|
2200
|
+
# csv.convert(:nosuch) => [nil]
|
2201
|
+
# # Raises NoMethodError (undefined method `arity' for nil:NilClass)
|
2202
|
+
# csv.read
|
1432
2203
|
def convert(name = nil, &converter)
|
1433
2204
|
parser_fields_converter.add_converter(name, &converter)
|
1434
2205
|
end
|
1435
2206
|
|
1436
|
-
#
|
1437
2207
|
# :call-seq:
|
1438
|
-
# header_convert(
|
1439
|
-
# header_convert { |
|
1440
|
-
#
|
2208
|
+
# header_convert(converter_name) -> array_of_procs
|
2209
|
+
# header_convert {|header, field_info| ... } -> array_of_procs
|
2210
|
+
#
|
2211
|
+
# - With no block, installs a header converter (a \Proc).
|
2212
|
+
# - With a block, defines and installs a custom header converter.
|
2213
|
+
# - Returns the \Array of installed header converters.
|
1441
2214
|
#
|
1442
|
-
#
|
2215
|
+
# - Argument +converter_name+, if given, should be the name
|
2216
|
+
# of an existing header converter.
|
1443
2217
|
#
|
1444
|
-
#
|
1445
|
-
#
|
2218
|
+
# See {Header Converters}[#class-CSV-label-Header+Converters].
|
2219
|
+
# ---
|
2220
|
+
#
|
2221
|
+
# With no block, installs a header converter:
|
2222
|
+
# csv = CSV.new('')
|
2223
|
+
# csv.header_convert(:symbol)
|
2224
|
+
# csv.header_convert(:downcase)
|
2225
|
+
# csv.header_converters # => [:symbol, :downcase]
|
2226
|
+
#
|
2227
|
+
# ---
|
1446
2228
|
#
|
2229
|
+
# The block, if given, is called for each header:
|
2230
|
+
# - Argument +header+ is the header value.
|
2231
|
+
# - Argument +field_info+ is a CSV::FieldInfo object
|
2232
|
+
# containing details about the header.
|
2233
|
+
#
|
2234
|
+
# The examples here assume the prior execution of:
|
2235
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
2236
|
+
# path = 't.csv'
|
2237
|
+
# File.write(path, string)
|
2238
|
+
#
|
2239
|
+
# Example giving a block:
|
2240
|
+
# csv = CSV.open(path, headers: true)
|
2241
|
+
# csv.header_convert {|header, field_info| p [header, field_info]; header.upcase }
|
2242
|
+
# table = csv.read
|
2243
|
+
# table # => #<CSV::Table mode:col_or_row row_count:4>
|
2244
|
+
# table.headers # => ["NAME", "VALUE"]
|
2245
|
+
#
|
2246
|
+
# Output:
|
2247
|
+
# ["Name", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
|
2248
|
+
# ["Value", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
|
2249
|
+
|
2250
|
+
# The block need not return a \String object:
|
2251
|
+
# csv = CSV.open(path, headers: true)
|
2252
|
+
# csv.header_convert {|header, field_info| header.to_sym }
|
2253
|
+
# table = csv.read
|
2254
|
+
# table.headers # => [:Name, :Value]
|
2255
|
+
#
|
2256
|
+
# If +converter_name+ is given, the block is not called:
|
2257
|
+
# csv = CSV.open(path, headers: true)
|
2258
|
+
# csv.header_convert(:downcase) {|header, field_info| fail 'Cannot happen' }
|
2259
|
+
# table = csv.read
|
2260
|
+
# table.headers # => ["name", "value"]
|
2261
|
+
# ---
|
2262
|
+
#
|
2263
|
+
# Raises a parse-time exception if +converter_name+ is not the name of a built-in
|
2264
|
+
# field converter:
|
2265
|
+
# csv = CSV.open(path, headers: true)
|
2266
|
+
# csv.header_convert(:nosuch)
|
2267
|
+
# # Raises NoMethodError (undefined method `arity' for nil:NilClass)
|
2268
|
+
# csv.read
|
1447
2269
|
def header_convert(name = nil, &converter)
|
1448
2270
|
header_fields_converter.add_converter(name, &converter)
|
1449
2271
|
end
|
@@ -1461,11 +2283,28 @@ class CSV
|
|
1461
2283
|
parser_enumerator.each(&block)
|
1462
2284
|
end
|
1463
2285
|
|
2286
|
+
# :call-seq:
|
2287
|
+
# read
|
1464
2288
|
#
|
1465
|
-
#
|
2289
|
+
# Forms the remaining rows from +self+ into:
|
2290
|
+
# - A CSV::Table object, if headers are in use.
|
2291
|
+
# - An Array of Arrays, otherwise.
|
1466
2292
|
#
|
1467
2293
|
# The data source must be open for reading.
|
1468
2294
|
#
|
2295
|
+
# Without headers:
|
2296
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2297
|
+
# path = 't.csv'
|
2298
|
+
# File.write(path, string)
|
2299
|
+
# csv = CSV.open(path)
|
2300
|
+
# csv.read # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
2301
|
+
#
|
2302
|
+
# With headers:
|
2303
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
2304
|
+
# path = 't.csv'
|
2305
|
+
# File.write(path, string)
|
2306
|
+
# csv = CSV.open(path, headers: true)
|
2307
|
+
# csv.read # => #<CSV::Table mode:col_or_row row_count:4>
|
1469
2308
|
def read
|
1470
2309
|
rows = to_a
|
1471
2310
|
if parser.use_headers?
|