csv 3.3.6 → 3.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/NEWS.md +19 -0
- data/doc/csv/options/common/quote_char.rdoc +1 -1
- data/doc/csv/options/generating/write_headers.rdoc +1 -1
- data/doc/csv/options/parsing/headers.rdoc +5 -4
- data/doc/csv/options/parsing/skip_blanks.rdoc +1 -1
- data/doc/csv/options/parsing/skip_lines.rdoc +4 -2
- data/doc/csv/recipes/generating.rdoc +7 -7
- data/lib/csv/parser.rb +52 -46
- data/lib/csv/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 405d44be299fb5493de237683e24cdbf6e29982e4378c1d01a82e2ad4bbb21a3
|
|
4
|
+
data.tar.gz: 28a53de42e2c35091f441a3d9a7a886afc30bad1c514ea26715d633ee8a00080
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7cb81c8caf90a7673fd22fc24644b33fb171c9f7b60ea9e679612008912d7ae10150351001d2daea5afdc6360cbe486f454cc3b29387b9c514d232b0c68b5ad3
|
|
7
|
+
data.tar.gz: 72d77e22c654b26dc8fed349670d107169605119554f6b18cd79f7477bd7284f099560edc68bfba1561775fe8acb081fd1427aa6235c8540bc7cca93a7e7a397
|
data/NEWS.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# News
|
|
2
2
|
|
|
3
|
+
## 3.3.7 - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Improvements
|
|
6
|
+
|
|
7
|
+
* Improved documents.
|
|
8
|
+
* GH-376
|
|
9
|
+
* Patch by Oskar Eichler
|
|
10
|
+
|
|
11
|
+
* Improved error handling.
|
|
12
|
+
* GH-378
|
|
13
|
+
* GH-379
|
|
14
|
+
* Reported by sswander
|
|
15
|
+
|
|
16
|
+
### Thanks
|
|
17
|
+
|
|
18
|
+
* Oskar Eichler
|
|
19
|
+
|
|
20
|
+
* sswander
|
|
21
|
+
|
|
3
22
|
## 3.3.6 - 2026-07-27
|
|
4
23
|
|
|
5
24
|
### Improvements
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
====== Option +quote_char+
|
|
2
2
|
|
|
3
|
-
Specifies the character (\String of length 1) used
|
|
3
|
+
Specifies the character (\String of length 1) used to quote fields
|
|
4
4
|
in both parsing and generating.
|
|
5
5
|
This String will be transcoded into the data's \Encoding before use.
|
|
6
6
|
|
|
@@ -31,9 +31,10 @@ the first row of the data is treated as a row of headers:
|
|
|
31
31
|
bax,2
|
|
32
32
|
EOT
|
|
33
33
|
csv = CSV.new(str, headers: true)
|
|
34
|
-
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:
|
|
34
|
+
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"" headers:true>
|
|
35
|
+
csv.headers # => true
|
|
36
|
+
csv.shift # => #<CSV::Row "Name":"foo" "Count":"0">
|
|
35
37
|
csv.headers # => ["Name", "Count"]
|
|
36
|
-
csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
|
|
37
38
|
|
|
38
39
|
---
|
|
39
40
|
|
|
@@ -46,7 +47,7 @@ If set to an \Array, the \Array elements are treated as headers:
|
|
|
46
47
|
csv = CSV.new(str, headers: ['Name', 'Count'])
|
|
47
48
|
csv
|
|
48
49
|
csv.headers # => ["Name", "Count"]
|
|
49
|
-
csv.shift # => #<CSV::Row "Name":"
|
|
50
|
+
csv.shift # => #<CSV::Row "Name":"foo" "Count":"0">
|
|
50
51
|
|
|
51
52
|
---
|
|
52
53
|
|
|
@@ -60,4 +61,4 @@ with the current +options+, and the returned \Array is treated as headers:
|
|
|
60
61
|
csv = CSV.new(str, headers: 'Name,Count')
|
|
61
62
|
csv
|
|
62
63
|
csv.headers # => ["Name", "Count"]
|
|
63
|
-
csv.shift # => #<CSV::Row "Name":"
|
|
64
|
+
csv.shift # => #<CSV::Row "Name":"foo" "Count":"0">
|
|
@@ -6,7 +6,7 @@ a line that contains a column separator is not considered to be blank.
|
|
|
6
6
|
Default value:
|
|
7
7
|
CSV::DEFAULT_OPTIONS.fetch(:skip_blanks) # => false
|
|
8
8
|
|
|
9
|
-
See also option {
|
|
9
|
+
See also option {skip_lines}[#class-CSV-label-Option+skip_lines].
|
|
10
10
|
|
|
11
11
|
For examples in this section:
|
|
12
12
|
str = <<-EOT
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Specifies an object to use in identifying comment lines in the input that are to be ignored:
|
|
4
4
|
* If a \Regexp, ignores lines that match it.
|
|
5
|
-
* If a \String,
|
|
5
|
+
* If a \String, ignores lines that include it.
|
|
6
6
|
* If +nil+, no lines are considered to be comments.
|
|
7
|
+
* Otherwise, the object must respond to +match+; ignores lines for which +match+ returns a truthy value.
|
|
7
8
|
|
|
8
9
|
Default value:
|
|
9
10
|
CSV::DEFAULT_OPTIONS.fetch(:skip_lines) # => nil
|
|
@@ -32,6 +33,7 @@ Using a \String:
|
|
|
32
33
|
|
|
33
34
|
---
|
|
34
35
|
|
|
35
|
-
Raises an exception if given an object that is not a \Regexp, a \String,
|
|
36
|
+
Raises an exception if given an object that is not a \Regexp, a \String, +nil+,
|
|
37
|
+
or an object that responds to +match+:
|
|
36
38
|
# Raises ArgumentError (:skip_lines has to respond to #match: 0)
|
|
37
39
|
CSV.parse(str, skip_lines: 0)
|
|
@@ -16,7 +16,7 @@ All code snippets on this page assume that the following has been executed:
|
|
|
16
16
|
- {Generating to a File}[#label-Generating+to+a+File]
|
|
17
17
|
- {Recipe: Generate to File with Headers}[#label-Recipe-3A+Generate+to+File+with+Headers]
|
|
18
18
|
- {Recipe: Generate to File Without Headers}[#label-Recipe-3A+Generate+to+File+Without+Headers]
|
|
19
|
-
- {Generating to IO
|
|
19
|
+
- {Generating to an IO Stream}[#label-Generating+to+an+IO+Stream]
|
|
20
20
|
- {Recipe: Generate to IO Stream with Headers}[#label-Recipe-3A+Generate+to+IO+Stream+with+Headers]
|
|
21
21
|
- {Recipe: Generate to IO Stream Without Headers}[#label-Recipe-3A+Generate+to+IO+Stream+Without+Headers]
|
|
22
22
|
- {Converting Fields}[#label-Converting+Fields]
|
|
@@ -71,11 +71,11 @@ that are to be generated:
|
|
|
71
71
|
|
|
72
72
|
==== Generating to a \File
|
|
73
73
|
|
|
74
|
-
You can generate
|
|
74
|
+
You can generate \CSV data to a \File, with or without headers.
|
|
75
75
|
|
|
76
76
|
===== Recipe: Generate to \File with Headers
|
|
77
77
|
|
|
78
|
-
Use class method CSV.open with option +headers+ generate to a \File.
|
|
78
|
+
Use class method CSV.open with option +headers+ to generate to a \File.
|
|
79
79
|
|
|
80
80
|
This example uses method CSV#<< to append the rows
|
|
81
81
|
that are to be generated:
|
|
@@ -105,7 +105,7 @@ that are to be generated:
|
|
|
105
105
|
|
|
106
106
|
You can generate \CSV data to an \IO stream, with or without headers.
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
===== Recipe: Generate to \IO Stream with Headers
|
|
109
109
|
|
|
110
110
|
Use class method CSV.new with option +headers+ to generate \CSV data to an \IO stream:
|
|
111
111
|
path = 't.csv'
|
|
@@ -187,7 +187,7 @@ For strict compliance, use option +:row_sep+ to specify row separator <tt>"\r\n"
|
|
|
187
187
|
===== Recipe: Generate Non-Compliant Row Separator
|
|
188
188
|
|
|
189
189
|
For data with non-compliant row separators, use option +:row_sep+ with a different value:
|
|
190
|
-
This example source uses semicolon (<tt>";
|
|
190
|
+
This example source uses semicolon (<tt>";"</tt>) as its row separator:
|
|
191
191
|
output_string = CSV.generate('', row_sep: ";") do |csv|
|
|
192
192
|
csv << ['Foo', 0]
|
|
193
193
|
csv << ['Bar', 1]
|
|
@@ -223,11 +223,11 @@ This example source uses TAB (<tt>"\t"</tt>) as its column separator:
|
|
|
223
223
|
|
|
224
224
|
==== Quotes
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
RFC 4180 allows most fields to be quoted or not.
|
|
227
227
|
By default, \CSV does not quote most fields.
|
|
228
228
|
|
|
229
229
|
However, a field containing the current row separator, column separator,
|
|
230
|
-
or quote character is automatically quoted, producing
|
|
230
|
+
or quote character is automatically quoted, producing RFC 4180 compliance:
|
|
231
231
|
# Field contains row separator.
|
|
232
232
|
output_string = CSV.generate('') do |csv|
|
|
233
233
|
row_sep = csv.row_sep
|
data/lib/csv/parser.rb
CHANGED
|
@@ -978,63 +978,69 @@ class CSV
|
|
|
978
978
|
|
|
979
979
|
def parse_quotable_loose(&block)
|
|
980
980
|
@scanner.keep_start
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
@
|
|
985
|
-
next
|
|
986
|
-
end
|
|
987
|
-
original_line = line
|
|
988
|
-
line = line.delete_suffix(@row_separator)
|
|
989
|
-
|
|
990
|
-
if line.empty?
|
|
991
|
-
if @skip_blanks
|
|
981
|
+
kept = true
|
|
982
|
+
begin
|
|
983
|
+
@scanner.each_line(@row_separator) do |line|
|
|
984
|
+
if @skip_lines and skip_line?(line)
|
|
992
985
|
@scanner.keep_drop
|
|
993
986
|
@scanner.keep_start
|
|
994
987
|
next
|
|
995
988
|
end
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
989
|
+
original_line = line
|
|
990
|
+
line = line.delete_suffix(@row_separator)
|
|
991
|
+
|
|
992
|
+
if line.empty?
|
|
993
|
+
if @skip_blanks
|
|
994
|
+
@scanner.keep_drop
|
|
995
|
+
@scanner.keep_start
|
|
996
|
+
next
|
|
997
|
+
end
|
|
998
|
+
row = []
|
|
999
|
+
quoted_fields = FieldsConverter::NO_QUOTED_FIELDS
|
|
1000
|
+
elsif line.include?(@cr) or line.include?(@lf)
|
|
1001
|
+
kept = false
|
|
1002
|
+
@scanner.keep_back
|
|
1003
|
+
@parse_method = :parse_quotable_robust
|
|
1004
|
+
return parse_quotable_robust(&block)
|
|
1005
|
+
else
|
|
1006
|
+
row = line.split(@split_column_separator, -1)
|
|
1007
|
+
quoted_fields = []
|
|
1008
|
+
n_columns = row.size
|
|
1009
|
+
i = 0
|
|
1010
|
+
while i < n_columns
|
|
1011
|
+
column = row[i]
|
|
1012
|
+
if column.empty?
|
|
1015
1013
|
quoted_fields << false
|
|
1016
|
-
|
|
1017
|
-
elsif n_quotes == 2 and
|
|
1018
|
-
column.start_with?(@quote_character) and
|
|
1019
|
-
column.end_with?(@quote_character)
|
|
1020
|
-
quoted_fields << true
|
|
1021
|
-
row[i] = column[1..-2]
|
|
1014
|
+
row[i] = nil
|
|
1022
1015
|
else
|
|
1023
|
-
@
|
|
1024
|
-
|
|
1025
|
-
|
|
1016
|
+
n_quotes = column.count(@quote_character)
|
|
1017
|
+
if n_quotes.zero?
|
|
1018
|
+
quoted_fields << false
|
|
1019
|
+
# no quote
|
|
1020
|
+
elsif n_quotes == 2 and
|
|
1021
|
+
column.start_with?(@quote_character) and
|
|
1022
|
+
column.end_with?(@quote_character)
|
|
1023
|
+
quoted_fields << true
|
|
1024
|
+
row[i] = column[1..-2]
|
|
1025
|
+
else
|
|
1026
|
+
kept = false
|
|
1027
|
+
@scanner.keep_back
|
|
1028
|
+
@parse_method = :parse_quotable_robust
|
|
1029
|
+
return parse_quotable_robust(&block)
|
|
1030
|
+
end
|
|
1031
|
+
validate_field_size(row[i])
|
|
1026
1032
|
end
|
|
1027
|
-
|
|
1033
|
+
i += 1
|
|
1028
1034
|
end
|
|
1029
|
-
i += 1
|
|
1030
1035
|
end
|
|
1036
|
+
@scanner.keep_drop
|
|
1037
|
+
@scanner.keep_start
|
|
1038
|
+
@last_line = original_line
|
|
1039
|
+
emit_row(row, quoted_fields, &block)
|
|
1031
1040
|
end
|
|
1032
|
-
|
|
1033
|
-
@scanner.
|
|
1034
|
-
@last_line = original_line
|
|
1035
|
-
emit_row(row, quoted_fields, &block)
|
|
1041
|
+
ensure
|
|
1042
|
+
@scanner.keep_drop if kept
|
|
1036
1043
|
end
|
|
1037
|
-
@scanner.keep_drop
|
|
1038
1044
|
end
|
|
1039
1045
|
|
|
1040
1046
|
def parse_quotable_robust(&block)
|
data/lib/csv/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: csv
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.3.
|
|
4
|
+
version: 3.3.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Edward Gray II
|
|
@@ -71,7 +71,7 @@ licenses:
|
|
|
71
71
|
- Ruby
|
|
72
72
|
- BSD-2-Clause
|
|
73
73
|
metadata:
|
|
74
|
-
changelog_uri: https://github.com/ruby/csv/releases/tag/v3.3.
|
|
74
|
+
changelog_uri: https://github.com/ruby/csv/releases/tag/v3.3.7
|
|
75
75
|
rdoc_options:
|
|
76
76
|
- "--main"
|
|
77
77
|
- README.md
|
|
@@ -88,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
89
|
version: '0'
|
|
90
90
|
requirements: []
|
|
91
|
-
rubygems_version: 4.0.
|
|
91
|
+
rubygems_version: 4.0.20
|
|
92
92
|
specification_version: 4
|
|
93
93
|
summary: CSV Reading and Writing
|
|
94
94
|
test_files: []
|