Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK! K'tWtWrecipes/parsing.rdocnu[== Recipes for Parsing \CSV These recipes are specific code examples for specific \CSV parsing tasks. For other recipes, see {Recipes for CSV}[./recipes_rdoc.html]. All code snippets on this page assume that the following has been executed: require 'csv' === Contents - {Source Formats}[#label-Source+Formats] - {Parsing from a String}[#label-Parsing+from+a+String] - {Recipe: Parse from String with Headers}[#label-Recipe-3A+Parse+from+String+with+Headers] - {Recipe: Parse from String Without Headers}[#label-Recipe-3A+Parse+from+String+Without+Headers] - {Parsing from a File}[#label-Parsing+from+a+File] - {Recipe: Parse from File with Headers}[#label-Recipe-3A+Parse+from+File+with+Headers] - {Recipe: Parse from File Without Headers}[#label-Recipe-3A+Parse+from+File+Without+Headers] - {Parsing from an IO Stream}[#label-Parsing+from+an+IO+Stream] - {Recipe: Parse from IO Stream with Headers}[#label-Recipe-3A+Parse+from+IO+Stream+with+Headers] - {Recipe: Parse from IO Stream Without Headers}[#label-Recipe-3A+Parse+from+IO+Stream+Without+Headers] - {RFC 4180 Compliance}[#label-RFC+4180+Compliance] - {Row Separator}[#label-Row+Separator] - {Recipe: Handle Compliant Row Separator}[#label-Recipe-3A+Handle+Compliant+Row+Separator] - {Recipe: Handle Non-Compliant Row Separator}[#label-Recipe-3A+Handle+Non-Compliant+Row+Separator] - {Column Separator}[#label-Column+Separator] - {Recipe: Handle Compliant Column Separator}[#label-Recipe-3A+Handle+Compliant+Column+Separator] - {Recipe: Handle Non-Compliant Column Separator}[#label-Recipe-3A+Handle+Non-Compliant+Column+Separator] - {Quote Character}[#label-Quote+Character] - {Recipe: Handle Compliant Quote Character}[#label-Recipe-3A+Handle+Compliant+Quote+Character] - {Recipe: Handle Non-Compliant Quote Character}[#label-Recipe-3A+Handle+Non-Compliant+Quote+Character] - {Recipe: Allow Liberal Parsing}[#label-Recipe-3A+Allow+Liberal+Parsing] - {Special Handling}[#label-Special+Handling] - {Special Line Handling}[#label-Special+Line+Handling] - {Recipe: Ignore Blank Lines}[#label-Recipe-3A+Ignore+Blank+Lines] - {Recipe: Ignore Selected Lines}[#label-Recipe-3A+Ignore+Selected+Lines] - {Special Field Handling}[#label-Special+Field+Handling] - {Recipe: Strip Fields}[#label-Recipe-3A+Strip+Fields] - {Recipe: Handle Null Fields}[#label-Recipe-3A+Handle+Null+Fields] - {Recipe: Handle Empty Fields}[#label-Recipe-3A+Handle+Empty+Fields] - {Converting Fields}[#label-Converting+Fields] - {Converting Fields to Objects}[#label-Converting+Fields+to+Objects] - {Recipe: Convert Fields to Integers}[#label-Recipe-3A+Convert+Fields+to+Integers] - {Recipe: Convert Fields to Floats}[#label-Recipe-3A+Convert+Fields+to+Floats] - {Recipe: Convert Fields to Numerics}[#label-Recipe-3A+Convert+Fields+to+Numerics] - {Recipe: Convert Fields to Dates}[#label-Recipe-3A+Convert+Fields+to+Dates] - {Recipe: Convert Fields to DateTimes}[#label-Recipe-3A+Convert+Fields+to+DateTimes] - {Recipe: Convert Fields to Times}[#label-Recipe-3A+Convert+Fields+to+Times] - {Recipe: Convert Assorted Fields to Objects}[#label-Recipe-3A+Convert+Assorted+Fields+to+Objects] - {Recipe: Convert Fields to Other Objects}[#label-Recipe-3A+Convert+Fields+to+Other+Objects] - {Recipe: Filter Field Strings}[#label-Recipe-3A+Filter+Field+Strings] - {Recipe: Register Field Converters}[#label-Recipe-3A+Register+Field+Converters] - {Using Multiple Field Converters}[#label-Using+Multiple+Field+Converters] - {Recipe: Specify Multiple Field Converters in Option :converters}[#label-Recipe-3A+Specify+Multiple+Field+Converters+in+Option+-3Aconverters] - {Recipe: Specify Multiple Field Converters in a Custom Converter List}[#label-Recipe-3A+Specify+Multiple+Field+Converters+in+a+Custom+Converter+List] - {Converting Headers}[#label-Converting+Headers] - {Recipe: Convert Headers to Lowercase}[#label-Recipe-3A+Convert+Headers+to+Lowercase] - {Recipe: Convert Headers to Symbols}[#label-Recipe-3A+Convert+Headers+to+Symbols] - {Recipe: Filter Header Strings}[#label-Recipe-3A+Filter+Header+Strings] - {Recipe: Register Header Converters}[#label-Recipe-3A+Register+Header+Converters] - {Using Multiple Header Converters}[#label-Using+Multiple+Header+Converters] - {Recipe: Specify Multiple Header Converters in Option :header_converters}[#label-Recipe-3A+Specify+Multiple+Header+Converters+in+Option+-3Aheader_converters] - {Recipe: Specify Multiple Header Converters in a Custom Header Converter List}[#label-Recipe-3A+Specify+Multiple+Header+Converters+in+a+Custom+Header+Converter+List] - {Diagnostics}[#label-Diagnostics] - {Recipe: Capture Unconverted Fields}[#label-Recipe-3A+Capture+Unconverted+Fields] - {Recipe: Capture Field Info}[#label-Recipe-3A+Capture+Field+Info] === Source Formats You can parse \CSV data from a \String, from a \File (via its path), or from an \IO stream. ==== Parsing from a \String You can parse \CSV data from a \String, with or without headers. ===== Recipe: Parse from \String with Headers Use class method CSV.parse with option +headers+ to read a source \String all at once (may have memory resource implications): string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" CSV.parse(string, headers: true) # => # Use instance method CSV#each with option +headers+ to read a source \String one row at a time: CSV.new(string, headers: true).each do |row| p row end Output: # # # ===== Recipe: Parse from \String Without Headers Use class method CSV.parse without option +headers+ to read a source \String all at once (may have memory resource implications): string = "foo,0\nbar,1\nbaz,2\n" CSV.parse(string) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Use instance method CSV#each without option +headers+ to read a source \String one row at a time: CSV.new(string).each do |row| p row end Output: ["foo", "0"] ["bar", "1"] ["baz", "2"] ==== Parsing from a \File You can parse \CSV data from a \File, with or without headers. ===== Recipe: Parse from \File with Headers Use class method CSV.read with option +headers+ to read a file all at once: string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.read(path, headers: true) # => # Use class method CSV.foreach with option +headers+ to read one row at a time: CSV.foreach(path, headers: true) do |row| p row end Output: # # # ===== Recipe: Parse from \File Without Headers Use class method CSV.read without option +headers+ to read a file all at once: string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Use class method CSV.foreach without option +headers+ to read one row at a time: CSV.foreach(path) do |row| p row end Output: ["foo", "0"] ["bar", "1"] ["baz", "2"] ==== Parsing from an \IO Stream You can parse \CSV data from an \IO stream, with or without headers. ===== Recipe: Parse from \IO Stream with Headers Use class method CSV.parse with option +headers+ to read an \IO stream all at once: string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) File.open(path) do |file| CSV.parse(file, headers: true) end # => # Use class method CSV.foreach with option +headers+ to read one row at a time: File.open(path) do |file| CSV.foreach(file, headers: true) do |row| p row end end Output: # # # ===== Recipe: Parse from \IO Stream Without Headers Use class method CSV.parse without option +headers+ to read an \IO stream all at once: string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) File.open(path) do |file| CSV.parse(file) end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Use class method CSV.foreach without option +headers+ to read one row at a time: File.open(path) do |file| CSV.foreach(file) do |row| p row end end Output: ["foo", "0"] ["bar", "1"] ["baz", "2"] === RFC 4180 Compliance By default, \CSV parses data that is compliant with {RFC 4180}[https://www.rfc-editor.org/rfc/rfc4180] with respect to: - Row separator. - Column separator. - Quote character. ==== Row Separator RFC 4180 specifies the row separator CRLF (Ruby "\r\n"). Although the \CSV default row separator is "\n", the parser also by default handles row separator "\r" and the RFC-compliant "\r\n". ===== Recipe: Handle Compliant Row Separator For strict compliance, use option +:row_sep+ to specify row separator "\r\n", which allows the compliant row separator: source = "foo,1\r\nbar,1\r\nbaz,2\r\n" CSV.parse(source, row_sep: "\r\n") # => [["foo", "1"], ["bar", "1"], ["baz", "2"]] But rejects other row separators: source = "foo,1\nbar,1\nbaz,2\n" CSV.parse(source, row_sep: "\r\n") # Raised MalformedCSVError source = "foo,1\rbar,1\rbaz,2\r" CSV.parse(source, row_sep: "\r\n") # Raised MalformedCSVError source = "foo,1\n\rbar,1\n\rbaz,2\n\r" CSV.parse(source, row_sep: "\r\n") # Raised MalformedCSVError ===== Recipe: Handle Non-Compliant Row Separator For data with non-compliant row separators, use option +:row_sep+. This example source uses semicolon (";") as its row separator: source = "foo,1;bar,1;baz,2;" CSV.parse(source, row_sep: ';') # => [["foo", "1"], ["bar", "1"], ["baz", "2"]] ==== Column Separator RFC 4180 specifies column separator COMMA (Ruby ","). ===== Recipe: Handle Compliant Column Separator Because the \CSV default comma separator is ',', you need not specify option +:col_sep+ for compliant data: source = "foo,1\nbar,1\nbaz,2\n" CSV.parse(source) # => [["foo", "1"], ["bar", "1"], ["baz", "2"]] ===== Recipe: Handle Non-Compliant Column Separator For data with non-compliant column separators, use option +:col_sep+. This example source uses TAB ("\t") as its column separator: source = "foo,1\tbar,1\tbaz,2" CSV.parse(source, col_sep: "\t") # => [["foo", "1"], ["bar", "1"], ["baz", "2"]] ==== Quote Character RFC 4180 specifies quote character DQUOTE (Ruby "\""). ===== Recipe: Handle Compliant Quote Character Because the \CSV default quote character is "\"", you need not specify option +:quote_char+ for compliant data: source = "\"foo\",\"1\"\n\"bar\",\"1\"\n\"baz\",\"2\"\n" CSV.parse(source) # => [["foo", "1"], ["bar", "1"], ["baz", "2"]] ===== Recipe: Handle Non-Compliant Quote Character For data with non-compliant quote characters, use option +:quote_char+. This example source uses SQUOTE ("'") as its quote character: source = "'foo','1'\n'bar','1'\n'baz','2'\n" CSV.parse(source, quote_char: "'") # => [["foo", "1"], ["bar", "1"], ["baz", "2"]] ==== Recipe: Allow Liberal Parsing Use option +:liberal_parsing+ to specify that \CSV should attempt to parse input not conformant with RFC 4180, such as double quotes in unquoted fields: source = 'is,this "three, or four",fields' CSV.parse(source) # Raises MalformedCSVError CSV.parse(source, liberal_parsing: true) # => [["is", "this \"three", " or four\"", "fields"]] === Special Handling You can use parsing options to specify special handling for certain lines and fields. ==== Special Line Handling Use parsing options to specify special handling for blank lines, or for other selected lines. ===== Recipe: Ignore Blank Lines Use option +:skip_blanks+ to ignore blank lines: source = <<-EOT foo,0 bar,1 baz,2 , EOT parsed = CSV.parse(source, skip_blanks: true) parsed # => [["foo", "0"], ["bar", "1"], ["baz", "2"], [nil, nil]] ===== Recipe: Ignore Selected Lines Use option +:skip_lines+ to ignore selected lines. source = <<-EOT # Comment foo,0 bar,1 baz,2 # Another comment EOT parsed = CSV.parse(source, skip_lines: /^#/) parsed # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] ==== Special Field Handling Use parsing options to specify special handling for certain field values. ===== Recipe: Strip Fields Use option +:strip+ to strip parsed field values: CSV.parse_line(' a , b ', strip: true) # => ["a", "b"] ===== Recipe: Handle Null Fields Use option +:nil_value+ to specify a value that will replace each field that is null (no text): CSV.parse_line('a,,b,,c', nil_value: 0) # => ["a", 0, "b", 0, "c"] ===== Recipe: Handle Empty Fields Use option +:empty_value+ to specify a value that will replace each field that is empty (\String of length 0); CSV.parse_line('a,"",b,"",c', empty_value: 'x') # => ["a", "x", "b", "x", "c"] === Converting Fields You can use field converters to change parsed \String fields into other objects, or to otherwise modify the \String fields. ==== Converting Fields to Objects Use field converters to change parsed \String objects into other, more specific, objects. There are built-in field converters for converting to objects of certain classes: - \Float - \Integer - \Date - \DateTime - \Time Other built-in field converters include: - +:numeric+: converts to \Integer and \Float. - +:all+: converts to \DateTime, \Integer, \Float. You can also define field converters to convert to objects of other classes. ===== Recipe: Convert Fields to Integers Convert fields to \Integer objects using built-in converter +:integer+: source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, converters: :integer) parsed.map {|row| row['Value'].class} # => [Integer, Integer, Integer] ===== Recipe: Convert Fields to Floats Convert fields to \Float objects using built-in converter +:float+: source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, converters: :float) parsed.map {|row| row['Value'].class} # => [Float, Float, Float] ===== Recipe: Convert Fields to Numerics Convert fields to \Integer and \Float objects using built-in converter +:numeric+: source = "Name,Value\nfoo,0\nbar,1.1\nbaz,2.2\n" parsed = CSV.parse(source, headers: true, converters: :numeric) parsed.map {|row| row['Value'].class} # => [Integer, Float, Float] ===== Recipe: Convert Fields to Dates Convert fields to \Date objects using built-in converter +:date+: source = "Name,Date\nfoo,2001-02-03\nbar,2001-02-04\nbaz,2001-02-03\n" parsed = CSV.parse(source, headers: true, converters: :date) parsed.map {|row| row['Date'].class} # => [Date, Date, Date] ===== Recipe: Convert Fields to DateTimes Convert fields to \DateTime objects using built-in converter +:date_time+: source = "Name,DateTime\nfoo,2001-02-03\nbar,2001-02-04\nbaz,2020-05-07T14:59:00-05:00\n" parsed = CSV.parse(source, headers: true, converters: :date_time) parsed.map {|row| row['DateTime'].class} # => [DateTime, DateTime, DateTime] ===== Recipe: Convert Fields to Times Convert fields to \Time objects using built-in converter +:time+: source = "Name,Time\nfoo,2001-02-03\nbar,2001-02-04\nbaz,2020-05-07T14:59:00-05:00\n" parsed = CSV.parse(source, headers: true, converters: :time) parsed.map {|row| row['Time'].class} # => [Time, Time, Time] ===== Recipe: Convert Assorted Fields to Objects Convert assorted fields to objects using built-in converter +:all+: source = "Type,Value\nInteger,0\nFloat,1.0\nDateTime,2001-02-04\n" parsed = CSV.parse(source, headers: true, converters: :all) parsed.map {|row| row['Value'].class} # => [Integer, Float, DateTime] ===== Recipe: Convert Fields to Other Objects Define a custom field converter to convert \String fields into other objects. This example defines and uses a custom field converter that converts each column-1 value to a \Rational object: rational_converter = proc do |field, field_context| field_context.index == 1 ? field.to_r : field end source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, converters: rational_converter) parsed.map {|row| row['Value'].class} # => [Rational, Rational, Rational] ==== Recipe: Filter Field Strings Define a custom field converter to modify \String fields. This example defines and uses a custom field converter that strips whitespace from each field value: strip_converter = proc {|field| field.strip } source = "Name,Value\n foo , 0 \n bar , 1 \n baz , 2 \n" parsed = CSV.parse(source, headers: true, converters: strip_converter) parsed['Name'] # => ["foo", "bar", "baz"] parsed['Value'] # => ["0", "1", "2"] ==== Recipe: Register Field Converters Register a custom field converter, assigning it a name; then refer to the converter by its name: rational_converter = proc do |field, field_context| field_context.index == 1 ? field.to_r : field end CSV::Converters[:rational] = rational_converter source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, converters: :rational) parsed['Value'] # => [(0/1), (1/1), (2/1)] ==== Using Multiple Field Converters You can use multiple field converters in either of these ways: - Specify converters in option +:converters+. - Specify converters in a custom converter list. ===== Recipe: Specify Multiple Field Converters in Option +:converters+ Apply multiple field converters by specifying them in option +:converters+: source = "Name,Value\nfoo,0\nbar,1.0\nbaz,2.0\n" parsed = CSV.parse(source, headers: true, converters: [:integer, :float]) parsed['Value'] # => [0, 1.0, 2.0] ===== Recipe: Specify Multiple Field Converters in a Custom Converter List Apply multiple field converters by defining and registering a custom converter list: strip_converter = proc {|field| field.strip } CSV::Converters[:strip] = strip_converter CSV::Converters[:my_converters] = [:integer, :float, :strip] source = "Name,Value\n foo , 0 \n bar , 1.0 \n baz , 2.0 \n" parsed = CSV.parse(source, headers: true, converters: :my_converters) parsed['Name'] # => ["foo", "bar", "baz"] parsed['Value'] # => [0, 1.0, 2.0] === Converting Headers You can use header converters to modify parsed \String headers. Built-in header converters include: - +:symbol+: converts \String header to \Symbol. - +:downcase+: converts \String header to lowercase. You can also define header converters to otherwise modify header \Strings. ==== Recipe: Convert Headers to Lowercase Convert headers to lowercase using built-in converter +:downcase+: source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, header_converters: :downcase) parsed.headers # => ["name", "value"] ==== Recipe: Convert Headers to Symbols Convert headers to downcased Symbols using built-in converter +:symbol+: source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, header_converters: :symbol) parsed.headers # => [:name, :value] parsed.headers.map {|header| header.class} # => [Symbol, Symbol] ==== Recipe: Filter Header Strings Define a custom header converter to modify \String fields. This example defines and uses a custom header converter that capitalizes each header \String: capitalize_converter = proc {|header| header.capitalize } source = "NAME,VALUE\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, header_converters: capitalize_converter) parsed.headers # => ["Name", "Value"] ==== Recipe: Register Header Converters Register a custom header converter, assigning it a name; then refer to the converter by its name: capitalize_converter = proc {|header| header.capitalize } CSV::HeaderConverters[:capitalize] = capitalize_converter source = "NAME,VALUE\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, headers: true, header_converters: :capitalize) parsed.headers # => ["Name", "Value"] ==== Using Multiple Header Converters You can use multiple header converters in either of these ways: - Specify header converters in option +:header_converters+. - Specify header converters in a custom header converter list. ===== Recipe: Specify Multiple Header Converters in Option :header_converters Apply multiple header converters by specifying them in option +:header_converters+: source = "Name,Value\nfoo,0\nbar,1.0\nbaz,2.0\n" parsed = CSV.parse(source, headers: true, header_converters: [:downcase, :symbol]) parsed.headers # => [:name, :value] ===== Recipe: Specify Multiple Header Converters in a Custom Header Converter List Apply multiple header converters by defining and registering a custom header converter list: CSV::HeaderConverters[:my_header_converters] = [:symbol, :downcase] source = "NAME,VALUE\nfoo,0\nbar,1.0\nbaz,2.0\n" parsed = CSV.parse(source, headers: true, header_converters: :my_header_converters) parsed.headers # => [:name, :value] === Diagnostics ==== Recipe: Capture Unconverted Fields To capture unconverted field values, use option +:unconverted_fields+: source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" parsed = CSV.parse(source, converters: :integer, unconverted_fields: true) parsed # => [["Name", "Value"], ["foo", 0], ["bar", 1], ["baz", 2]] parsed.each {|row| p row.unconverted_fields } Output: ["Name", "Value"] ["foo", "0"] ["bar", "1"] ["baz", "2"] ==== Recipe: Capture Field Info To capture field info in a custom converter, accept two block arguments. The first is the field value; the second is a +CSV::FieldInfo+ object: strip_converter = proc {|field, field_info| p field_info; field.strip } source = " foo , 0 \n bar , 1 \n baz , 2 \n" parsed = CSV.parse(source, converters: strip_converter) parsed # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Output: # # # # # # PK!rDM recipes/recipes.rdocnu[== Recipes for \CSV The recipes are specific code examples for specific tasks. See: - {Recipes for Parsing CSV}[./parsing_rdoc.html] - {Recipes for Generating CSV}[./generating_rdoc.html] - {Recipes for Filtering CSV}[./filtering_rdoc.html] PK!{d""recipes/filtering.rdocnu[== Recipes for Filtering \CSV These recipes are specific code examples for specific \CSV filtering tasks. For other recipes, see {Recipes for CSV}[./recipes_rdoc.html]. All code snippets on this page assume that the following has been executed: require 'csv' === Contents - {Source and Output Formats}[#label-Source+and+Output+Formats] - {Filtering String to String}[#label-Filtering+String+to+String] - {Recipe: Filter String to String parsing Headers}[#label-Recipe-3A+Filter+String+to+String+parsing+Headers] - {Recipe: Filter String to String parsing and writing Headers}[#label-Recipe-3A+Filter+String+to+String+parsing+and+writing+Headers] - {Recipe: Filter String to String Without Headers}[#label-Recipe-3A+Filter+String+to+String+Without+Headers] - {Filtering String to IO Stream}[#label-Filtering+String+to+IO+Stream] - {Recipe: Filter String to IO Stream parsing Headers}[#label-Recipe-3A+Filter+String+to+IO+Stream+parsing+Headers] - {Recipe: Filter String to IO Stream parsing and writing Headers}[#label-Recipe-3A+Filter+String+to+IO+Stream+parsing+and+writing+Headers] - {Recipe: Filter String to IO Stream Without Headers}[#label-Recipe-3A+Filter+String+to+IO+Stream+Without+Headers] - {Filtering IO Stream to String}[#label-Filtering+IO+Stream+to+String] - {Recipe: Filter IO Stream to String parsing Headers}[#label-Recipe-3A+Filter+IO+Stream+to+String+parsing+Headers] - {Recipe: Filter IO Stream to String parsing and writing Headers}[#label-Recipe-3A+Filter+IO+Stream+to+String+parsing+and+writing+Headers] - {Recipe: Filter IO Stream to String Without Headers}[#label-Recipe-3A+Filter+IO+Stream+to+String+Without+Headers] - {Filtering IO Stream to IO Stream}[#label-Filtering+IO+Stream+to+IO+Stream] - {Recipe: Filter IO Stream to IO Stream parsing Headers}[#label-Recipe-3A+Filter+IO+Stream+to+IO+Stream+parsing+Headers] - {Recipe: Filter IO Stream to IO Stream parsing and writing Headers}[#label-Recipe-3A+Filter+IO+Stream+to+IO+Stream+parsing+and+writing+Headers] - {Recipe: Filter IO Stream to IO Stream Without Headers}[#label-Recipe-3A+Filter+IO+Stream+to+IO+Stream+Without+Headers] === Source and Output Formats You can use a Unix-style "filter" for \CSV data. The filter reads source \CSV data and writes output \CSV data as modified by the filter. The input and output \CSV data may be any mixture of \Strings and \IO streams. ==== Filtering \String to \String You can filter one \String to another, with or without headers. ===== Recipe: Filter \String to \String parsing Headers Use class method CSV.filter with option +headers+ to filter a \String to another \String: in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" out_string = '' CSV.filter(in_string, out_string, headers: true) do |row| row['Name'] = row['Name'].upcase row['Value'] *= 4 end out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \String to \String parsing and writing Headers Use class method CSV.filter with option +headers+ and +out_write_headers+ to filter a \String to another \String including header row: in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" out_string = '' CSV.filter(in_string, out_string, headers: true, out_write_headers: true) do |row| unless row.is_a?(Array) row['Name'] = row['Name'].upcase row['Value'] *= 4 end end out_string # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \String to \String Without Headers Use class method CSV.filter without option +headers+ to filter a \String to another \String: in_string = "foo,0\nbar,1\nbaz,2\n" out_string = '' CSV.filter(in_string, out_string) do |row| row[0] = row[0].upcase row[1] *= 4 end out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n" ==== Filtering \String to \IO Stream You can filter a \String to an \IO stream, with or without headers. ===== Recipe: Filter \String to \IO Stream parsing Headers Use class method CSV.filter with option +headers+ to filter a \String to an \IO stream: in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.open(path, 'w') do |out_io| CSV.filter(in_string, out_io, headers: true) do |row| row['Name'] = row['Name'].upcase row['Value'] *= 4 end end p File.read(path) # => "FOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \String to \IO Stream parsing and writing Headers Use class method CSV.filter with option +headers+ and +out_write_headers+ to filter a \String to an \IO stream including header row: in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.open(path, 'w') do |out_io| CSV.filter(in_string, out_io, headers: true, out_write_headers: true ) do |row| unless row.is_a?(Array) row['Name'] = row['Name'].upcase row['Value'] *= 4 end end end p File.read(path) # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \String to \IO Stream Without Headers Use class method CSV.filter without option +headers+ to filter a \String to an \IO stream: in_string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.open(path, 'w') do |out_io| CSV.filter(in_string, out_io) do |row| row[0] = row[0].upcase row[1] *= 4 end end p File.read(path) # => "FOO,0000\nBAR,1111\nBAZ,2222\n" ==== Filtering \IO Stream to \String You can filter an \IO stream to a \String, with or without headers. ===== Recipe: Filter \IO Stream to \String parsing Headers Use class method CSV.filter with option +headers+ to filter an \IO stream to a \String: in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, in_string) out_string = '' File.open(path) do |in_io| CSV.filter(in_io, out_string, headers: true) do |row| row['Name'] = row['Name'].upcase row['Value'] *= 4 end end out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \IO Stream to \String parsing and writing Headers Use class method CSV.filter with option +headers+ and +out_write_headers+ to filter an \IO stream to a \String including header row: in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, in_string) out_string = '' File.open(path) do |in_io| CSV.filter(in_io, out_string, headers: true, out_write_headers: true) do |row| unless row.is_a?(Array) row['Name'] = row['Name'].upcase row['Value'] *= 4 end end end out_string # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \IO Stream to \String Without Headers Use class method CSV.filter without option +headers+ to filter an \IO stream to a \String: in_string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, in_string) out_string = '' File.open(path) do |in_io| CSV.filter(in_io, out_string) do |row| row[0] = row[0].upcase row[1] *= 4 end end out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n" ==== Filtering \IO Stream to \IO Stream You can filter an \IO stream to another \IO stream, with or without headers. ===== Recipe: Filter \IO Stream to \IO Stream parsing Headers Use class method CSV.filter with option +headers+ to filter an \IO stream to another \IO stream: in_path = 't.csv' in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" File.write(in_path, in_string) out_path = 'u.csv' File.open(in_path) do |in_io| File.open(out_path, 'w') do |out_io| CSV.filter(in_io, out_io, headers: true) do |row| row['Name'] = row['Name'].upcase row['Value'] *= 4 end end end p File.read(out_path) # => "FOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \IO Stream to \IO Stream parsing and writing Headers Use class method CSV.filter with option +headers+ and +out_write_headers+ to filter an \IO stream to another \IO stream including header row: in_path = 't.csv' in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" File.write(in_path, in_string) out_path = 'u.csv' File.open(in_path) do |in_io| File.open(out_path, 'w') do |out_io| CSV.filter(in_io, out_io, headers: true, out_write_headers: true) do |row| unless row.is_a?(Array) row['Name'] = row['Name'].upcase row['Value'] *= 4 end end end end p File.read(out_path) # => "Name,Value\nFOO,0000\nBAR,1111\nBAZ,2222\n" ===== Recipe: Filter \IO Stream to \IO Stream Without Headers Use class method CSV.filter without option +headers+ to filter an \IO stream to another \IO stream: in_path = 't.csv' in_string = "foo,0\nbar,1\nbaz,2\n" File.write(in_path, in_string) out_path = 'u.csv' File.open(in_path) do |in_io| File.open(out_path, 'w') do |out_io| CSV.filter(in_io, out_io) do |row| row[0] = row[0].upcase row[1] *= 4 end end end p File.read(out_path) # => "FOO,0000\nBAR,1111\nBAZ,2222\n" PK!c%73)3)recipes/generating.rdocnu[== Recipes for Generating \CSV These recipes are specific code examples for specific \CSV generating tasks. For other recipes, see {Recipes for CSV}[./recipes_rdoc.html]. All code snippets on this page assume that the following has been executed: require 'csv' === Contents - {Output Formats}[#label-Output+Formats] - {Generating to a String}[#label-Generating+to+a+String] - {Recipe: Generate to String with Headers}[#label-Recipe-3A+Generate+to+String+with+Headers] - {Recipe: Generate to String Without Headers}[#label-Recipe-3A+Generate+to+String+Without+Headers] - {Generating to a File}[#label-Generating+to+a+File] - {Recipe: Generate to File with Headers}[#label-Recipe-3A+Generate+to+File+with+Headers] - {Recipe: Generate to File Without Headers}[#label-Recipe-3A+Generate+to+File+Without+Headers] - {Generating to IO an Stream}[#label-Generating+to+an+IO+Stream] - {Recipe: Generate to IO Stream with Headers}[#label-Recipe-3A+Generate+to+IO+Stream+with+Headers] - {Recipe: Generate to IO Stream Without Headers}[#label-Recipe-3A+Generate+to+IO+Stream+Without+Headers] - {Converting Fields}[#label-Converting+Fields] - {Recipe: Filter Generated Field Strings}[#label-Recipe-3A+Filter+Generated+Field+Strings] - {Recipe: Specify Multiple Write Converters}[#label-Recipe-3A+Specify+Multiple+Write+Converters] - {RFC 4180 Compliance}[#label-RFC+4180+Compliance] - {Row Separator}[#label-Row+Separator] - {Recipe: Generate Compliant Row Separator}[#label-Recipe-3A+Generate+Compliant+Row+Separator] - {Recipe: Generate Non-Compliant Row Separator}[#label-Recipe-3A+Generate+Non-Compliant+Row+Separator] - {Column Separator}[#label-Column+Separator] - {Recipe: Generate Compliant Column Separator}[#label-Recipe-3A+Generate+Compliant+Column+Separator] - {Recipe: Generate Non-Compliant Column Separator}[#label-Recipe-3A+Generate+Non-Compliant+Column+Separator] - {Quotes}[#label-Quotes] - {Recipe: Quote All Fields}[#label-Recipe-3A+Quote+All+Fields] - {Recipe: Quote Empty Fields}[#label-Recipe-3A+Quote+Empty+Fields] - {Recipe: Generate Compliant Quote Character}[#label-Recipe-3A+Generate+Compliant+Quote+Character] - {Recipe: Generate Non-Compliant Quote Character}[#label-Recipe-3A+Generate+Non-Compliant+Quote+Character] === Output Formats You can generate \CSV output to a \String, to a \File (via its path), or to an \IO stream. ==== Generating to a \String You can generate \CSV output to a \String, with or without headers. ===== Recipe: Generate to \String with Headers Use class method CSV.generate with option +headers+ to generate to a \String. This example uses method CSV#<< to append the rows that are to be generated: output_string = CSV.generate('', headers: ['Name', 'Value'], write_headers: true) do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "Name,Value\nFoo,0\nBar,1\nBaz,2\n" ===== Recipe: Generate to \String Without Headers Use class method CSV.generate without option +headers+ to generate to a \String. This example uses method CSV#<< to append the rows that are to be generated: output_string = CSV.generate do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "Foo,0\nBar,1\nBaz,2\n" ==== Generating to a \File You can generate /CSV data to a \File, with or without headers. ===== Recipe: Generate to \File with Headers Use class method CSV.open with option +headers+ generate to a \File. This example uses method CSV#<< to append the rows that are to be generated: path = 't.csv' CSV.open(path, 'w', headers: ['Name', 'Value'], write_headers: true) do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end p File.read(path) # => "Name,Value\nFoo,0\nBar,1\nBaz,2\n" ===== Recipe: Generate to \File Without Headers Use class method CSV.open without option +headers+ to generate to a \File. This example uses method CSV#<< to append the rows that are to be generated: path = 't.csv' CSV.open(path, 'w') do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end p File.read(path) # => "Foo,0\nBar,1\nBaz,2\n" ==== Generating to an \IO Stream You can generate \CSV data to an \IO stream, with or without headers. ==== Recipe: Generate to \IO Stream with Headers Use class method CSV.new with option +headers+ to generate \CSV data to an \IO stream: path = 't.csv' File.open(path, 'w') do |file| csv = CSV.new(file, headers: ['Name', 'Value'], write_headers: true) csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end p File.read(path) # => "Name,Value\nFoo,0\nBar,1\nBaz,2\n" ===== Recipe: Generate to \IO Stream Without Headers Use class method CSV.new without option +headers+ to generate \CSV data to an \IO stream: path = 't.csv' File.open(path, 'w') do |file| csv = CSV.new(file) csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end p File.read(path) # => "Foo,0\nBar,1\nBaz,2\n" === Converting Fields You can use _write_ _converters_ to convert fields when generating \CSV. ==== Recipe: Filter Generated Field Strings Use option :write_converters and a custom converter to convert field values when generating \CSV. This example defines and uses a custom write converter to strip whitespace from generated fields: strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field } output_string = CSV.generate(write_converters: strip_converter) do |csv| csv << [' foo ', 0] csv << [' bar ', 1] csv << [' baz ', 2] end output_string # => "foo,0\nbar,1\nbaz,2\n" ==== Recipe: Specify Multiple Write Converters Use option :write_converters and multiple custom converters to convert field values when generating \CSV. This example defines and uses two custom write converters to strip and upcase generated fields: strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field } upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field } converters = [strip_converter, upcase_converter] output_string = CSV.generate(write_converters: converters) do |csv| csv << [' foo ', 0] csv << [' bar ', 1] csv << [' baz ', 2] end output_string # => "FOO,0\nBAR,1\nBAZ,2\n" === RFC 4180 Compliance By default, \CSV generates data that is compliant with {RFC 4180}[https://www.rfc-editor.org/rfc/rfc4180] with respect to: - Column separator. - Quote character. ==== Row Separator RFC 4180 specifies the row separator CRLF (Ruby "\r\n"). ===== Recipe: Generate Compliant Row Separator For strict compliance, use option +:row_sep+ to specify row separator "\r\n": output_string = CSV.generate('', row_sep: "\r\n") do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "Foo,0\r\nBar,1\r\nBaz,2\r\n" ===== Recipe: Generate Non-Compliant Row Separator For data with non-compliant row separators, use option +:row_sep+ with a different value: This example source uses semicolon (";') as its row separator: output_string = CSV.generate('', row_sep: ";") do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "Foo,0;Bar,1;Baz,2;" ==== Column Separator RFC 4180 specifies column separator COMMA (Ruby ","). ===== Recipe: Generate Compliant Column Separator Because the \CSV default comma separator is ",", you need not specify option +:col_sep+ for compliant data: output_string = CSV.generate('') do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "Foo,0\nBar,1\nBaz,2\n" ===== Recipe: Generate Non-Compliant Column Separator For data with non-compliant column separators, use option +:col_sep+. This example source uses TAB ("\t") as its column separator: output_string = CSV.generate('', col_sep: "\t") do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "Foo\t0\nBar\t1\nBaz\t2\n" ==== Quotes IFC 4180 allows most fields to be quoted or not. By default, \CSV does not quote most fields. However, a field containing the current row separator, column separator, or quote character is automatically quoted, producing IFC 4180 compliance: # Field contains row separator. output_string = CSV.generate('') do |csv| row_sep = csv.row_sep csv << ["Foo#{row_sep}Foo", 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "\"Foo\nFoo\",0\nBar,1\nBaz,2\n" # Field contains column separator. output_string = CSV.generate('') do |csv| col_sep = csv.col_sep csv << ["Foo#{col_sep}Foo", 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "\"Foo,Foo\",0\nBar,1\nBaz,2\n" # Field contains quote character. output_string = CSV.generate('') do |csv| quote_char = csv.quote_char csv << ["Foo#{quote_char}Foo", 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "\"Foo\"\"Foo\",0\nBar,1\nBaz,2\n" ===== Recipe: Quote All Fields Use option +:force_quotes+ to force quoted fields: output_string = CSV.generate('', force_quotes: true) do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "\"Foo\",\"0\"\n\"Bar\",\"1\"\n\"Baz\",\"2\"\n" ===== Recipe: Quote Empty Fields Use option +:quote_empty+ to force quoting for empty fields: output_string = CSV.generate('', quote_empty: true) do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['', 2] end output_string # => "Foo,0\nBar,1\n\"\",2\n" ===== Recipe: Generate Compliant Quote Character RFC 4180 specifies quote character DQUOTE (Ruby "\""). Because the \CSV default quote character is also "\"", you need not specify option +:quote_char+ for compliant data: output_string = CSV.generate('', force_quotes: true) do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "\"Foo\",\"0\"\n\"Bar\",\"1\"\n\"Baz\",\"2\"\n" ===== Recipe: Generate Non-Compliant Quote Character For data with non-compliant quote characters, use option +:quote_char+. This example source uses SQUOTE ("'") as its quote character: output_string = CSV.generate('', quote_char: "'", force_quotes: true) do |csv| csv << ['Foo', 0] csv << ['Bar', 1] csv << ['Baz', 2] end output_string # => "'Foo','0'\n'Bar','1'\n'Baz','2'\n" PK!<<options/parsing/skip_lines.rdocnu[====== Option +skip_lines+ Specifies an object to use in identifying comment lines in the input that are to be ignored: * If a \Regexp, ignores lines that match it. * If a \String, converts it to a \Regexp, ignores lines that match it. * If +nil+, no lines are considered to be comments. Default value: CSV::DEFAULT_OPTIONS.fetch(:skip_lines) # => nil For examples in this section: str = <<-EOT # Comment foo,0 bar,1 baz,2 # Another comment EOT str # => "# Comment\nfoo,0\nbar,1\nbaz,2\n# Another comment\n" Using the default, +nil+: ary = CSV.parse(str) ary # => [["# Comment"], ["foo", "0"], ["bar", "1"], ["baz", "2"], ["# Another comment"]] Using a \Regexp: ary = CSV.parse(str, skip_lines: /^#/) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Using a \String: ary = CSV.parse(str, skip_lines: '#') ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] --- Raises an exception if given an object that is not a \Regexp, a \String, or +nil+: # Raises ArgumentError (:skip_lines has to respond to #match: 0) CSV.parse(str, skip_lines: 0) PK!x*^kkoptions/parsing/strip.rdocnu[====== Option +strip+ Specifies the boolean value that determines whether whitespace is stripped from each input field. Default value: CSV::DEFAULT_OPTIONS.fetch(:strip) # => false With default value +false+: ary = CSV.parse_line(' a , b ') ary # => [" a ", " b "] With value +true+: ary = CSV.parse_line(' a , b ', strip: true) ary # => ["a", "b"] PK!J> options/parsing/empty_value.rdocnu[====== Option +empty_value+ Specifies the object that is to be substituted for each field that has an empty \String. Default value: CSV::DEFAULT_OPTIONS.fetch(:empty_value) # => "" (empty string) With the default, "": CSV.parse_line('a,"",b,"",c') # => ["a", "", "b", "", "c"] With a different object: CSV.parse_line('a,"",b,"",c', empty_value: 'x') # => ["a", "x", "b", "x", "c"] PK!R{options/parsing/converters.rdocnu[====== Option +converters+ Specifies converters to be used in parsing fields. See {Field Converters}[#class-CSV-label-Field+Converters] Default value: CSV::DEFAULT_OPTIONS.fetch(:converters) # => nil The value may be a field converter name (see {Stored Converters}[#class-CSV-label-Stored+Converters]): str = '1,2,3' # Without a converter array = CSV.parse_line(str) array # => ["1", "2", "3"] # With built-in converter :integer array = CSV.parse_line(str, converters: :integer) array # => [1, 2, 3] The value may be a converter list (see {Converter Lists}[#class-CSV-label-Converter+Lists]): str = '1,3.14159' # Without converters array = CSV.parse_line(str) array # => ["1", "3.14159"] # With built-in converters array = CSV.parse_line(str, converters: [:integer, :float]) array # => [1, 3.14159] The value may be a \Proc custom converter: (see {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters]): str = ' foo , bar , baz ' # Without a converter array = CSV.parse_line(str) array # => [" foo ", " bar ", " baz "] # With a custom converter array = CSV.parse_line(str, converters: proc {|field| field.strip }) array # => ["foo", "bar", "baz"] See also {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters] --- Raises an exception if the converter is not a converter name or a \Proc: str = 'foo,0' # Raises NoMethodError (undefined method `arity' for nil:NilClass) CSV.parse(str, converters: :foo) PK!88#options/parsing/return_headers.rdocnu[====== Option +return_headers+ Specifies the boolean that determines whether method #shift returns or ignores the header row. Default value: CSV::DEFAULT_OPTIONS.fetch(:return_headers) # => false Examples: str = <<-EOT Name,Count foo,0 bar,1 bax,2 EOT # Without return_headers first row is str. csv = CSV.new(str, headers: true) csv.shift # => # # With return_headers first row is headers. csv = CSV.new(str, headers: true, return_headers: true) csv.shift # => # PK!^;h__options/parsing/nil_value.rdocnu[====== Option +nil_value+ Specifies the object that is to be substituted for each null (no-text) field. Default value: CSV::DEFAULT_OPTIONS.fetch(:nil_value) # => nil With the default, +nil+: CSV.parse_line('a,,b,,c') # => ["a", nil, "b", nil, "c"] With a different object: CSV.parse_line('a,,b,,c', nil_value: 0) # => ["a", 0, "b", 0, "c"] PK!njnoptions/parsing/headers.rdocnu[====== Option +headers+ Specifies a boolean, \Symbol, \Array, or \String to be used to define column headers. Default value: CSV::DEFAULT_OPTIONS.fetch(:headers) # => false --- Without +headers+: str = <<-EOT Name,Count foo,0 bar,1 bax,2 EOT csv = CSV.new(str) csv # => # csv.headers # => nil csv.shift # => ["Name", "Count"] --- If set to +true+ or the \Symbol +:first_row+, the first row of the data is treated as a row of headers: str = <<-EOT Name,Count foo,0 bar,1 bax,2 EOT csv = CSV.new(str, headers: true) csv # => # csv.headers # => ["Name", "Count"] csv.shift # => # --- If set to an \Array, the \Array elements are treated as headers: str = <<-EOT foo,0 bar,1 bax,2 EOT csv = CSV.new(str, headers: ['Name', 'Count']) csv csv.headers # => ["Name", "Count"] csv.shift # => # --- If set to a \String +str+, method CSV::parse_line(str, options) is called with the current +options+, and the returned \Array is treated as headers: str = <<-EOT foo,0 bar,1 bax,2 EOT csv = CSV.new(str, headers: 'Name,Count') csv csv.headers # => ["Name", "Count"] csv.shift # => # PK!v6&options/parsing/header_converters.rdocnu[====== Option +header_converters+ Specifies converters to be used in parsing headers. See {Header Converters}[#class-CSV-label-Header+Converters] Default value: CSV::DEFAULT_OPTIONS.fetch(:header_converters) # => nil Identical in functionality to option {converters}[#class-CSV-label-Option+converters] except that: - The converters apply only to the header row. - The built-in header converters are +:downcase+ and +:symbol+. This section assumes prior execution of: str = <<-EOT Name,Value foo,0 bar,1 baz,2 EOT # With no header converter table = CSV.parse(str, headers: true) table.headers # => ["Name", "Value"] The value may be a header converter name (see {Stored Converters}[#class-CSV-label-Stored+Converters]): table = CSV.parse(str, headers: true, header_converters: :downcase) table.headers # => ["name", "value"] The value may be a converter list (see {Converter Lists}[#class-CSV-label-Converter+Lists]): header_converters = [:downcase, :symbol] table = CSV.parse(str, headers: true, header_converters: header_converters) table.headers # => [:name, :value] The value may be a \Proc custom converter (see {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters]): upcase_converter = proc {|field| field.upcase } table = CSV.parse(str, headers: true, header_converters: upcase_converter) table.headers # => ["NAME", "VALUE"] See also {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters] PK!%options/parsing/field_size_limit.rdocnu[====== Option +field_size_limit+ Specifies the \Integer field size limit. Default value: CSV::DEFAULT_OPTIONS.fetch(:field_size_limit) # => nil This is a maximum size CSV will read ahead looking for the closing quote for a field. (In truth, it reads to the first line ending beyond this size.) If a quote cannot be found within the limit CSV will raise a MalformedCSVError, assuming the data is faulty. You can use this limit to prevent what are effectively DoS attacks on the parser. However, this limit can cause a legitimate parse to fail; therefore the default value is +nil+ (no limit). For the examples in this section: str = <<~EOT "a","b" " 2345 ","" EOT str # => "\"a\",\"b\"\n\"\n2345\n\",\"\"\n" Using the default +nil+: ary = CSV.parse(str) ary # => [["a", "b"], ["\n2345\n", ""]] Using 50: field_size_limit = 50 ary = CSV.parse(str, field_size_limit: field_size_limit) ary # => [["a", "b"], ["\n2345\n", ""]] --- Raises an exception if a field is too long: big_str = "123456789\n" * 1024 # Raises CSV::MalformedCSVError (Field size exceeded in line 1.) CSV.parse('valid,fields,"' + big_str + '"', field_size_limit: 2048) PK!`ꐟ'options/parsing/unconverted_fields.rdocnu[====== Option +unconverted_fields+ Specifies the boolean that determines whether unconverted field values are to be available. Default value: CSV::DEFAULT_OPTIONS.fetch(:unconverted_fields) # => nil The unconverted field values are those found in the source data, prior to any conversions performed via option +converters+. When option +unconverted_fields+ is +true+, each returned row (\Array or \CSV::Row) has an added method, +unconverted_fields+, that returns the unconverted field values: str = <<-EOT foo,0 bar,1 baz,2 EOT # Without unconverted_fields csv = CSV.parse(str, converters: :integer) csv # => [["foo", 0], ["bar", 1], ["baz", 2]] csv.first.respond_to?(:unconverted_fields) # => false # With unconverted_fields csv = CSV.parse(str, converters: :integer, unconverted_fields: true) csv # => [["foo", 0], ["bar", 1], ["baz", 2]] csv.first.respond_to?(:unconverted_fields) # => true csv.first.unconverted_fields # => ["foo", "0"] PK!9; options/parsing/skip_blanks.rdocnu[====== Option +skip_blanks+ Specifies a boolean that determines whether blank lines in the input will be ignored; a line that contains a column separator is not considered to be blank. Default value: CSV::DEFAULT_OPTIONS.fetch(:skip_blanks) # => false See also option {skiplines}[#class-CSV-label-Option+skip_lines]. For examples in this section: str = <<-EOT foo,0 bar,1 baz,2 , EOT Using the default, +false+: ary = CSV.parse(str) ary # => [["foo", "0"], [], ["bar", "1"], ["baz", "2"], [], [nil, nil]] Using +true+: ary = CSV.parse(str, skip_blanks: true) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"], [nil, nil]] Using a truthy value: ary = CSV.parse(str, skip_blanks: :foo) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"], [nil, nil]] PK!$options/parsing/liberal_parsing.rdocnu[====== Option +liberal_parsing+ Specifies the boolean or hash value that determines whether CSV will attempt to parse input not conformant with RFC 4180, such as double quotes in unquoted fields. Default value: CSV::DEFAULT_OPTIONS.fetch(:liberal_parsing) # => false For the next two examples: str = 'is,this "three, or four",fields' Without +liberal_parsing+: # Raises CSV::MalformedCSVError (Illegal quoting in str 1.) CSV.parse_line(str) With +liberal_parsing+: ary = CSV.parse_line(str, liberal_parsing: true) ary # => ["is", "this \"three", " or four\"", "fields"] Use the +backslash_quote+ sub-option to parse values that use a backslash to escape a double-quote character. This causes the parser to treat \" as if it were "". For the next two examples: str = 'Show,"Harry \"Handcuff\" Houdini, the one and only","Tampa Theater"' With +liberal_parsing+, but without the +backslash_quote+ sub-option: # Incorrect interpretation of backslash; incorrectly interprets the quoted comma as a field separator. ary = CSV.parse_line(str, liberal_parsing: true) ary # => ["Show", "\"Harry \\\"Handcuff\\\" Houdini", " the one and only\"", "Tampa Theater"] puts ary[1] # => "Harry \"Handcuff\" Houdini With +liberal_parsing+ and its +backslash_quote+ sub-option: ary = CSV.parse_line(str, liberal_parsing: { backslash_quote: true }) ary # => ["Show", "Harry \"Handcuff\" Houdini, the one and only", "Tampa Theater"] puts ary[1] # => Harry "Handcuff" Houdini, the one and only PK!l|^^#options/generating/quote_empty.rdocnu[====== Option +quote_empty+ Specifies the boolean that determines whether an empty value is to be double-quoted. Default value: CSV::DEFAULT_OPTIONS.fetch(:quote_empty) # => true With the default +true+: CSV.generate_line(['"', ""]) # => "\"\"\"\",\"\"\n" With +false+: CSV.generate_line(['"', ""], quote_empty: false) # => "\"\"\"\",\n" PK!`a|]~~'options/generating/write_nil_value.rdocnu[====== Option +write_nil_value+ Specifies the object that is to be substituted for each +nil+-valued field. Default value: CSV::DEFAULT_OPTIONS.fetch(:write_nil_value) # => nil Without the option: str = CSV.generate_line(['a', nil, 'c', nil]) str # => "a,,c,\n" With the option: str = CSV.generate_line(['a', nil, 'c', nil], write_nil_value: "x") str # => "a,x,c,x\n" PK!%options/generating/write_headers.rdocnu[====== Option +write_headers+ Specifies the boolean that determines whether a header row is included in the output; ignored if there are no headers. Default value: CSV::DEFAULT_OPTIONS.fetch(:write_headers) # => nil Without +write_headers+: file_path = 't.csv' CSV.open(file_path,'w', :headers => ['Name','Value'] ) do |csv| csv << ['foo', '0'] end CSV.open(file_path) do |csv| csv.shift end # => ["foo", "0"] With +write_headers+": CSV.open(file_path,'w', :write_headers => true, :headers => ['Name','Value'] ) do |csv| csv << ['foo', '0'] end CSV.open(file_path) do |csv| csv.shift end # => ["Name", "Value"] PK!>J0)options/generating/write_empty_value.rdocnu[====== Option +write_empty_value+ Specifies the object that is to be substituted for each field that has an empty \String. Default value: CSV::DEFAULT_OPTIONS.fetch(:write_empty_value) # => "" Without the option: str = CSV.generate_line(['a', '', 'c', '']) str # => "a,\"\",c,\"\"\n" With the option: str = CSV.generate_line(['a', '', 'c', ''], write_empty_value: "x") str # => "a,x,c,x\n" PK!jd$options/generating/force_quotes.rdocnu[====== Option +force_quotes+ Specifies the boolean that determines whether each output field is to be double-quoted. Default value: CSV::DEFAULT_OPTIONS.fetch(:force_quotes) # => false For examples in this section: ary = ['foo', 0, nil] Using the default, +false+: str = CSV.generate_line(ary) str # => "foo,0,\n" Using +true+: str = CSV.generate_line(ary, force_quotes: true) str # => "\"foo\",\"0\",\"\"\n" PK!LHyy(options/generating/write_converters.rdocnu[====== Option +write_converters+ Specifies converters to be used in generating fields. See {Write Converters}[#class-CSV-label-Write+Converters] Default value: CSV::DEFAULT_OPTIONS.fetch(:write_converters) # => nil With no write converter: str = CSV.generate_line(["\na\n", "\tb\t", " c "]) str # => "\"\na\n\",\tb\t, c \n" With a write converter: strip_converter = proc {|field| field.strip } str = CSV.generate_line(["\na\n", "\tb\t", " c "], write_converters: strip_converter) str # => "a,b,c\n" With two write converters (called in order): upcase_converter = proc {|field| field.upcase } downcase_converter = proc {|field| field.downcase } write_converters = [upcase_converter, downcase_converter] str = CSV.generate_line(['a', 'b', 'c'], write_converters: write_converters) str # => "a,b,c\n" See also {Write Converters}[#class-CSV-label-Write+Converters] PK!E1mmoptions/common/quote_char.rdocnu[====== Option +quote_char+ Specifies the character (\String of length 1) used used to quote fields in both parsing and generating. This String will be transcoded into the data's \Encoding before use. Default value: CSV::DEFAULT_OPTIONS.fetch(:quote_char) # => "\"" (double quote) This is useful for an application that incorrectly uses ' (single-quote) to quote fields, instead of the correct " (double-quote). Using the default (double quote): str = CSV.generate do |csv| csv << ['foo', 0] csv << ["'bar'", 1] csv << ['"baz"', 2] end str # => "foo,0\n'bar',1\n\"\"\"baz\"\"\",2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]] Using ' (single-quote): quote_char = "'" str = CSV.generate(quote_char: quote_char) do |csv| csv << ['foo', 0] csv << ["'bar'", 1] csv << ['"baz"', 2] end str # => "foo,0\n'''bar''',1\n\"baz\",2\n" ary = CSV.parse(str, quote_char: quote_char) ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]] --- Raises an exception if the \String length is greater than 1: # Raises ArgumentError (:quote_char has to be nil or a single character String) CSV.new('', quote_char: 'xx') Raises an exception if the value is not a \String: # Raises ArgumentError (:quote_char has to be nil or a single character String) CSV.new('', quote_char: :foo) PK![B options/common/row_sep.rdocnu[====== Option +row_sep+ Specifies the row separator, a \String or the \Symbol :auto (see below), to be used for both parsing and generating. Default value: CSV::DEFAULT_OPTIONS.fetch(:row_sep) # => :auto --- When +row_sep+ is a \String, that \String becomes the row separator. The String will be transcoded into the data's Encoding before use. Using "\n": row_sep = "\n" str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Using | (pipe): row_sep = '|' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0|bar,1|baz,2|" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Using -- (two hyphens): row_sep = '--' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0--bar,1--baz,2--" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Using '' (empty string): row_sep = '' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0bar,1baz,2" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0bar", "1baz", "2"]] --- When +row_sep+ is the \Symbol +:auto+ (the default), generating uses "\n" as the row separator: str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" Parsing, on the other hand, invokes auto-discovery of the row separator. Auto-discovery reads ahead in the data looking for the next \r\n, +\n+, or +\r+ sequence. The sequence will be selected even if it occurs in a quoted field, assuming that you would have the same line endings there. Example: str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] The default $INPUT_RECORD_SEPARATOR ($/) is used if any of the following is true: * None of those sequences is found. * Data is +ARGF+, +STDIN+, +STDOUT+, or +STDERR+. * The stream is only available for output. Obviously, discovery takes a little time. Set manually if speed is important. Also note that IO objects should be opened in binary mode on Windows if this feature will be used as the line-ending translation can cause problems with resetting the document position to where it was before the read ahead. PK!7Boptions/common/col_sep.rdocnu[====== Option +col_sep+ Specifies the \String column separator to be used for both parsing and generating. The \String will be transcoded into the data's \Encoding before use. Default value: CSV::DEFAULT_OPTIONS.fetch(:col_sep) # => "," (comma) Using the default (comma): str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Using +:+ (colon): col_sep = ':' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo:0\nbar:1\nbaz:2\n" ary = CSV.parse(str, col_sep: col_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Using +::+ (two colons): col_sep = '::' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo::0\nbar::1\nbaz::2\n" ary = CSV.parse(str, col_sep: col_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]] Using '' (empty string): col_sep = '' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo0\nbar1\nbaz2\n" --- Raises an exception if parsing with the empty \String: col_sep = '' # Raises ArgumentError (:col_sep must be 1 or more characters: "") CSV.parse("foo0\nbar1\nbaz2\n", col_sep: col_sep) PK!\garguments/io.rdocnu[* Argument +io+ should be an IO object that is: * Open for reading; on return, the IO object will be closed. * Positioned at the beginning. To position at the end, for appending, use method CSV.generate. For any other positioning, pass a preset \StringIO object instead. PK! K'tWtWrecipes/parsing.rdocnu[PK!rDM Wrecipes/recipes.rdocnu[PK!{d""Xrecipes/filtering.rdocnu[PK!c%73)3){recipes/generating.rdocnu[PK!<<noptions/parsing/skip_lines.rdocnu[PK!x*^kkoptions/parsing/strip.rdocnu[PK!J> options/parsing/empty_value.rdocnu[PK!R{options/parsing/converters.rdocnu[PK!88#options/parsing/return_headers.rdocnu[PK!^;h__@options/parsing/nil_value.rdocnu[PK!njnoptions/parsing/headers.rdocnu[PK!v6&options/parsing/header_converters.rdocnu[PK!%options/parsing/field_size_limit.rdocnu[PK!`ꐟ' options/parsing/unconverted_fields.rdocnu[PK!9; 3options/parsing/skip_blanks.rdocnu[PK!$options/parsing/liberal_parsing.rdocnu[PK!l|^^#options/generating/quote_empty.rdocnu[PK!`a|]~~'options/generating/write_nil_value.rdocnu[PK!%loptions/generating/write_headers.rdocnu[PK!>J0)joptions/generating/write_empty_value.rdocnu[PK!jd$Woptions/generating/force_quotes.rdocnu[PK!LHyy(Uoptions/generating/write_converters.rdocnu[PK!E1mm&options/common/quote_char.rdocnu[PK![B options/common/row_sep.rdocnu[PK!7Boptions/common/col_sep.rdocnu[PK!\garguments/io.rdocnu[PK l