csv 3.1.2 → 3.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/NEWS.md +131 -0
- data/doc/csv/arguments/io.rdoc +5 -0
- data/doc/csv/options/common/col_sep.rdoc +63 -0
- data/doc/csv/options/common/quote_char.rdoc +42 -0
- data/doc/csv/options/common/row_sep.rdoc +100 -0
- data/doc/csv/options/generating/force_quotes.rdoc +17 -0
- data/doc/csv/options/generating/quote_empty.rdoc +12 -0
- data/doc/csv/options/generating/write_converters.rdoc +33 -0
- data/doc/csv/options/generating/write_empty_value.rdoc +15 -0
- data/doc/csv/options/generating/write_headers.rdoc +29 -0
- data/doc/csv/options/generating/write_nil_value.rdoc +14 -0
- data/doc/csv/options/parsing/converters.rdoc +46 -0
- data/doc/csv/options/parsing/empty_value.rdoc +13 -0
- data/doc/csv/options/parsing/field_size_limit.rdoc +39 -0
- data/doc/csv/options/parsing/header_converters.rdoc +43 -0
- data/doc/csv/options/parsing/headers.rdoc +63 -0
- data/doc/csv/options/parsing/liberal_parsing.rdoc +19 -0
- data/doc/csv/options/parsing/nil_value.rdoc +12 -0
- data/doc/csv/options/parsing/return_headers.rdoc +22 -0
- data/doc/csv/options/parsing/skip_blanks.rdoc +31 -0
- data/doc/csv/options/parsing/skip_lines.rdoc +37 -0
- data/doc/csv/options/parsing/strip.rdoc +15 -0
- data/doc/csv/options/parsing/unconverted_fields.rdoc +27 -0
- data/lib/csv.rb +1686 -570
- data/lib/csv/fields_converter.rb +1 -1
- data/lib/csv/parser.rb +10 -5
- data/lib/csv/row.rb +1 -1
- data/lib/csv/table.rb +285 -64
- data/lib/csv/version.rb +1 -1
- data/lib/csv/writer.rb +45 -4
- metadata +33 -7
@@ -0,0 +1,29 @@
|
|
1
|
+
====== Option +write_headers+
|
2
|
+
|
3
|
+
Specifies the boolean that determines whether a header row is included in the output;
|
4
|
+
ignored if there are no headers.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:write_headers) # => nil
|
8
|
+
|
9
|
+
Without +write_headers+:
|
10
|
+
file_path = 't.csv'
|
11
|
+
CSV.open(file_path,'w',
|
12
|
+
:headers => ['Name','Value']
|
13
|
+
) do |csv|
|
14
|
+
csv << ['foo', '0']
|
15
|
+
end
|
16
|
+
CSV.open(file_path) do |csv|
|
17
|
+
csv.shift
|
18
|
+
end # => ["foo", "0"]
|
19
|
+
|
20
|
+
With +write_headers+":
|
21
|
+
CSV.open(file_path,'w',
|
22
|
+
:write_headers=> true,
|
23
|
+
:headers => ['Name','Value']
|
24
|
+
) do |csv|
|
25
|
+
csv << ['foo', '0']
|
26
|
+
end
|
27
|
+
CSV.open(file_path) do |csv|
|
28
|
+
csv.shift
|
29
|
+
end # => ["Name", "Value"]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
====== Option +write_nil_value+
|
2
|
+
|
3
|
+
Specifies the object that is to be substituted for each +nil+-valued field.
|
4
|
+
|
5
|
+
Default value:
|
6
|
+
CSV::DEFAULT_OPTIONS.fetch(:write_nil_value) # => nil
|
7
|
+
|
8
|
+
Without the option:
|
9
|
+
str = CSV.generate_line(['a', nil, 'c', nil])
|
10
|
+
str # => "a,,c,\n"
|
11
|
+
|
12
|
+
With the option:
|
13
|
+
str = CSV.generate_line(['a', nil, 'c', nil], write_nil_value: "x")
|
14
|
+
str # => "a,x,c,x\n"
|
@@ -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)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
====== Option +empty_value+
|
2
|
+
|
3
|
+
Specifies the object that is to be substituted
|
4
|
+
for each field that has an empty \String.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:empty_value) # => "" (empty string)
|
8
|
+
|
9
|
+
With the default, <tt>""</tt>:
|
10
|
+
CSV.parse_line('a,"",b,"",c') # => ["a", "", "b", "", "c"]
|
11
|
+
|
12
|
+
With a different object:
|
13
|
+
CSV.parse_line('a,"",b,"",c', empty_value: 'x') # => ["a", "x", "b", "x", "c"]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
====== Option +field_size_limit+
|
2
|
+
|
3
|
+
Specifies the \Integer field size limit.
|
4
|
+
|
5
|
+
Default value:
|
6
|
+
CSV::DEFAULT_OPTIONS.fetch(:field_size_limit) # => nil
|
7
|
+
|
8
|
+
This is a maximum size CSV will read ahead looking for the closing quote for a field.
|
9
|
+
(In truth, it reads to the first line ending beyond this size.)
|
10
|
+
If a quote cannot be found within the limit CSV will raise a MalformedCSVError,
|
11
|
+
assuming the data is faulty.
|
12
|
+
You can use this limit to prevent what are effectively DoS attacks on the parser.
|
13
|
+
However, this limit can cause a legitimate parse to fail;
|
14
|
+
therefore the default value is +nil+ (no limit).
|
15
|
+
|
16
|
+
For the examples in this section:
|
17
|
+
str = <<~EOT
|
18
|
+
"a","b"
|
19
|
+
"
|
20
|
+
2345
|
21
|
+
",""
|
22
|
+
EOT
|
23
|
+
str # => "\"a\",\"b\"\n\"\n2345\n\",\"\"\n"
|
24
|
+
|
25
|
+
Using the default +nil+:
|
26
|
+
ary = CSV.parse(str)
|
27
|
+
ary # => [["a", "b"], ["\n2345\n", ""]]
|
28
|
+
|
29
|
+
Using <tt>50</tt>:
|
30
|
+
field_size_limit = 50
|
31
|
+
ary = CSV.parse(str, field_size_limit: field_size_limit)
|
32
|
+
ary # => [["a", "b"], ["\n2345\n", ""]]
|
33
|
+
|
34
|
+
---
|
35
|
+
|
36
|
+
Raises an exception if a field is too long:
|
37
|
+
big_str = "123456789\n" * 1024
|
38
|
+
# Raises CSV::MalformedCSVError (Field size exceeded in line 1.)
|
39
|
+
CSV.parse('valid,fields,"' + big_str + '"', field_size_limit: 2048)
|
@@ -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
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
====== Option +headers+
|
2
|
+
|
3
|
+
Specifies a boolean, \Symbol, \Array, or \String to be used
|
4
|
+
to define column headers.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:headers) # => false
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
Without +headers+:
|
12
|
+
str = <<-EOT
|
13
|
+
Name,Count
|
14
|
+
foo,0
|
15
|
+
bar,1
|
16
|
+
bax,2
|
17
|
+
EOT
|
18
|
+
csv = CSV.new(str)
|
19
|
+
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
20
|
+
csv.headers # => nil
|
21
|
+
csv.shift # => ["Name", "Count"]
|
22
|
+
|
23
|
+
---
|
24
|
+
|
25
|
+
If set to +true+ or the \Symbol +:first_row+,
|
26
|
+
the first row of the data is treated as a row of headers:
|
27
|
+
str = <<-EOT
|
28
|
+
Name,Count
|
29
|
+
foo,0
|
30
|
+
bar,1
|
31
|
+
bax,2
|
32
|
+
EOT
|
33
|
+
csv = CSV.new(str, headers: true)
|
34
|
+
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:2 col_sep:"," row_sep:"\n" quote_char:"\"" headers:["Name", "Count"]>
|
35
|
+
csv.headers # => ["Name", "Count"]
|
36
|
+
csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
|
37
|
+
|
38
|
+
---
|
39
|
+
|
40
|
+
If set to an \Array, the \Array elements are treated as headers:
|
41
|
+
str = <<-EOT
|
42
|
+
foo,0
|
43
|
+
bar,1
|
44
|
+
bax,2
|
45
|
+
EOT
|
46
|
+
csv = CSV.new(str, headers: ['Name', 'Count'])
|
47
|
+
csv
|
48
|
+
csv.headers # => ["Name", "Count"]
|
49
|
+
csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
|
50
|
+
|
51
|
+
---
|
52
|
+
|
53
|
+
If set to a \String +str+, method <tt>CSV::parse_line(str, options)</tt> is called
|
54
|
+
with the current +options+, and the returned \Array is treated as headers:
|
55
|
+
str = <<-EOT
|
56
|
+
foo,0
|
57
|
+
bar,1
|
58
|
+
bax,2
|
59
|
+
EOT
|
60
|
+
csv = CSV.new(str, headers: 'Name,Count')
|
61
|
+
csv
|
62
|
+
csv.headers # => ["Name", "Count"]
|
63
|
+
csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
|
@@ -0,0 +1,19 @@
|
|
1
|
+
====== Option +liberal_parsing+
|
2
|
+
|
3
|
+
Specifies the boolean value that determines whether
|
4
|
+
CSV will attempt to parse input not conformant with RFC 4180,
|
5
|
+
such as double quotes in unquoted fields.
|
6
|
+
|
7
|
+
Default value:
|
8
|
+
CSV::DEFAULT_OPTIONS.fetch(:liberal_parsing) # => false
|
9
|
+
|
10
|
+
For examples in this section:
|
11
|
+
str = 'is,this "three, or four",fields'
|
12
|
+
|
13
|
+
Without +liberal_parsing+:
|
14
|
+
# Raises CSV::MalformedCSVError (Illegal quoting in str 1.)
|
15
|
+
CSV.parse_line(str)
|
16
|
+
|
17
|
+
With +liberal_parsing+:
|
18
|
+
ary = CSV.parse_line(str, liberal_parsing: true)
|
19
|
+
ary # => ["is", "this \"three", " or four\"", "fields"]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
====== Option +nil_value+
|
2
|
+
|
3
|
+
Specifies the object that is to be substituted for each null (no-text) field.
|
4
|
+
|
5
|
+
Default value:
|
6
|
+
CSV::DEFAULT_OPTIONS.fetch(:nil_value) # => nil
|
7
|
+
|
8
|
+
With the default, +nil+:
|
9
|
+
CSV.parse_line('a,,b,,c') # => ["a", nil, "b", nil, "c"]
|
10
|
+
|
11
|
+
With a different object:
|
12
|
+
CSV.parse_line('a,,b,,c', nil_value: 0) # => ["a", 0, "b", 0, "c"]
|
@@ -0,0 +1,22 @@
|
|
1
|
+
====== Option +return_headers+
|
2
|
+
|
3
|
+
Specifies the boolean that determines whether method #shift
|
4
|
+
returns or ignores the header row.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:return_headers) # => false
|
8
|
+
|
9
|
+
Examples:
|
10
|
+
str = <<-EOT
|
11
|
+
Name,Count
|
12
|
+
foo,0
|
13
|
+
bar,1
|
14
|
+
bax,2
|
15
|
+
EOT
|
16
|
+
# Without return_headers first row is str.
|
17
|
+
csv = CSV.new(str, headers: true)
|
18
|
+
csv.shift # => #<CSV::Row "Name":"foo" "Count":"0">
|
19
|
+
# With return_headers first row is headers.
|
20
|
+
csv = CSV.new(str, headers: true, return_headers: true)
|
21
|
+
csv.shift # => #<CSV::Row "Name":"Name" "Count":"Count">
|
22
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
====== Option +skip_blanks+
|
2
|
+
|
3
|
+
Specifies a boolean that determines whether blank lines in the input will be ignored;
|
4
|
+
a line that contains a column separator is not considered to be blank.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:skip_blanks) # => false
|
8
|
+
|
9
|
+
See also option {skiplines}[#class-CSV-label-Option+skip_lines].
|
10
|
+
|
11
|
+
For examples in this section:
|
12
|
+
str = <<-EOT
|
13
|
+
foo,0
|
14
|
+
|
15
|
+
bar,1
|
16
|
+
baz,2
|
17
|
+
|
18
|
+
,
|
19
|
+
EOT
|
20
|
+
|
21
|
+
Using the default, +false+:
|
22
|
+
ary = CSV.parse(str)
|
23
|
+
ary # => [["foo", "0"], [], ["bar", "1"], ["baz", "2"], [], [nil, nil]]
|
24
|
+
|
25
|
+
Using +true+:
|
26
|
+
ary = CSV.parse(str, skip_blanks: true)
|
27
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"], [nil, nil]]
|
28
|
+
|
29
|
+
Using a truthy value:
|
30
|
+
ary = CSV.parse(str, skip_blanks: :foo)
|
31
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"], [nil, nil]]
|
@@ -0,0 +1,37 @@
|
|
1
|
+
====== Option +skip_lines+
|
2
|
+
|
3
|
+
Specifies an object to use in identifying comment lines in the input that are to be ignored:
|
4
|
+
* If a \Regexp, ignores lines that match it.
|
5
|
+
* If a \String, converts it to a \Regexp, ignores lines that match it.
|
6
|
+
* If +nil+, no lines are considered to be comments.
|
7
|
+
|
8
|
+
Default value:
|
9
|
+
CSV::DEFAULT_OPTIONS.fetch(:skip_lines) # => nil
|
10
|
+
|
11
|
+
For examples in this section:
|
12
|
+
str = <<-EOT
|
13
|
+
# Comment
|
14
|
+
foo,0
|
15
|
+
bar,1
|
16
|
+
baz,2
|
17
|
+
# Another comment
|
18
|
+
EOT
|
19
|
+
str # => "# Comment\nfoo,0\nbar,1\nbaz,2\n# Another comment\n"
|
20
|
+
|
21
|
+
Using the default, +nil+:
|
22
|
+
ary = CSV.parse(str)
|
23
|
+
ary # => [["# Comment"], ["foo", "0"], ["bar", "1"], ["baz", "2"], ["# Another comment"]]
|
24
|
+
|
25
|
+
Using a \Regexp:
|
26
|
+
ary = CSV.parse(str, skip_lines: /^#/)
|
27
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
28
|
+
|
29
|
+
Using a \String:
|
30
|
+
ary = CSV.parse(str, skip_lines: '#')
|
31
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
32
|
+
|
33
|
+
---
|
34
|
+
|
35
|
+
Raises an exception if given an object that is not a \Regexp, a \String, or +nil+:
|
36
|
+
# Raises ArgumentError (:skip_lines has to respond to #match: 0)
|
37
|
+
CSV.parse(str, skip_lines: 0)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
====== Option +strip+
|
2
|
+
|
3
|
+
Specifies the boolean value that determines whether
|
4
|
+
whitespace is stripped from each input field.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:strip) # => false
|
8
|
+
|
9
|
+
With default value +false+:
|
10
|
+
ary = CSV.parse_line(' a , b ')
|
11
|
+
ary # => [" a ", " b "]
|
12
|
+
|
13
|
+
With value +true+:
|
14
|
+
ary = CSV.parse_line(' a , b ', strip: true)
|
15
|
+
ary # => ["a", "b"]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
====== Option +unconverted_fields+
|
2
|
+
|
3
|
+
Specifies the boolean that determines whether unconverted field values are to be available.
|
4
|
+
|
5
|
+
Default value:
|
6
|
+
CSV::DEFAULT_OPTIONS.fetch(:unconverted_fields) # => nil
|
7
|
+
|
8
|
+
The unconverted field values are those found in the source data,
|
9
|
+
prior to any conversions performed via option +converters+.
|
10
|
+
|
11
|
+
When option +unconverted_fields+ is +true+,
|
12
|
+
each returned row (\Array or \CSV::Row) has an added method,
|
13
|
+
+unconverted_fields+, that returns the unconverted field values:
|
14
|
+
str = <<-EOT
|
15
|
+
foo,0
|
16
|
+
bar,1
|
17
|
+
baz,2
|
18
|
+
EOT
|
19
|
+
# Without unconverted_fields
|
20
|
+
csv = CSV.parse(str, converters: :integer)
|
21
|
+
csv # => [["foo", 0], ["bar", 1], ["baz", 2]]
|
22
|
+
csv.first.respond_to?(:unconverted_fields) # => false
|
23
|
+
# With unconverted_fields
|
24
|
+
csv = CSV.parse(str, converters: :integer, unconverted_fields: true)
|
25
|
+
csv # => [["foo", 0], ["bar", 1], ["baz", 2]]
|
26
|
+
csv.first.respond_to?(:unconverted_fields) # => true
|
27
|
+
csv.first.unconverted_fields # => ["foo", "0"]
|
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,85 +103,322 @@ require_relative "csv/writer"
|
|
103
103
|
|
104
104
|
using CSV::MatchP if CSV.const_defined?(:MatchP)
|
105
105
|
|
106
|
+
# == \CSV
|
107
|
+
# \CSV (comma-separated values) 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>.
|
106
112
|
#
|
107
|
-
# This
|
108
|
-
#
|
109
|
-
#
|
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"
|
110
117
|
#
|
111
|
-
#
|
118
|
+
# Despite the name \CSV, a \CSV representation can use different separators.
|
112
119
|
#
|
113
|
-
#
|
120
|
+
# == \Class \CSV
|
114
121
|
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
# csv.each do |row|
|
119
|
-
# # ...
|
120
|
-
# end
|
121
|
-
# # or
|
122
|
-
# row = csv.shift
|
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.
|
123
125
|
#
|
124
|
-
#
|
125
|
-
#
|
126
|
+
# To make \CSV available:
|
127
|
+
# require 'csv'
|
126
128
|
#
|
127
|
-
#
|
128
|
-
# described in the Specialized Methods section.
|
129
|
+
# All examples here assume that this has been done.
|
129
130
|
#
|
130
|
-
#
|
131
|
+
# == Keeping It Simple
|
131
132
|
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
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.
|
135
136
|
#
|
136
|
-
#
|
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.
|
137
141
|
#
|
138
|
-
# ===
|
142
|
+
# === Simple Parsing
|
139
143
|
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
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].
|
146
151
|
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
152
|
+
# ==== Parsing a \String
|
153
|
+
#
|
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"]]
|
153
202
|
#
|
154
|
-
#
|
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"]
|
155
207
|
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
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
|
161
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]
|
233
|
+
# end
|
234
|
+
# output_string # => "foo,0\nbar,1\nbaz,2\n"
|
235
|
+
#
|
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"
|
239
|
+
#
|
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"
|
243
|
+
#
|
244
|
+
# === "Filtering" \CSV
|
162
245
|
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
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
|
168
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
|
309
|
+
#
|
310
|
+
# The default values for options are:
|
311
|
+
# DEFAULT_OPTIONS = {
|
312
|
+
# # For both parsing and generating.
|
313
|
+
# col_sep: ",",
|
314
|
+
# row_sep: :auto,
|
315
|
+
# quote_char: '"',
|
316
|
+
# # For parsing.
|
317
|
+
# field_size_limit: nil,
|
318
|
+
# converters: nil,
|
319
|
+
# unconverted_fields: nil,
|
320
|
+
# headers: false,
|
321
|
+
# return_headers: false,
|
322
|
+
# header_converters: nil,
|
323
|
+
# skip_blanks: false,
|
324
|
+
# skip_lines: nil,
|
325
|
+
# liberal_parsing: false,
|
326
|
+
# nil_value: nil,
|
327
|
+
# empty_value: "",
|
328
|
+
# # For generating.
|
329
|
+
# write_headers: nil,
|
330
|
+
# quote_empty: true,
|
331
|
+
# force_quotes: false,
|
332
|
+
# write_converters: nil,
|
333
|
+
# write_nil_value: nil,
|
334
|
+
# write_empty_value: "",
|
335
|
+
# strip: false,
|
336
|
+
# }
|
337
|
+
#
|
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.
|
359
|
+
#
|
360
|
+
# :include: ../doc/csv/options/common/row_sep.rdoc
|
361
|
+
#
|
362
|
+
# :include: ../doc/csv/options/common/col_sep.rdoc
|
363
|
+
#
|
364
|
+
# :include: ../doc/csv/options/common/quote_char.rdoc
|
365
|
+
#
|
366
|
+
# :include: ../doc/csv/options/parsing/field_size_limit.rdoc
|
367
|
+
#
|
368
|
+
# :include: ../doc/csv/options/parsing/converters.rdoc
|
369
|
+
#
|
370
|
+
# :include: ../doc/csv/options/parsing/unconverted_fields.rdoc
|
371
|
+
#
|
372
|
+
# :include: ../doc/csv/options/parsing/headers.rdoc
|
373
|
+
#
|
374
|
+
# :include: ../doc/csv/options/parsing/return_headers.rdoc
|
375
|
+
#
|
376
|
+
# :include: ../doc/csv/options/parsing/header_converters.rdoc
|
377
|
+
#
|
378
|
+
# :include: ../doc/csv/options/parsing/skip_blanks.rdoc
|
379
|
+
#
|
380
|
+
# :include: ../doc/csv/options/parsing/skip_lines.rdoc
|
381
|
+
#
|
382
|
+
# :include: ../doc/csv/options/parsing/strip.rdoc
|
383
|
+
#
|
384
|
+
# :include: ../doc/csv/options/parsing/liberal_parsing.rdoc
|
385
|
+
#
|
386
|
+
# :include: ../doc/csv/options/parsing/nil_value.rdoc
|
387
|
+
#
|
388
|
+
# :include: ../doc/csv/options/parsing/empty_value.rdoc
|
389
|
+
#
|
390
|
+
# ==== Options for Generating
|
391
|
+
#
|
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.
|
402
|
+
#
|
403
|
+
# :include: ../doc/csv/options/common/row_sep.rdoc
|
404
|
+
#
|
405
|
+
# :include: ../doc/csv/options/common/col_sep.rdoc
|
406
|
+
#
|
407
|
+
# :include: ../doc/csv/options/common/quote_char.rdoc
|
169
408
|
#
|
170
|
-
#
|
409
|
+
# :include: ../doc/csv/options/generating/write_headers.rdoc
|
171
410
|
#
|
172
|
-
#
|
173
|
-
# csv_string = ["CSV", "data"].to_csv # to CSV
|
174
|
-
# csv_array = "CSV,String".parse_csv # from CSV
|
411
|
+
# :include: ../doc/csv/options/generating/force_quotes.rdoc
|
175
412
|
#
|
176
|
-
#
|
177
|
-
# CSV { |csv_out| csv_out << %w{my data here} } # to $stdout
|
178
|
-
# CSV(csv = "") { |csv_str| csv_str << %w{my data here} } # to a String
|
179
|
-
# CSV($stderr) { |csv_err| csv_err << %w{my data here} } # to $stderr
|
180
|
-
# CSV($stdin) { |csv_in| csv_in.each { |row| p row } } # from $stdin
|
413
|
+
# :include: ../doc/csv/options/generating/quote_empty.rdoc
|
181
414
|
#
|
182
|
-
#
|
415
|
+
# :include: ../doc/csv/options/generating/write_converters.rdoc
|
183
416
|
#
|
184
|
-
#
|
417
|
+
# :include: ../doc/csv/options/generating/write_nil_value.rdoc
|
418
|
+
#
|
419
|
+
# :include: ../doc/csv/options/generating/write_empty_value.rdoc
|
420
|
+
#
|
421
|
+
# === \CSV with Headers
|
185
422
|
#
|
186
423
|
# CSV allows to specify column names of CSV file, whether they are in data, or
|
187
424
|
# provided separately. If headers are specified, reading methods return an instance
|
@@ -203,24 +440,337 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
203
440
|
# data = CSV.parse('Bob,Engineering,1000', headers: %i[name department salary])
|
204
441
|
# data.first #=> #<CSV::Row name:"Bob" department:"Engineering" salary:"1000">
|
205
442
|
#
|
206
|
-
# ===
|
207
|
-
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
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.
|
565
|
+
# A converter might, for example, convert an integer embedded in a \String
|
566
|
+
# into a true \Integer.
|
567
|
+
# (In fact, that's what built-in field converter +:integer+ does.)
|
568
|
+
#
|
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"]
|
589
|
+
# # Add a converter.
|
590
|
+
# csv.convert(:integer)
|
591
|
+
# csv.converters # => [:integer]
|
592
|
+
# csv.read # => [["bar", 1], ["baz", 2]]
|
593
|
+
#
|
594
|
+
# There are additional built-in \converters, and custom \converters are also supported.
|
595
|
+
#
|
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:
|
625
|
+
# data = '0,1,2,x'
|
626
|
+
# # Without the converter
|
627
|
+
# csv = CSV.parse_line(data)
|
628
|
+
# csv # => ["0", "1", "2", "x"]
|
629
|
+
# # With the converter
|
630
|
+
# csv = CSV.parse_line(data, converters: :integer)
|
631
|
+
# csv # => [0, 1, 2, "x"]
|
632
|
+
#
|
633
|
+
# Converter +:float+ converts each field that Float() accepts:
|
634
|
+
# data = '1.0,3.14159,x'
|
635
|
+
# # Without the converter
|
636
|
+
# csv = CSV.parse_line(data)
|
637
|
+
# csv # => ["1.0", "3.14159", "x"]
|
638
|
+
# # With the converter
|
639
|
+
# csv = CSV.parse_line(data, converters: :float)
|
640
|
+
# csv # => [1.0, 3.14159, "x"]
|
641
|
+
#
|
642
|
+
# Converter +:numeric+ converts with both +:integer+ and +:float+..
|
643
|
+
#
|
644
|
+
# Converter +:date+ converts each field that Date::parse accepts:
|
645
|
+
# data = '2001-02-03,x'
|
646
|
+
# # Without the converter
|
647
|
+
# csv = CSV.parse_line(data)
|
648
|
+
# csv # => ["2001-02-03", "x"]
|
649
|
+
# # With the converter
|
650
|
+
# csv = CSV.parse_line(data, converters: :date)
|
651
|
+
# csv # => [#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>, "x"]
|
652
|
+
#
|
653
|
+
# Converter +:date_time+ converts each field that DateTime::parse accepts:
|
654
|
+
# data = '2020-05-07T14:59:00-05:00,x'
|
655
|
+
# # Without the converter
|
656
|
+
# csv = CSV.parse_line(data)
|
657
|
+
# csv # => ["2020-05-07T14:59:00-05:00", "x"]
|
658
|
+
# # With the converter
|
659
|
+
# csv = CSV.parse_line(data, converters: :date_time)
|
660
|
+
# csv # => [#<DateTime: 2020-05-07T14:59:00-05:00 ((2458977j,71940s,0n),-18000s,2299161j)>, "x"]
|
661
|
+
#
|
662
|
+
# Converter +:numeric+ converts with both +:date_time+ and +:numeric+..
|
663
|
+
#
|
664
|
+
# As seen above, method #convert adds \converters to a \CSV instance,
|
665
|
+
# and method #converters returns an \Array of the \converters in effect:
|
666
|
+
# csv = CSV.new('0,1,2')
|
667
|
+
# csv.converters # => []
|
668
|
+
# csv.convert(:integer)
|
669
|
+
# csv.converters # => [:integer]
|
670
|
+
# csv.convert(:date)
|
671
|
+
# csv.converters # => [:integer, :date]
|
672
|
+
#
|
673
|
+
# ===== Custom Field \Converters
|
674
|
+
#
|
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"]]
|
683
|
+
#
|
684
|
+
# ==== Header \Converters
|
685
|
+
#
|
686
|
+
# Header converters operate only on headers (and not on other rows).
|
687
|
+
#
|
688
|
+
# There are three ways to use header \converters;
|
689
|
+
# these examples use built-in header converter +:dowhcase+,
|
690
|
+
# which downcases each parsed header.
|
691
|
+
#
|
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:
|
717
|
+
# CSV::HeaderConverters.keys # => [:downcase, :symbol]
|
718
|
+
#
|
719
|
+
# Converter +:downcase+ converts each header by downcasing it:
|
720
|
+
# string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
|
721
|
+
# tbl = CSV.parse(string, headers: true, header_converters: :downcase)
|
722
|
+
# tbl.class # => CSV::Table
|
723
|
+
# tbl.headers # => ["name", "count"]
|
724
|
+
#
|
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)
|
728
|
+
# tbl.headers # => [:name, :count]
|
729
|
+
# Details:
|
730
|
+
# - Strips leading and trailing whitespace.
|
731
|
+
# - Downcases the header.
|
732
|
+
# - Replaces embedded spaces with underscores.
|
733
|
+
# - Removes non-word characters.
|
734
|
+
# - Makes the string into a \Symbol.
|
735
|
+
#
|
736
|
+
# ===== Custom Header \Converters
|
737
|
+
#
|
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"]
|
747
|
+
#
|
748
|
+
# ===== Write \Converters
|
749
|
+
#
|
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)
|
224
774
|
#
|
225
775
|
# This new CSV parser is m17n savvy. The parser works in the Encoding of the IO
|
226
776
|
# or String object being read from or written to. Your data is never transcoded
|
@@ -301,30 +851,12 @@ class CSV
|
|
301
851
|
# The encoding used by all converters.
|
302
852
|
ConverterEncoding = Encoding.find("UTF-8")
|
303
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].
|
304
856
|
#
|
305
|
-
# This Hash
|
306
|
-
#
|
307
|
-
#
|
308
|
-
#
|
309
|
-
# <b><tt>:integer</tt></b>:: Converts any field Integer() accepts.
|
310
|
-
# <b><tt>:float</tt></b>:: Converts any field Float() accepts.
|
311
|
-
# <b><tt>:numeric</tt></b>:: A combination of <tt>:integer</tt>
|
312
|
-
# and <tt>:float</tt>.
|
313
|
-
# <b><tt>:date</tt></b>:: Converts any field Date::parse() accepts.
|
314
|
-
# <b><tt>:date_time</tt></b>:: Converts any field DateTime::parse() accepts.
|
315
|
-
# <b><tt>:all</tt></b>:: All built-in converters. A combination of
|
316
|
-
# <tt>:date_time</tt> and <tt>:numeric</tt>.
|
317
|
-
#
|
318
|
-
# All built-in converters transcode field data to UTF-8 before attempting a
|
319
|
-
# conversion. If your data cannot be transcoded to UTF-8 the conversion will
|
320
|
-
# fail and the field will remain unchanged.
|
321
|
-
#
|
322
|
-
# This Hash is intentionally left unfrozen and users should feel free to add
|
323
|
-
# values to it that can be accessed by all CSV objects.
|
324
|
-
#
|
325
|
-
# To add a combo field, the value should be an Array of names. Combo fields
|
326
|
-
# can be nested with other combo fields.
|
327
|
-
#
|
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].
|
328
860
|
Converters = {
|
329
861
|
integer: lambda { |f|
|
330
862
|
Integer(f.encode(ConverterEncoding)) rescue f
|
@@ -352,27 +884,12 @@ class CSV
|
|
352
884
|
all: [:date_time, :numeric],
|
353
885
|
}
|
354
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].
|
355
889
|
#
|
356
|
-
# This Hash
|
357
|
-
#
|
358
|
-
#
|
359
|
-
#
|
360
|
-
# <b><tt>:downcase</tt></b>:: Calls downcase() on the header String.
|
361
|
-
# <b><tt>:symbol</tt></b>:: Leading/trailing spaces are dropped, string is
|
362
|
-
# downcased, remaining spaces are replaced with
|
363
|
-
# underscores, non-word characters are dropped,
|
364
|
-
# and finally to_sym() is called.
|
365
|
-
#
|
366
|
-
# All built-in header converters transcode header data to UTF-8 before
|
367
|
-
# attempting a conversion. If your data cannot be transcoded to UTF-8 the
|
368
|
-
# conversion will fail and the header will remain unchanged.
|
369
|
-
#
|
370
|
-
# This Hash is intentionally left unfrozen and users should feel free to add
|
371
|
-
# values to it that can be accessed by all CSV objects.
|
372
|
-
#
|
373
|
-
# To add a combo field, the value should be an Array of names. Combo fields
|
374
|
-
# can be nested with other combo fields.
|
375
|
-
#
|
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].
|
376
893
|
HeaderConverters = {
|
377
894
|
downcase: lambda { |h| h.encode(ConverterEncoding).downcase },
|
378
895
|
symbol: lambda { |h|
|
@@ -380,29 +897,13 @@ class CSV
|
|
380
897
|
gsub(/\s+/, "_").to_sym
|
381
898
|
}
|
382
899
|
}
|
383
|
-
|
384
|
-
#
|
385
|
-
# The options used when no overrides are given by calling code. They are:
|
386
|
-
#
|
387
|
-
# <b><tt>:col_sep</tt></b>:: <tt>","</tt>
|
388
|
-
# <b><tt>:row_sep</tt></b>:: <tt>:auto</tt>
|
389
|
-
# <b><tt>:quote_char</tt></b>:: <tt>'"'</tt>
|
390
|
-
# <b><tt>:field_size_limit</tt></b>:: +nil+
|
391
|
-
# <b><tt>:converters</tt></b>:: +nil+
|
392
|
-
# <b><tt>:unconverted_fields</tt></b>:: +nil+
|
393
|
-
# <b><tt>:headers</tt></b>:: +false+
|
394
|
-
# <b><tt>:return_headers</tt></b>:: +false+
|
395
|
-
# <b><tt>:header_converters</tt></b>:: +nil+
|
396
|
-
# <b><tt>:skip_blanks</tt></b>:: +false+
|
397
|
-
# <b><tt>:force_quotes</tt></b>:: +false+
|
398
|
-
# <b><tt>:skip_lines</tt></b>:: +nil+
|
399
|
-
# <b><tt>:liberal_parsing</tt></b>:: +false+
|
400
|
-
# <b><tt>:quote_empty</tt></b>:: +true+
|
401
|
-
#
|
900
|
+
# Default values for method options.
|
402
901
|
DEFAULT_OPTIONS = {
|
902
|
+
# For both parsing and generating.
|
403
903
|
col_sep: ",",
|
404
904
|
row_sep: :auto,
|
405
905
|
quote_char: '"',
|
906
|
+
# For parsing.
|
406
907
|
field_size_limit: nil,
|
407
908
|
converters: nil,
|
408
909
|
unconverted_fields: nil,
|
@@ -410,22 +911,62 @@ class CSV
|
|
410
911
|
return_headers: false,
|
411
912
|
header_converters: nil,
|
412
913
|
skip_blanks: false,
|
413
|
-
force_quotes: false,
|
414
914
|
skip_lines: nil,
|
415
915
|
liberal_parsing: false,
|
916
|
+
nil_value: nil,
|
917
|
+
empty_value: "",
|
918
|
+
# For generating.
|
919
|
+
write_headers: nil,
|
416
920
|
quote_empty: true,
|
921
|
+
force_quotes: false,
|
922
|
+
write_converters: nil,
|
923
|
+
write_nil_value: nil,
|
924
|
+
write_empty_value: "",
|
925
|
+
strip: false,
|
417
926
|
}.freeze
|
418
927
|
|
419
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| ... }
|
934
|
+
#
|
935
|
+
# Creates or retrieves cached \CSV objects.
|
936
|
+
# For arguments and options, see CSV.new.
|
937
|
+
#
|
938
|
+
# ---
|
420
939
|
#
|
421
|
-
#
|
422
|
-
# instance will be cached and returned for all future calls to this method for
|
423
|
-
# the same +data+ object (tested by Object#object_id()) with the same
|
424
|
-
# +options+.
|
940
|
+
# With no block given, returns a \CSV object.
|
425
941
|
#
|
426
|
-
#
|
427
|
-
#
|
942
|
+
# The first call to +instance+ creates and caches a \CSV object:
|
943
|
+
# s0 = 's0'
|
944
|
+
# csv0 = CSV.instance(s0)
|
945
|
+
# csv0.class # => CSV
|
428
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
|
429
970
|
def instance(data = $stdout, **options)
|
430
971
|
# create a _signature_ for this method call, data object and options
|
431
972
|
sig = [data.object_id] +
|
@@ -442,30 +983,61 @@ class CSV
|
|
442
983
|
end
|
443
984
|
end
|
444
985
|
|
445
|
-
#
|
446
986
|
# :call-seq:
|
447
|
-
# filter(
|
448
|
-
# filter(
|
449
|
-
# filter(
|
450
|
-
#
|
451
|
-
#
|
452
|
-
#
|
453
|
-
#
|
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| ... }
|
454
994
|
#
|
455
|
-
#
|
456
|
-
# (generally String or IO objects). If not given, they default to
|
457
|
-
# <tt>ARGF</tt> and <tt>$stdout</tt>.
|
995
|
+
# Reads \CSV input and writes \CSV output.
|
458
996
|
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
462
|
-
#
|
463
|
-
#
|
464
|
-
#
|
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.
|
465
1003
|
#
|
466
|
-
#
|
467
|
-
#
|
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].
|
468
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"
|
469
1041
|
def filter(input=nil, output=nil, **options)
|
470
1042
|
# parse options for input, output, or both
|
471
1043
|
in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR}
|
@@ -492,18 +1064,111 @@ class CSV
|
|
492
1064
|
end
|
493
1065
|
|
494
1066
|
#
|
495
|
-
#
|
496
|
-
#
|
497
|
-
#
|
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/csv/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.
|
1134
|
+
#
|
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)
|
1139
|
+
#
|
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">
|
498
1147
|
#
|
499
|
-
#
|
500
|
-
#
|
501
|
-
#
|
502
|
-
#
|
503
|
-
#
|
504
|
-
#
|
505
|
-
#
|
506
|
-
#
|
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| }
|
507
1172
|
#
|
508
1173
|
def foreach(path, mode="r", **options, &block)
|
509
1174
|
return to_enum(__method__, path, mode, **options) unless block_given?
|
@@ -514,29 +1179,71 @@ class CSV
|
|
514
1179
|
|
515
1180
|
#
|
516
1181
|
# :call-seq:
|
517
|
-
# generate(
|
518
|
-
# generate(
|
1182
|
+
# generate(csv_string, **options) {|csv| ... }
|
1183
|
+
# generate(**options) {|csv| ... }
|
1184
|
+
#
|
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].
|
1189
|
+
#
|
1190
|
+
# ---
|
519
1191
|
#
|
520
|
-
#
|
521
|
-
#
|
522
|
-
#
|
523
|
-
# will be returned.
|
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.
|
524
1195
|
#
|
525
|
-
# Note that a passed String *is* modified by this method.
|
526
|
-
#
|
1196
|
+
# Note that a passed \String *is* modified by this method.
|
1197
|
+
# Pass <tt>csv_string</tt>.dup if the \String must be preserved.
|
527
1198
|
#
|
528
|
-
#
|
529
|
-
#
|
530
|
-
#
|
531
|
-
#
|
1199
|
+
# This method has one additional option: <tt>:encoding</tt>,
|
1200
|
+
# which sets the base Encoding for the output if no no +str+ is specified.
|
1201
|
+
# CSV needs this hint if you plan to output non-ASCII compatible data.
|
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)
|
532
1238
|
#
|
533
1239
|
def generate(str=nil, **options)
|
1240
|
+
encoding = options[:encoding]
|
534
1241
|
# add a default empty String, if none was given
|
535
1242
|
if str
|
536
1243
|
str = StringIO.new(str)
|
537
1244
|
str.seek(0, IO::SEEK_END)
|
1245
|
+
str.set_encoding(encoding) if encoding
|
538
1246
|
else
|
539
|
-
encoding = options[:encoding]
|
540
1247
|
str = +""
|
541
1248
|
str.force_encoding(encoding) if encoding
|
542
1249
|
end
|
@@ -545,97 +1252,140 @@ class CSV
|
|
545
1252
|
csv.string # return final String
|
546
1253
|
end
|
547
1254
|
|
1255
|
+
# :call-seq:
|
1256
|
+
# CSV.generate_line(ary)
|
1257
|
+
# CSV.generate_line(ary, **options)
|
548
1258
|
#
|
549
|
-
#
|
550
|
-
#
|
1259
|
+
# Returns the \String created by generating \CSV from +ary+
|
1260
|
+
# using the specified +options+.
|
551
1261
|
#
|
552
|
-
#
|
553
|
-
# understands an additional <tt>:encoding</tt> parameter to set the base
|
554
|
-
# Encoding for the output. This method will try to guess your Encoding from
|
555
|
-
# the first non-+nil+ field in +row+, if possible, but you may need to use
|
556
|
-
# this parameter as a backup plan.
|
1262
|
+
# Argument +ary+ must be an \Array.
|
557
1263
|
#
|
558
|
-
#
|
559
|
-
#
|
1264
|
+
# Special options:
|
1265
|
+
# * Option <tt>:row_sep</tt> defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>
|
1266
|
+
# (<tt>$/</tt>).:
|
1267
|
+
# $INPUT_RECORD_SEPARATOR # => "\n"
|
1268
|
+
# * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
|
1269
|
+
# Encoding for the output. This method will try to guess your Encoding from
|
1270
|
+
# the first non-+nil+ field in +row+, if possible, but you may need to use
|
1271
|
+
# this parameter as a backup plan.
|
1272
|
+
#
|
1273
|
+
# For other +options+,
|
1274
|
+
# see {Options for Generating}[#class-CSV-label-Options+for+Generating].
|
1275
|
+
#
|
1276
|
+
# ---
|
1277
|
+
#
|
1278
|
+
# Returns the \String generated from an \Array:
|
1279
|
+
# CSV.generate_line(['foo', '0']) # => "foo,0\n"
|
1280
|
+
#
|
1281
|
+
# ---
|
1282
|
+
#
|
1283
|
+
# Raises an exception if +ary+ is not an \Array:
|
1284
|
+
# # Raises NoMethodError (undefined method `find' for :foo:Symbol)
|
1285
|
+
# CSV.generate_line(:foo)
|
560
1286
|
#
|
561
1287
|
def generate_line(row, **options)
|
562
1288
|
options = {row_sep: $INPUT_RECORD_SEPARATOR}.merge(options)
|
563
1289
|
str = +""
|
564
1290
|
if options[:encoding]
|
565
1291
|
str.force_encoding(options[:encoding])
|
566
|
-
|
567
|
-
|
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
|
568
1306
|
end
|
569
1307
|
(new(str, **options) << row).string
|
570
1308
|
end
|
571
1309
|
|
572
1310
|
#
|
573
1311
|
# :call-seq:
|
574
|
-
# open(
|
575
|
-
# open(
|
576
|
-
# open(
|
577
|
-
# open(
|
578
|
-
#
|
579
|
-
#
|
580
|
-
#
|
581
|
-
#
|
582
|
-
#
|
583
|
-
#
|
584
|
-
#
|
585
|
-
#
|
586
|
-
#
|
587
|
-
#
|
588
|
-
#
|
589
|
-
#
|
590
|
-
#
|
591
|
-
#
|
592
|
-
#
|
593
|
-
#
|
594
|
-
#
|
595
|
-
#
|
596
|
-
#
|
597
|
-
#
|
598
|
-
#
|
599
|
-
#
|
600
|
-
#
|
601
|
-
#
|
602
|
-
#
|
603
|
-
#
|
604
|
-
#
|
605
|
-
#
|
606
|
-
#
|
607
|
-
#
|
608
|
-
#
|
609
|
-
#
|
610
|
-
#
|
611
|
-
#
|
612
|
-
#
|
613
|
-
# * fileno()
|
614
|
-
# * flock()
|
615
|
-
# * flush()
|
616
|
-
# * fsync()
|
617
|
-
# * internal_encoding()
|
618
|
-
# * ioctl()
|
619
|
-
# * isatty()
|
620
|
-
# * path()
|
621
|
-
# * pid()
|
622
|
-
# * pos()
|
623
|
-
# * pos=()
|
624
|
-
# * reopen()
|
625
|
-
# * seek()
|
626
|
-
# * stat()
|
627
|
-
# * sync()
|
628
|
-
# * sync=()
|
629
|
-
# * tell()
|
630
|
-
# * to_i()
|
631
|
-
# * to_io()
|
632
|
-
# * truncate()
|
633
|
-
# * 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/csv/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.
|
634
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)
|
635
1382
|
def open(filename, mode="r", **options)
|
636
1383
|
# wrap a File opened with the remaining +args+ with no newline
|
637
1384
|
# decorator
|
638
1385
|
file_opts = {universal_newline: false}.merge(options)
|
1386
|
+
options.delete(:invalid)
|
1387
|
+
options.delete(:undef)
|
1388
|
+
options.delete(:replace)
|
639
1389
|
|
640
1390
|
begin
|
641
1391
|
f = File.open(filename, mode, **file_opts)
|
@@ -666,16 +1416,116 @@ class CSV
|
|
666
1416
|
|
667
1417
|
#
|
668
1418
|
# :call-seq:
|
669
|
-
# parse(
|
670
|
-
# 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/csv/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.
|
671
1445
|
#
|
672
|
-
#
|
673
|
-
#
|
674
|
-
#
|
1446
|
+
# Parse a \String:
|
1447
|
+
# a_of_a = CSV.parse(string)
|
1448
|
+
# a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
675
1449
|
#
|
676
|
-
#
|
677
|
-
#
|
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"]]
|
678
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 }
|
1462
|
+
#
|
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
|
+
# ---
|
1488
|
+
#
|
1489
|
+
# With no block given, returns a CSV::Table object formed from the source.
|
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)
|
679
1529
|
def parse(str, **options, &block)
|
680
1530
|
csv = new(str, **options)
|
681
1531
|
|
@@ -689,44 +1539,117 @@ class CSV
|
|
689
1539
|
end
|
690
1540
|
end
|
691
1541
|
|
1542
|
+
# :call-seq:
|
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
|
1549
|
+
#
|
1550
|
+
# Returns the data created by parsing the first line of +string+ or +io+
|
1551
|
+
# using the specified +options+.
|
1552
|
+
#
|
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/csv/arguments/io.rdoc
|
1556
|
+
# - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
|
1557
|
+
#
|
1558
|
+
# ====== Without Option +headers+
|
1559
|
+
#
|
1560
|
+
# Without option +headers+, returns the first row as a new \Array.
|
1561
|
+
#
|
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)
|
1566
|
+
#
|
1567
|
+
# Parse the first line from a \String object:
|
1568
|
+
# CSV.parse_line(string) # => ["foo", "0"]
|
1569
|
+
#
|
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"]
|
1574
|
+
#
|
1575
|
+
# Returns +nil+ if the argument is an empty \String:
|
1576
|
+
# CSV.parse_line('') # => nil
|
692
1577
|
#
|
693
|
-
#
|
694
|
-
# an Array. Note that if +line+ contains multiple rows, anything beyond the
|
695
|
-
# first row is ignored.
|
1578
|
+
# ====== With Option +headers+
|
696
1579
|
#
|
697
|
-
#
|
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
|
+
#
|
1596
|
+
# ---
|
1597
|
+
#
|
1598
|
+
# Raises an exception if the argument is +nil+:
|
1599
|
+
# # Raises ArgumentError (Cannot parse nil as CSV):
|
1600
|
+
# CSV.parse_line(nil)
|
698
1601
|
#
|
699
1602
|
def parse_line(line, **options)
|
700
|
-
new(line, **options).
|
1603
|
+
new(line, **options).each.first
|
701
1604
|
end
|
702
1605
|
|
703
1606
|
#
|
704
|
-
#
|
705
|
-
#
|
706
|
-
#
|
707
|
-
#
|
708
|
-
#
|
709
|
-
#
|
710
|
-
#
|
711
|
-
# <tt>encoding: "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file
|
712
|
-
# but transcode it to UTF-8 before CSV parses it.
|
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.
|
713
1614
|
#
|
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"]]
|
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>
|
714
1626
|
def read(path, **options)
|
715
1627
|
open(path, **options) { |csv| csv.read }
|
716
1628
|
end
|
717
1629
|
|
718
|
-
#
|
1630
|
+
# :call-seq:
|
1631
|
+
# CSV.readlines(source, **options)
|
1632
|
+
#
|
1633
|
+
# Alias for CSV.read.
|
719
1634
|
def readlines(path, **options)
|
720
1635
|
read(path, **options)
|
721
1636
|
end
|
722
1637
|
|
1638
|
+
# :call-seq:
|
1639
|
+
# CSV.table(source, **options)
|
723
1640
|
#
|
724
|
-
#
|
1641
|
+
# Calls CSV.read with +source+, +options+, and certain default options:
|
1642
|
+
# - +headers+: +true+
|
1643
|
+
# - +converbers+: +:numeric+
|
1644
|
+
# - +header_converters+: +:symbol+
|
725
1645
|
#
|
726
|
-
#
|
727
|
-
# converters: :numeric,
|
728
|
-
# header_converters: :symbol }.merge(options) )
|
1646
|
+
# Returns a CSV::Table object.
|
729
1647
|
#
|
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>
|
730
1653
|
def table(path, **options)
|
731
1654
|
default_options = {
|
732
1655
|
headers: true,
|
@@ -738,185 +1661,43 @@ class CSV
|
|
738
1661
|
end
|
739
1662
|
end
|
740
1663
|
|
1664
|
+
# :call-seq:
|
1665
|
+
# CSV.new(string)
|
1666
|
+
# CSV.new(io)
|
1667
|
+
# CSV.new(string, **options)
|
1668
|
+
# CSV.new(io, **options)
|
1669
|
+
#
|
1670
|
+
# Returns the new \CSV object created using +string+ or +io+
|
1671
|
+
# and the specified +options+.
|
1672
|
+
#
|
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/csv/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.
|
741
1681
|
#
|
742
|
-
#
|
743
|
-
#
|
744
|
-
#
|
745
|
-
#
|
746
|
-
#
|
747
|
-
#
|
748
|
-
#
|
749
|
-
#
|
750
|
-
#
|
751
|
-
#
|
752
|
-
#
|
753
|
-
#
|
754
|
-
#
|
755
|
-
#
|
756
|
-
#
|
757
|
-
#
|
758
|
-
#
|
759
|
-
#
|
760
|
-
#
|
761
|
-
# that CSV automatically discover this
|
762
|
-
# from the data. Auto-discovery reads
|
763
|
-
# ahead in the data looking for the next
|
764
|
-
# <tt>"\r\n"</tt>, <tt>"\n"</tt>, or
|
765
|
-
# <tt>"\r"</tt> sequence. A sequence
|
766
|
-
# will be selected even if it occurs in
|
767
|
-
# a quoted field, assuming that you
|
768
|
-
# would have the same line endings
|
769
|
-
# there. If none of those sequences is
|
770
|
-
# found, +data+ is <tt>ARGF</tt>,
|
771
|
-
# <tt>STDIN</tt>, <tt>STDOUT</tt>, or
|
772
|
-
# <tt>STDERR</tt>, or the stream is only
|
773
|
-
# available for output, the default
|
774
|
-
# <tt>$INPUT_RECORD_SEPARATOR</tt>
|
775
|
-
# (<tt>$/</tt>) is used. Obviously,
|
776
|
-
# discovery takes a little time. Set
|
777
|
-
# manually if speed is important. Also
|
778
|
-
# note that IO objects should be opened
|
779
|
-
# in binary mode on Windows if this
|
780
|
-
# feature will be used as the
|
781
|
-
# line-ending translation can cause
|
782
|
-
# problems with resetting the document
|
783
|
-
# position to where it was before the
|
784
|
-
# read ahead. This String will be
|
785
|
-
# transcoded into the data's Encoding
|
786
|
-
# before parsing.
|
787
|
-
# <b><tt>:quote_char</tt></b>:: The character used to quote fields.
|
788
|
-
# This has to be a single character
|
789
|
-
# String. This is useful for
|
790
|
-
# application that incorrectly use
|
791
|
-
# <tt>'</tt> as the quote character
|
792
|
-
# instead of the correct <tt>"</tt>.
|
793
|
-
# CSV will always consider a double
|
794
|
-
# sequence of this character to be an
|
795
|
-
# escaped quote. This String will be
|
796
|
-
# transcoded into the data's Encoding
|
797
|
-
# before parsing.
|
798
|
-
# <b><tt>:field_size_limit</tt></b>:: This is a maximum size CSV will read
|
799
|
-
# ahead looking for the closing quote
|
800
|
-
# for a field. (In truth, it reads to
|
801
|
-
# the first line ending beyond this
|
802
|
-
# size.) If a quote cannot be found
|
803
|
-
# within the limit CSV will raise a
|
804
|
-
# MalformedCSVError, assuming the data
|
805
|
-
# is faulty. You can use this limit to
|
806
|
-
# prevent what are effectively DoS
|
807
|
-
# attacks on the parser. However, this
|
808
|
-
# limit can cause a legitimate parse to
|
809
|
-
# fail and thus is set to +nil+, or off,
|
810
|
-
# by default.
|
811
|
-
# <b><tt>:converters</tt></b>:: An Array of names from the Converters
|
812
|
-
# Hash and/or lambdas that handle custom
|
813
|
-
# conversion. A single converter
|
814
|
-
# doesn't have to be in an Array. All
|
815
|
-
# built-in converters try to transcode
|
816
|
-
# fields to UTF-8 before converting.
|
817
|
-
# The conversion will fail if the data
|
818
|
-
# cannot be transcoded, leaving the
|
819
|
-
# field unchanged.
|
820
|
-
# <b><tt>:unconverted_fields</tt></b>:: If set to +true+, an
|
821
|
-
# unconverted_fields() method will be
|
822
|
-
# added to all returned rows (Array or
|
823
|
-
# CSV::Row) that will return the fields
|
824
|
-
# as they were before conversion. Note
|
825
|
-
# that <tt>:headers</tt> supplied by
|
826
|
-
# Array or String were not fields of the
|
827
|
-
# document and thus will have an empty
|
828
|
-
# Array attached.
|
829
|
-
# <b><tt>:headers</tt></b>:: If set to <tt>:first_row</tt> or
|
830
|
-
# +true+, the initial row of the CSV
|
831
|
-
# file will be treated as a row of
|
832
|
-
# headers. If set to an Array, the
|
833
|
-
# contents will be used as the headers.
|
834
|
-
# If set to a String, the String is run
|
835
|
-
# through a call of CSV::parse_line()
|
836
|
-
# with the same <tt>:col_sep</tt>,
|
837
|
-
# <tt>:row_sep</tt>, and
|
838
|
-
# <tt>:quote_char</tt> as this instance
|
839
|
-
# to produce an Array of headers. This
|
840
|
-
# setting causes CSV#shift() to return
|
841
|
-
# rows as CSV::Row objects instead of
|
842
|
-
# Arrays and CSV#read() to return
|
843
|
-
# CSV::Table objects instead of an Array
|
844
|
-
# of Arrays.
|
845
|
-
# <b><tt>:return_headers</tt></b>:: When +false+, header rows are silently
|
846
|
-
# swallowed. If set to +true+, header
|
847
|
-
# rows are returned in a CSV::Row object
|
848
|
-
# with identical headers and
|
849
|
-
# fields (save that the fields do not go
|
850
|
-
# through the converters).
|
851
|
-
# <b><tt>:write_headers</tt></b>:: When +true+ and <tt>:headers</tt> is
|
852
|
-
# set, a header row will be added to the
|
853
|
-
# output.
|
854
|
-
# <b><tt>:header_converters</tt></b>:: Identical in functionality to
|
855
|
-
# <tt>:converters</tt> save that the
|
856
|
-
# conversions are only made to header
|
857
|
-
# rows. All built-in converters try to
|
858
|
-
# transcode headers to UTF-8 before
|
859
|
-
# converting. The conversion will fail
|
860
|
-
# if the data cannot be transcoded,
|
861
|
-
# leaving the header unchanged.
|
862
|
-
# <b><tt>:skip_blanks</tt></b>:: When setting a +true+ value, CSV will
|
863
|
-
# skip over any empty rows. Note that
|
864
|
-
# this setting will not skip rows that
|
865
|
-
# contain column separators, even if
|
866
|
-
# the rows contain no actual data. If
|
867
|
-
# you want to skip rows that contain
|
868
|
-
# separators but no content, consider
|
869
|
-
# using <tt>:skip_lines</tt>, or
|
870
|
-
# inspecting fields.compact.empty? on
|
871
|
-
# each row.
|
872
|
-
# <b><tt>:force_quotes</tt></b>:: When setting a +true+ value, CSV will
|
873
|
-
# quote all CSV fields it creates.
|
874
|
-
# <b><tt>:skip_lines</tt></b>:: When setting an object responding to
|
875
|
-
# <tt>match</tt>, every line matching
|
876
|
-
# it is considered a comment and ignored
|
877
|
-
# during parsing. When set to a String,
|
878
|
-
# it is first converted to a Regexp.
|
879
|
-
# When set to +nil+ no line is considered
|
880
|
-
# a comment. If the passed object does
|
881
|
-
# not respond to <tt>match</tt>,
|
882
|
-
# <tt>ArgumentError</tt> is thrown.
|
883
|
-
# <b><tt>:liberal_parsing</tt></b>:: When setting a +true+ value, CSV will
|
884
|
-
# attempt to parse input not conformant
|
885
|
-
# with RFC 4180, such as double quotes
|
886
|
-
# in unquoted fields.
|
887
|
-
# <b><tt>:nil_value</tt></b>:: When set an object, any values of an
|
888
|
-
# empty field is replaced by the set
|
889
|
-
# object, not nil.
|
890
|
-
# <b><tt>:empty_value</tt></b>:: When setting an object, any values of a
|
891
|
-
# blank string field is replaced by
|
892
|
-
# the set object.
|
893
|
-
# <b><tt>:quote_empty</tt></b>:: When setting a +true+ value, CSV will
|
894
|
-
# quote empty values with double quotes.
|
895
|
-
# When +false+, CSV will emit an
|
896
|
-
# empty string for an empty field value.
|
897
|
-
# <b><tt>:write_converters</tt></b>:: Converts values on each line with the
|
898
|
-
# specified <tt>Proc</tt> object(s),
|
899
|
-
# which receive a <tt>String</tt> value
|
900
|
-
# and return a <tt>String</tt> or +nil+
|
901
|
-
# value.
|
902
|
-
# When an array is specified, each
|
903
|
-
# converter will be applied in order.
|
904
|
-
# <b><tt>:write_nil_value</tt></b>:: When a <tt>String</tt> value, +nil+
|
905
|
-
# value(s) on each line will be replaced
|
906
|
-
# with the specified value.
|
907
|
-
# <b><tt>:write_empty_value</tt></b>:: When a <tt>String</tt> or +nil+ value,
|
908
|
-
# empty value(s) on each line will be
|
909
|
-
# replaced with the specified value.
|
910
|
-
# <b><tt>:strip</tt></b>:: When setting a +true+ value, CSV will
|
911
|
-
# strip "\t\r\n\f\v" around the values.
|
912
|
-
# If you specify a string instead of
|
913
|
-
# +true+, CSV will strip string. The
|
914
|
-
# length of the string must be 1.
|
915
|
-
#
|
916
|
-
# See CSV::DEFAULT_OPTIONS for the default settings.
|
917
|
-
#
|
918
|
-
# Options cannot be overridden in the instance methods for performance reasons,
|
919
|
-
# so be sure to set what you want here.
|
1682
|
+
# In addition to the \CSV instance methods, several \IO methods are delegated.
|
1683
|
+
# See {Delegated Methods}[#class-CSV-label-Delegated+Methods].
|
1684
|
+
#
|
1685
|
+
# ---
|
1686
|
+
#
|
1687
|
+
# Create a \CSV object from a \String object:
|
1688
|
+
# csv = CSV.new('foo,0')
|
1689
|
+
# csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1690
|
+
#
|
1691
|
+
# Create a \CSV object from a \File object:
|
1692
|
+
# File.write('t.csv', 'foo,0')
|
1693
|
+
# csv = CSV.new(File.open('t.csv'))
|
1694
|
+
# csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
|
1695
|
+
#
|
1696
|
+
# ---
|
1697
|
+
#
|
1698
|
+
# Raises an exception if the argument is +nil+:
|
1699
|
+
# # Raises ArgumentError (Cannot parse nil as CSV):
|
1700
|
+
# CSV.new(nil)
|
920
1701
|
#
|
921
1702
|
def initialize(data,
|
922
1703
|
col_sep: ",",
|
@@ -1001,51 +1782,67 @@ class CSV
|
|
1001
1782
|
writer if @writer_options[:write_headers]
|
1002
1783
|
end
|
1003
1784
|
|
1785
|
+
# :call-seq:
|
1786
|
+
# csv.col_sep -> string
|
1004
1787
|
#
|
1005
|
-
#
|
1006
|
-
#
|
1007
|
-
#
|
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 # => ","
|
1008
1791
|
def col_sep
|
1009
1792
|
parser.column_separator
|
1010
1793
|
end
|
1011
1794
|
|
1795
|
+
# :call-seq:
|
1796
|
+
# csv.row_sep -> string
|
1012
1797
|
#
|
1013
|
-
#
|
1014
|
-
#
|
1015
|
-
#
|
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"
|
1016
1801
|
def row_sep
|
1017
1802
|
parser.row_separator
|
1018
1803
|
end
|
1019
1804
|
|
1805
|
+
# :call-seq:
|
1806
|
+
# csv.quote_char -> character
|
1020
1807
|
#
|
1021
|
-
#
|
1022
|
-
#
|
1023
|
-
#
|
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 # => "\""
|
1024
1811
|
def quote_char
|
1025
1812
|
parser.quote_character
|
1026
1813
|
end
|
1027
1814
|
|
1815
|
+
# :call-seq:
|
1816
|
+
# csv.field_size_limit -> integer or nil
|
1028
1817
|
#
|
1029
|
-
#
|
1030
|
-
#
|
1031
|
-
#
|
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
|
1032
1821
|
def field_size_limit
|
1033
1822
|
parser.field_size_limit
|
1034
1823
|
end
|
1035
1824
|
|
1825
|
+
# :call-seq:
|
1826
|
+
# csv.skip_lines -> regexp or nil
|
1036
1827
|
#
|
1037
|
-
#
|
1038
|
-
#
|
1039
|
-
#
|
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
|
1040
1831
|
def skip_lines
|
1041
1832
|
parser.skip_lines
|
1042
1833
|
end
|
1043
1834
|
|
1044
|
-
#
|
1045
|
-
#
|
1046
|
-
#
|
1047
|
-
#
|
1048
|
-
#
|
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
|
1049
1846
|
def converters
|
1050
1847
|
parser_fields_converter.map do |converter|
|
1051
1848
|
name = Converters.rassoc(converter)
|
@@ -1053,19 +1850,23 @@ class CSV
|
|
1053
1850
|
end
|
1054
1851
|
end
|
1055
1852
|
|
1853
|
+
# :call-seq:
|
1854
|
+
# csv.unconverted_fields? -> object
|
1056
1855
|
#
|
1057
|
-
# Returns
|
1058
|
-
#
|
1059
|
-
#
|
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
|
1060
1860
|
def unconverted_fields?
|
1061
1861
|
parser.unconverted_fields?
|
1062
1862
|
end
|
1063
1863
|
|
1864
|
+
# :call-seq:
|
1865
|
+
# csv.headers -> object
|
1064
1866
|
#
|
1065
|
-
# Returns
|
1066
|
-
#
|
1067
|
-
#
|
1068
|
-
#
|
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
|
1069
1870
|
def headers
|
1070
1871
|
if @writer
|
1071
1872
|
@writer.headers
|
@@ -1077,27 +1878,33 @@ class CSV
|
|
1077
1878
|
raw_headers
|
1078
1879
|
end
|
1079
1880
|
end
|
1881
|
+
|
1882
|
+
# :call-seq:
|
1883
|
+
# csv.return_headers? -> true or false
|
1080
1884
|
#
|
1081
|
-
# Returns
|
1082
|
-
#
|
1083
|
-
#
|
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
|
1084
1888
|
def return_headers?
|
1085
1889
|
parser.return_headers?
|
1086
1890
|
end
|
1087
1891
|
|
1892
|
+
# :call-seq:
|
1893
|
+
# csv.write_headers? -> true or false
|
1088
1894
|
#
|
1089
|
-
# Returns
|
1090
|
-
#
|
1091
|
-
#
|
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
|
1092
1898
|
def write_headers?
|
1093
1899
|
@writer_options[:write_headers]
|
1094
1900
|
end
|
1095
1901
|
|
1902
|
+
# :call-seq:
|
1903
|
+
# csv.header_converters -> array
|
1096
1904
|
#
|
1097
|
-
# Returns
|
1098
|
-
#
|
1099
|
-
#
|
1100
|
-
#
|
1905
|
+
# Returns an \Array containing header converters; used for parsing;
|
1906
|
+
# see {Header Converters}[#class-CSV-label-Header+Converters]:
|
1907
|
+
# CSV.new('').header_converters # => []
|
1101
1908
|
def header_converters
|
1102
1909
|
header_fields_converter.map do |converter|
|
1103
1910
|
name = HeaderConverters.rassoc(converter)
|
@@ -1105,34 +1912,74 @@ class CSV
|
|
1105
1912
|
end
|
1106
1913
|
end
|
1107
1914
|
|
1915
|
+
# :call-seq:
|
1916
|
+
# csv.skip_blanks? -> true or false
|
1108
1917
|
#
|
1109
|
-
# Returns
|
1110
|
-
#
|
1111
|
-
#
|
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
|
1112
1921
|
def skip_blanks?
|
1113
1922
|
parser.skip_blanks?
|
1114
1923
|
end
|
1115
1924
|
|
1116
|
-
#
|
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
|
1117
1932
|
def force_quotes?
|
1118
1933
|
@writer_options[:force_quotes]
|
1119
1934
|
end
|
1120
1935
|
|
1121
|
-
#
|
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
|
1122
1942
|
def liberal_parsing?
|
1123
1943
|
parser.liberal_parsing?
|
1124
1944
|
end
|
1125
1945
|
|
1946
|
+
# :call-seq:
|
1947
|
+
# csv.encoding -> endcoding
|
1126
1948
|
#
|
1127
|
-
#
|
1128
|
-
#
|
1129
|
-
#
|
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>
|
1130
1952
|
attr_reader :encoding
|
1131
1953
|
|
1132
|
-
#
|
1133
|
-
#
|
1134
|
-
#
|
1135
|
-
#
|
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
|
1136
1983
|
def lineno
|
1137
1984
|
if @writer
|
1138
1985
|
@writer.lineno
|
@@ -1141,9 +1988,22 @@ class CSV
|
|
1141
1988
|
end
|
1142
1989
|
end
|
1143
1990
|
|
1144
|
-
#
|
1145
|
-
#
|
1146
|
-
#
|
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"]
|
1147
2007
|
def line
|
1148
2008
|
parser.line
|
1149
2009
|
end
|
@@ -1219,13 +2079,56 @@ class CSV
|
|
1219
2079
|
|
1220
2080
|
### End Delegation ###
|
1221
2081
|
|
1222
|
-
#
|
1223
|
-
#
|
1224
|
-
#
|
1225
|
-
#
|
1226
|
-
#
|
1227
|
-
#
|
1228
|
-
#
|
2082
|
+
# :call-seq:
|
2083
|
+
# csv << row -> self
|
2084
|
+
#
|
2085
|
+
# Appends a row to +self+.
|
2086
|
+
#
|
2087
|
+
# - Argument +row+ must be an \Array object or a CSV::Row object.
|
2088
|
+
# - The output stream must be open for writing.
|
2089
|
+
#
|
2090
|
+
# ---
|
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 opened 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
|
1229
2132
|
def <<(row)
|
1230
2133
|
writer << row
|
1231
2134
|
self
|
@@ -1233,58 +2136,216 @@ class CSV
|
|
1233
2136
|
alias_method :add_row, :<<
|
1234
2137
|
alias_method :puts, :<<
|
1235
2138
|
|
1236
|
-
#
|
1237
2139
|
# :call-seq:
|
1238
|
-
# convert(
|
1239
|
-
# convert {
|
1240
|
-
#
|
1241
|
-
#
|
1242
|
-
#
|
1243
|
-
#
|
1244
|
-
#
|
1245
|
-
#
|
1246
|
-
#
|
1247
|
-
#
|
1248
|
-
#
|
1249
|
-
#
|
1250
|
-
#
|
2140
|
+
# convert(converter_name) -> array_of_procs
|
2141
|
+
# convert {|field, field_info| ... } -> array_of_procs
|
2142
|
+
#
|
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
|
+
# ---
|
2152
|
+
#
|
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]
|
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
|
1251
2203
|
def convert(name = nil, &converter)
|
1252
2204
|
parser_fields_converter.add_converter(name, &converter)
|
1253
2205
|
end
|
1254
2206
|
|
1255
|
-
#
|
1256
2207
|
# :call-seq:
|
1257
|
-
# header_convert(
|
1258
|
-
# header_convert { |
|
1259
|
-
#
|
1260
|
-
#
|
1261
|
-
#
|
1262
|
-
#
|
1263
|
-
#
|
1264
|
-
#
|
1265
|
-
#
|
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.
|
2214
|
+
#
|
2215
|
+
# - Argument +converter_name+, if given, should be the name
|
2216
|
+
# of an existing header converter.
|
2217
|
+
#
|
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
|
+
# ---
|
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
|
1266
2269
|
def header_convert(name = nil, &converter)
|
1267
2270
|
header_fields_converter.add_converter(name, &converter)
|
1268
2271
|
end
|
1269
2272
|
|
1270
2273
|
include Enumerable
|
1271
2274
|
|
1272
|
-
#
|
1273
|
-
#
|
1274
|
-
#
|
1275
|
-
#
|
1276
|
-
#
|
1277
|
-
# The data source must be
|
1278
|
-
#
|
2275
|
+
# :call-seq:
|
2276
|
+
# csv.each -> enumerator
|
2277
|
+
# csv.each {|row| ...}
|
2278
|
+
#
|
2279
|
+
# Calls the block with each successive row.
|
2280
|
+
# The data source must be opened for reading.
|
2281
|
+
#
|
2282
|
+
# Without headers:
|
2283
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2284
|
+
# csv = CSV.new(string)
|
2285
|
+
# csv.each do |row|
|
2286
|
+
# p row
|
2287
|
+
# end
|
2288
|
+
# Output:
|
2289
|
+
# ["foo", "0"]
|
2290
|
+
# ["bar", "1"]
|
2291
|
+
# ["baz", "2"]
|
2292
|
+
#
|
2293
|
+
# With headers:
|
2294
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
2295
|
+
# csv = CSV.new(string, headers: true)
|
2296
|
+
# csv.each do |row|
|
2297
|
+
# p row
|
2298
|
+
# end
|
2299
|
+
# Output:
|
2300
|
+
# <CSV::Row "Name":"foo" "Value":"0">
|
2301
|
+
# <CSV::Row "Name":"bar" "Value":"1">
|
2302
|
+
# <CSV::Row "Name":"baz" "Value":"2">
|
2303
|
+
#
|
2304
|
+
# ---
|
2305
|
+
#
|
2306
|
+
# Raises an exception if the source is not opened for reading:
|
2307
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2308
|
+
# csv = CSV.new(string)
|
2309
|
+
# csv.close
|
2310
|
+
# # Raises IOError (not opened for reading)
|
2311
|
+
# csv.each do |row|
|
2312
|
+
# p row
|
2313
|
+
# end
|
1279
2314
|
def each(&block)
|
1280
2315
|
parser_enumerator.each(&block)
|
1281
2316
|
end
|
1282
2317
|
|
1283
|
-
#
|
1284
|
-
#
|
1285
|
-
#
|
1286
|
-
#
|
1287
|
-
#
|
2318
|
+
# :call-seq:
|
2319
|
+
# csv.read -> array or csv_table
|
2320
|
+
#
|
2321
|
+
# Forms the remaining rows from +self+ into:
|
2322
|
+
# - A CSV::Table object, if headers are in use.
|
2323
|
+
# - An \Array of Arrays, otherwise.
|
2324
|
+
#
|
2325
|
+
# The data source must be opened for reading.
|
2326
|
+
#
|
2327
|
+
# Without headers:
|
2328
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2329
|
+
# path = 't.csv'
|
2330
|
+
# File.write(path, string)
|
2331
|
+
# csv = CSV.open(path)
|
2332
|
+
# csv.read # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
2333
|
+
#
|
2334
|
+
# With headers:
|
2335
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
2336
|
+
# path = 't.csv'
|
2337
|
+
# File.write(path, string)
|
2338
|
+
# csv = CSV.open(path, headers: true)
|
2339
|
+
# csv.read # => #<CSV::Table mode:col_or_row row_count:4>
|
2340
|
+
#
|
2341
|
+
# ---
|
2342
|
+
#
|
2343
|
+
# Raises an exception if the source is not opened for reading:
|
2344
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2345
|
+
# csv = CSV.new(string)
|
2346
|
+
# csv.close
|
2347
|
+
# # Raises IOError (not opened for reading)
|
2348
|
+
# csv.read
|
1288
2349
|
def read
|
1289
2350
|
rows = to_a
|
1290
2351
|
if parser.use_headers?
|
@@ -1295,18 +2356,69 @@ class CSV
|
|
1295
2356
|
end
|
1296
2357
|
alias_method :readlines, :read
|
1297
2358
|
|
1298
|
-
#
|
2359
|
+
# :call-seq:
|
2360
|
+
# csv.header_row? -> true or false
|
2361
|
+
#
|
2362
|
+
# Returns +true+ if the next row to be read is a header row\;
|
2363
|
+
# +false+ otherwise.
|
2364
|
+
#
|
2365
|
+
# Without headers:
|
2366
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2367
|
+
# csv = CSV.new(string)
|
2368
|
+
# csv.header_row? # => false
|
2369
|
+
#
|
2370
|
+
# With headers:
|
2371
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
2372
|
+
# csv = CSV.new(string, headers: true)
|
2373
|
+
# csv.header_row? # => true
|
2374
|
+
# csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
|
2375
|
+
# csv.header_row? # => false
|
2376
|
+
#
|
2377
|
+
# ---
|
2378
|
+
#
|
2379
|
+
# Raises an exception if the source is not opened for reading:
|
2380
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2381
|
+
# csv = CSV.new(string)
|
2382
|
+
# csv.close
|
2383
|
+
# # Raises IOError (not opened for reading)
|
2384
|
+
# csv.header_row?
|
1299
2385
|
def header_row?
|
1300
2386
|
parser.header_row?
|
1301
2387
|
end
|
1302
2388
|
|
1303
|
-
#
|
1304
|
-
#
|
1305
|
-
#
|
1306
|
-
#
|
1307
|
-
#
|
1308
|
-
#
|
1309
|
-
#
|
2389
|
+
# :call-seq:
|
2390
|
+
# csv.shift -> array, csv_row, or nil
|
2391
|
+
#
|
2392
|
+
# Returns the next row of data as:
|
2393
|
+
# - An \Array if no headers are used.
|
2394
|
+
# - A CSV::Row object if headers are used.
|
2395
|
+
#
|
2396
|
+
# The data source must be opened for reading.
|
2397
|
+
#
|
2398
|
+
# Without headers:
|
2399
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2400
|
+
# csv = CSV.new(string)
|
2401
|
+
# csv.shift # => ["foo", "0"]
|
2402
|
+
# csv.shift # => ["bar", "1"]
|
2403
|
+
# csv.shift # => ["baz", "2"]
|
2404
|
+
# csv.shift # => nil
|
2405
|
+
#
|
2406
|
+
# With headers:
|
2407
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
2408
|
+
# csv = CSV.new(string, headers: true)
|
2409
|
+
# csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
|
2410
|
+
# csv.shift # => #<CSV::Row "Name":"bar" "Value":"1">
|
2411
|
+
# csv.shift # => #<CSV::Row "Name":"baz" "Value":"2">
|
2412
|
+
# csv.shift # => nil
|
2413
|
+
#
|
2414
|
+
# ---
|
2415
|
+
#
|
2416
|
+
# Raises an exception if the source is not opened for reading:
|
2417
|
+
# string = "foo,0\nbar,1\nbaz,2\n"
|
2418
|
+
# csv = CSV.new(string)
|
2419
|
+
# csv.close
|
2420
|
+
# # Raises IOError (not opened for reading)
|
2421
|
+
# csv.shift
|
1310
2422
|
def shift
|
1311
2423
|
if @eof_error
|
1312
2424
|
eof_error, @eof_error = @eof_error, nil
|
@@ -1321,12 +2433,16 @@ class CSV
|
|
1321
2433
|
alias_method :gets, :shift
|
1322
2434
|
alias_method :readline, :shift
|
1323
2435
|
|
2436
|
+
# :call-seq:
|
2437
|
+
# csv.inspect -> string
|
1324
2438
|
#
|
1325
|
-
# Returns a
|
1326
|
-
#
|
1327
|
-
#
|
2439
|
+
# Returns a \String showing certain properties of +self+:
|
2440
|
+
# string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
2441
|
+
# csv = CSV.new(string, headers: true)
|
2442
|
+
# s = csv.inspect
|
2443
|
+
# s # => "#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:\",\" row_sep:\"\\n\" quote_char:\"\\\"\" headers:true>"
|
1328
2444
|
def inspect
|
1329
|
-
str = ["
|
2445
|
+
str = ["#<", self.class.to_s, " io_type:"]
|
1330
2446
|
# show type of wrapped IO
|
1331
2447
|
if @io == $stdout then str << "$stdout"
|
1332
2448
|
elsif @io == $stdin then str << "$stdin"
|