csv 3.1.5 → 3.1.6

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