Ruby 2.4

CSV

CSV类

Parent:ObjectIncluded modules:Enumerable

该课程提供了CSV文件和数据的完整界面。它提供了一些工具,使您能够根据需要读写字符串或IO对象。

从一个文件

一次一行

CSV.foreach("path/to/file.csv") do |row| # use row here... end

一次全部

arr_of_arrs = CSV.read("path/to/file.csv")

来自字符串

一次一行

CSV.parse("CSV,data,String") do |row| # use row here... end

一次全部

arr_of_arrs = CSV.parse("CSV,data,String")

写作

到一个文件

CSV.open("path/to/file.csv", "wb") do |csv| csv << ["row", "of", "CSV", "data"] csv << ["another", "row"] # ... end

到一个字符串

csv_string = CSV.generate do |csv| csv << ["row", "of", "CSV", "data"] csv << ["another", "row"] # ... end

转换单行

csv_string = ["CSV", "data"].to_csv # to CSV csv_array = "CSV,String".parse_csv # from CSV

快捷方式界面

CSV { |csv_out| csv_out << %w{my data here} } # to $stdout CSV(csv = "") { |csv_str| csv_str << %w{my data here} } # to a String CSV($stderr) { |csv_err| csv_err << %w{my data here} } # to $stderr CSV($stdin) { |csv_in| csv_in.each { |row| p row } } # from $stdin

高级用法

包装一个IO对象

csv = CSV.new(io, options) # ... read (with gets() or each()) from and write (with <<) to csv here ...

CSV和字符编码(M17n或多语言化)

这个新的CSV解析器是m17n精明的。解析器在正在读取或写入的IO或String对象的编码中工作。您的数据永远不会被转码(除非您要求Ruby为您进行转码),并且将在其编码中进行解析。因此,CSV将在您的数据编码中返回数组或字符串行。这是通过将解析器本身转码为您的Encoding来完成的。

当然,必须进行一些转码才能实现这种多编码支持。例如:col_sep:row_sep:quote_char必须进行转码,以配合您的数据。希望这可以让整个过程变得透明,因为CSV的默认值应该可以神奇地为您的数据工作。但是,您可以在目标编码中手动设置这些值以避免翻译。

同样重要的是要注意,虽然CSV的所有核心解析器现在都是Encoding不可知的,但有些功能却不是。例如,内置转换器将在转换之前尝试将数据转码为UTF-8。同样,您可以提供知道您的Encodings的自定义转换器以避免此翻译。对于我来说,在所有Ruby的Encodings中支持本地转换是非常困难的。

无论如何,这很简单:确保传入CSV的IO和String对象具有正确的Encoding集合,并且所有内容都应该可以正常工作。允许您打开IO对象(CSV :: foreach(),:: open,:: read和:: readlines)的CSV方法允许您指定编码。

使用不兼容ASCII的编码将CSV生成为字符串时会出现一个小例外。CSV没有现成的数据用于自我准备,因此您可能需要为大多数情况手动指定所需的编码。当使用:: generate_line或Array#to_csv()时,它会尝试使用输出行中的字段进行猜测。

我试图在方法文档中指出任何其他编码问题。

我已尽最大努力测试了所有非“虚拟”编码Ruby所附带的功能。但是,这是一个勇敢的新代码,可能有一些错误。请随时报告您发现的任何问题。

常量

ConverterEncoding

所有转换器使用的编码。

Converters

这个Hash包含可以按名称访问的CSV内置转换器。您可以使用#convert或通过options哈希传递给:: new 来选择转换器。

:integer

转换Integer()接受的任何字段。

:float

转换Float()接受的任何字段。

:numeric

的组合:integer:float

:date

转换Date.parse接受的任何字段。

:date_time

转换DateTime.parse接受的任何字段。

:all

所有内置转换器。的组合:date_time:numeric

所有内置转换器在尝试转换之前都将字段数据转码为UTF-8。如果您的数据无法转码为UTF-8,转换将失败,该字段将保持不变。

此散列有意留下解冻,用户应该随时向其添加可供所有CSV对象访问的值。

要添加组合字段,该值应该是一个名称数组。组合字段可以与其他组合字段嵌套。

DEFAULT_OPTIONS

未通过调用代码提供覆盖时使用的选项。他们是:

:col_sep

","

:row_sep

:auto

:quote_char

'"'

:field_size_limit

nil

:converters

nil

:unconverted_fields

nil

:headers

false

:return_headers

false

:header_converters

nil

:skip_blanks

false

:force_quotes

false

:skip_lines

nil

:liberal_parsing

false

DateMatcher

正则表达式用于查找和转换一些常见的日期格式。

DateTimeMatcher

正则表达式用于查找和转换一些常见的DateTime格式。

FieldInfo

FieldInfo Struct包含有关在读取数据源时字段位置的详细信息。CSV会将此结构传递给一些根据字段结构做出决定的块。有关示例,请参阅#convert_fields。

index

该行中该字段的从零开始的索引。

line

该行的数据源的行来自。

header

该列的标题(如果可用)。

HeaderConverters

这个Hash包含可以通过名称访问的CSV内置标题转换器。您可以使用#header_convert或通过options传递给:: new 的哈希来选择HeaderConverters 。

:downcase

在标题String上调用downcase()。

:symbol

前导/尾随空格被删除,字符串被降低,剩余空间被替换为下划线,非字字符被删除,最后调用to_sym()。

所有内置标题转换器在尝试转换之前都会将标题数据转码为UTF-8。如果您的数据无法转码为UTF-8,转换将失败并且标题保持不变。

此散列有意留下解冻,用户应该随时向其添加可供所有CSV对象访问的值。

要添加组合字段,该值应该是一个名称数组。组合字段可以与其他组合字段嵌套。

VERSION

已安装库的版本。

属性

col_sepR

编码:col_sep用于解析和写入。详情请参阅:: new。

encodingR

Encoding CSV正在解析或写入。这将是Encoding数据将被写入,并且/或Encoding数据将被写入。

field_size_limitR

字段大小的限制(如果有的话)。详情请参阅:: new。

linenoR

从该文件读取的最后一行的行号。嵌套行尾字符的字段不会影响此计数。

quote_charR

编码:quote_char用于解析和写入。详情请参阅:: new。

row_sepR

编码:row_sep用于解析和写入。详情请参阅:: new。

skip_linesR

正则表达式将行标记为注释。详情请参阅:: new

公共类方法

filter( options = Hash.new ) { |row| ... } Show source

filter( input, options = Hash.new ) { |row| ... }

filter( input, output, options = Hash.new ) { |row| ... }

这种方法对于为CSV数据构建类Unix过滤器很方便。每行都被提供给提供的块,可以根据需要对其进行更改。块返回后,该行被添加到output修改或不修改。

inputoutput参数可以是任何东西::新接受(一般字符串或IO对象)。如果没有给出,他们默认为ARGF$stdout

在一些聪明的键解析之后,options参数也被过滤到:: new。 以in_或:input_开头的任何键都将剥离该前导标识符,并且只会在输入对象的选项哈希中使用。 以......开头的键:out_或:output_只影响输出。 所有其他键都分配给两个对象。

:output_row_sep option默认为$INPUT_RECORD_SEPARATOR$/)。

# File lib/csv.rb, line 1102 def self.filter(*args) # parse options for input, output, or both in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR} if args.last.is_a? Hash args.pop.each do |key, value| case key.to_s when /\Ain(?:put)?_(.+)\Z/ in_options[$1.to_sym] = value when /\Aout(?:put)?_(.+)\Z/ out_options[$1.to_sym] = value else in_options[key] = value out_options[key] = value end end end # build input and output wrappers input = new(args.shift || ARGF, in_options) output = new(args.shift || $stdout, out_options) # read, yield, write input.each do |row| yield row output << row end end

foreach(path, options = Hash.new, &block) Show source

此方法旨在作为读取CSV文件的主要界面。 你传递一个路径和任何你想要为读取设置的选项。 文件的每一行都会依次传递给提供的块。

options参数可以是任何东西::新理解。此方法还了解:encoding可用于指定要读取的文件中数据的编码的附加参数。除非数据在Encoding.default_external中,否则您必须提供此信息。CSV将使用它来确定如何解析数据。您可以提供第二个编码,以便在读取数据时对其进行转码。例如,encoding: "UTF-32BE:UTF-8"将从文件中读取UTF-32BE数据,但在CSV解析之前将其转码为UTF-8。

# File lib/csv.rb, line 1143 def self.foreach(path, options = Hash.new, &block) return to_enum(__method__, path, options) unless block open(path, options) do |csv| csv.each(&block) end end

generate( str, options = Hash.new ) { |csv| ... } Show source

generate( options = Hash.new ) { |csv| ... }

该方法将一个您提供的字符串或一个空的默认字符串包装到传递给提供的块的CSV对象中。您可以使用该块将CSV行附加到字符串,并在块退出时返回最终的字符串。

请注意,传递的字符串用这种方法修改。如果需要新的String,请在传递前调用dup()。

options参数可以是任何东西::新理解。此方法:encoding在未传递字符串以设置输出的基本编码时理解附加参数。如果您打算输出非ASCII兼容数据,则CSV需要此提示。

# File lib/csv.rb, line 1168 def self.generate(*args) # add a default empty String, if none was given if args.first.is_a? String io = StringIO.new(args.shift) io.seek(0, IO::SEEK_END) args.unshift(io) else encoding = args[-1][:encoding] if args.last.is_a?(Hash) str = String.new str.force_encoding(encoding) if encoding args.unshift(str) end csv = new(*args) # wrap yield csv # yield for appending csv.string # return final String end

generate_line(row, options = Hash.new) Show source

此方法是将单行(数组)转换为CSV字符串的快捷方式。

options参数可以是任何东西::新理解。此方法理解一个附加:encoding参数以设置输出的基本编码。如果可能,此方法将尝试从第一个非nil字段中猜测您的编码row,但您可能需要将此参数用作备份计划。

:row_sep option默认$INPUT_RECORD_SEPARATOR$/调用此方法时)。

# File lib/csv.rb, line 1198 def self.generate_line(row, options = Hash.new) options = {row_sep: $INPUT_RECORD_SEPARATOR}.merge(options) encoding = options.delete(:encoding) str = String.new if encoding str.force_encoding(encoding) elsif field = row.find { |f| not f.nil? } str.force_encoding(String(field).encoding) end (new(str, options) << row).string end

instance(data = $stdout, options = Hash.new) { |instance| ... } Show source

此方法将返回一个CSV实例,就像:: new一样,但实例将被缓存,并将在未来对同一data对象的此方法的所有调用(由Object#object_id测试)进行调用并返回options

如果给出了一个块,实例将被传递给该块,并且返回值将成为该块的返回值。

# File lib/csv.rb, line 1062 def self.instance(data = $stdout, options = Hash.new) # create a _signature_ for this method call, data object and options sig = [data.object_id] + options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s }) # fetch or create the instance for this signature @@instances ||= Hash.new instance = (@@instances[sig] ||= new(data, options)) if block_given? yield instance # run block, if given, returning result else instance # or return the instance end end

new(data, options = Hash.new) Show source

此构造函数将包装传入的data用于读取和/或写入的String或IO对象。除了CSV实例方法之外,还委派了几种IO方法。(查看::打开一个完整的列表。)如果你传递一个String data,你可以稍后用CSV.string()来获取它(例如在写入之后)。

请注意,包装的字符串将位于开头(用于阅读)。如果你想在最后(写作),使用::生成。如果您需要其他定位,请改为传递预设的StringIO对象。

你可以在options哈希中设置任何阅读和/或写作偏好。可用的选项有:

:col_sep

字符串放置在每个字段之间。该字符串将在解析之前转码为数据的编码。

:row_sep

附加到每行末尾的字符串。 这可以设置为special:auto设置,它会要求CSV自动从数据中发现这一点。 自动发现预读数据,查找下一个“\ r \ n”,“\ n”或“\ r”序列。 即使它出现在带引号的字段中,也会选择一个序列,假定您将在那里具有相同的行结束符。 如果没有找到这些序列,那么数据是ARGF,STDIN,STDOUT或STDERR,或者流只用于输出,则使用默认的$ INPUT_RECORD_SEPARATOR($ /)。 显然,发现需要一点时间。 如果速度很重要,请手动设置。 另请注意,如果使用此功能,则应在Windows上以二进制模式打开IO对象,因为行结束转换可能会导致将文档位置重置为读取前的位置。 该字符串将在解析之前转码为数据的Encoding。

:quote_char

用于引用字段的字符。这必须是单个字符的字符串。这对于错误地'用作引号字符而不是正确的应用程序很有用"。CSV将始终将此字符的双重序列视为转义引用。该字符串将在解析之前转码为数据的编码。

:field_size_limit

这是一个最大尺寸的CSV,将在前面查看字段的结束报价。(实际上,它会读到超出此大小的第一行。)如果在限制范围内找不到报价,则会假设数据有问题,CSV将引发格式错误的CAVE错误。您可以使用此限制来防止对解析器进行有效的DoS攻击。但是,此限制会导致合法的分析失败,因此nil默认情况下会设置为或关闭。

:converters

来自处理自定义转换的转换器哈希和/或lambda表达式的数组。单个转换器不必位于阵列中。所有内置转换器在转换之前都会尝试将字段转码为UTF-8。如果数据无法转码,转换将失败,并保持字段不变。

:unconverted_fields

如果设置为true,则将向所有返回的行(Array或CSV :: Row)添加一个unconverted_fields()方法,该方法将返回转换前的字段。请注意,:headers由数组或字符串提供的不是文档的字段,因此将有一个空数组。

:headers

如果设置为:first_row或true,则CSV文件的第一行将被视为一行标题。 如果设置为数组,则内容将用作标题。 如果设置为String,则该字符串将通过:: parse_line调用运行,该调用具有相同的:col_sep,:row_sep和:quote_char作为此实例以生成一个数组标题。 此设置会导致#shift将行作为CSV :: Row对象而不是数组返回,而将#read返回为CSV :: Table对象而不是阵列数组。

:return_headers

何时false,标题行被无声地吞下。如果设置为true,标题行将返回到具有相同标题和字段的CSV :: Row对象中(除字段不通过转换器)。

:write_headers

true:headers被设置时,标题行将被添加到输出中。

:header_converters

功能相同以:converters保存仅转换为标题行的转换。所有内置转换器在转换之前都会尝试将标题转换为UTF-8。如果数据无法转码,则转换将失败,并保持标题不变。

:skip_blanks

设置为true值时,CSV将跳过任何空行。请注意,即使行不包含实际数据,此设置也不会跳过包含列分隔符的行。如果您想跳过包含分隔符但没有内容的行,请考虑使用:skip_lines或检查fields.compact.empty?在每一行上。

:force_quotes

设置为true值时,CSV会引用它创建的所有CSV字段。

:skip_lines

当设置为响应的对象时match,与其匹配的每一行都被视为注释,并在解析过程中被忽略。设置为字符串时,首先将其转换为正则表达式。设置为nil不行时被视为评论。如果传递的对象没有响应matchArgumentError则抛出。

:liberal_parsing

设置为true值时,CSV将尝试解析与RFC 4180不符合的输入,例如未加引号的字段中的双引号。

有关默认设置,请参阅CSV :: DEFAULT_OPTIONS。

出于性能原因,选项不能在实例方法中重写,所以一定要在这里设置你想要的。

# File lib/csv.rb, line 1527 def initialize(data, options = Hash.new) if data.nil? raise ArgumentError.new("Cannot parse nil as CSV") end # build the options for this read/write options = DEFAULT_OPTIONS.merge(options) # create the IO object we will read from @io = data.is_a?(String) ? StringIO.new(data) : data # honor the IO encoding if we can, otherwise default to ASCII-8BIT @encoding = raw_encoding(nil) || ( if encoding = options.delete(:internal_encoding) case encoding when Encoding; encoding else Encoding.find(encoding) end end ) || ( case encoding = options.delete(:encoding) when Encoding; encoding when /\A[^:]+/; Encoding.find($&) end ) || Encoding.default_internal || Encoding.default_external # # prepare for building safe regular expressions in the target encoding, # if we can transcode the needed characters # @re_esc = "\\".encode(@encoding).freeze rescue "" @re_chars = /#{%"[-\\]\\[\\.^$?*+{}()|# \r\n\t\f\v]".encode(@encoding)}/ init_separators(options) init_parsers(options) init_converters(options) init_headers(options) init_comments(options) @force_encoding = !!(encoding || options.delete(:encoding)) options.delete(:internal_encoding) options.delete(:external_encoding) unless options.empty? raise ArgumentError, "Unknown options: #{options.keys.join(', ')}." end # track our own lineno since IO gets confused about line-ends is CSV fields @lineno = 0 end

open( filename, mode = "rb", options = Hash.new ) { |faster_csv| ... } Show source

open( filename, options = Hash.new ) { |faster_csv| ... }

open( filename, mode = "rb", options = Hash.new )

open( filename, options = Hash.new )

此方法打开一个IO对象,并用CSV将其打包。这是用于编写CSV文件的主要界面。

你必须传递一个,filename并可以选择添加一个modeRuby的open()。你也可以传递一个可选的包含任何options:: new的Hash 作为最终的参数。

此方法与Ruby的open()调用类似,因为它会将CSV对象传递给提供的块并在块终止时关闭它,或者在未提供块时返回CSV对象。(注意:这与将行传递给块的Ruby 1.8 CSV库不同,使用:: foreach表示该行为。)

除非数据在Encoding.default_external中,否则您必须提供带有嵌入式编码标志的模式。 CSV将检查底层IO对象的编码(通过您传递的模式设置)以确定如何解析数据。 您可以提供第二个编码,以便在正常调用IO.open时正确读取数据时对其进行转码。 例如,“rb:UTF-32BE:UTF-8”将从文件读取UTF-32BE数据,但在CSV解析之前将其转码为UTF-8。

为方便起见,打开的CSV对象将委托给许多IO方法。您可以致电:

  • binmode()

  • binmode?()

  • close()

  • close_read()

  • close_write()

  • closed?()

  • eof()

  • eof?()

  • external_encoding()

  • fcntl()

  • fileno()

  • flock()

  • flush()

  • fsync()

  • internal_encoding()

  • ioctl()

  • isatty()

  • path()

  • pid()

  • pos()

  • pos=()

  • reopen()

  • seek()

  • stat()

  • sync()

  • sync=()

  • tell()

  • to_i()

  • to_io()

  • truncate()

  • tty?()

# File lib/csv.rb, line 1273 def self.open(*args) # find the +options+ Hash options = if args.last.is_a? Hash then args.pop else Hash.new end # wrap a File opened with the remaining +args+ with no newline # decorator file_opts = {universal_newline: false}.merge(options) begin f = File.open(*args, file_opts) rescue ArgumentError => e raise unless /needs binmode/ =~ e.message and args.size == 1 args << "rb" file_opts = {encoding: Encoding.default_external}.merge(file_opts) retry end begin csv = new(f, options) rescue Exception f.close raise end # handle blocks like Ruby's open(), not like the CSV library if block_given? begin yield csv ensure csv.close end else csv end end

parse( str, options = Hash.new ) { |row| ... } Show source

parse( str, options = Hash.new )

此方法可用于轻松地将CSV从字符串中解析出来。 您可以提供一个块,它将依次在字符串的每一行中调用,或者仅使用返回的数组Array(当没有给出块时)。

你通过你的str来读取,并且包含任何东西的可选选项Hash :: new理解。

# File lib/csv.rb, line 1318 def self.parse(*args, &block) csv = new(*args) if block.nil? # slurp contents, if no block is given begin csv.read ensure csv.close end else # or pass each row to a provided block csv.each(&block) end end

parse_line(line, options = Hash.new) Show source

此方法是将CSV字符串的单行转换为数组的快捷方式。 请注意,如果行包含多行,则第一行之外的任何内容都将被忽略。

options参数可以是任何::新理解。

# File lib/csv.rb, line 1338 def self.parse_line(line, options = Hash.new) new(line, options).shift end

read(path, *options) Show source

用于将CSV文件粘贴到阵列数组中。 将路径传递给文件,任何选项:: new都可以理解。 此方法还了解一个额外的:编码参数,您可以使用该参数指定要读取的文件中的数据的编码。 除非数据在Encoding.default_external中,否则您必须提供此信息。 CSV将使用它来确定如何解析数据。 您可以提供第二个编码,以便在读取数据时对其进行转码。 例如,编码:“UTF-32BE:UTF-8”将从文件读取UTF-32BE数据,但在CSV解析之前将其转码为UTF-8。

# File lib/csv.rb, line 1353 def self.read(path, *options) open(path, *options) { |csv| csv.read } end

readlines(*args) Show source

别名为:: read。

# File lib/csv.rb, line 1358 def self.readlines(*args) read(*args) end

table(path, options = Hash.new) Show source

快捷方式:

CSV.read( path, { headers: true, converters: :numeric, header_converters: :symbol }.merge(options) )

# File lib/csv.rb, line 1369 def self.table(path, options = Hash.new) read( path, { headers: true, converters: :numeric, header_converters: :symbol }.merge(options) ) end

公共实例方法

<<(row) Show source

包装字符串和IO的主要写入方法row(数组或CSV ::行)被转换为CSV并附加到数据源。当传递一个CSV :: Row时,只有行的字段()被附加到输出。

数据源必须公开才能写入。

# File lib/csv.rb, line 1686 def <<(row) # make sure headers have been assigned if header_row? and [Array, String].include? @use_headers.class parse_headers # won't read data for Array or String self << @headers if @write_headers end # handle CSV::Row objects and Hashes row = case row when self.class::Row then row.fields when Hash then @headers.map { |header| row[header] } else row end @headers = row if header_row? @lineno += 1 output = row.map(&@quote).join(@col_sep) + @row_sep # quote and separate if @io.is_a?(StringIO) and output.encoding != (encoding = raw_encoding) if @force_encoding output = output.encode(encoding) elsif (compatible_encoding = Encoding.compatible?(@io.string, output)) @io.set_encoding(compatible_encoding) @io.seek(0, IO::SEEK_END) end end @io << output self # for chaining end

Also aliased as: add_row, puts

add_row(row)

Alias for: <<

convert( name ) Show source

convert { |field| ... }

convert { |field, field_info| ... }

您可以使用此方法安装内置的CSV :: Converters,或提供处理自定义转换的块。

如果您提供一个带有一个参数的块,它将通过该域并预期返回转换后的值或域本身。如果你的块有两个参数,它也将被传递一个CSV :: FieldInfo Struct,包含关于这个域的详细信息。同样,块应该返回一个转换的字段或字段本身。

# File lib/csv.rb, line 1735 def convert(name = nil, &converter) add_converter(:converters, self.class::Converters, name, &converter) end

converters() Show source

返回当前有效的转换器列表。详情请参阅:: new。内置转换器将按名称返回,而其他转换器将按原样返回。

# File lib/csv.rb, line 1600 def converters @converters.map do |converter| name = Converters.rassoc(converter) name ? name.first : converter end end

each() { |row| ... } Show source

Yields each row of the data source in turn.

支持Enumerable。

数据源必须打开以供阅读。

# File lib/csv.rb, line 1766 def each if block_given? while row = shift yield row end else to_enum end end

force_quotes?() Show source

如果所有输出字段都被引用,则返回true。 详情请参阅:: new。

# File lib/csv.rb, line 1643 def force_quotes?() @force_quotes end

gets()

别名:shift

header_convert( name ) Show source

header_convert { |field| ... }

header_convert { |field, field_info| ... }

与#convert相同,但是用于标题行。

请注意,必须在读取标题行之前调用此方法才能产生任何效果。

# File lib/csv.rb, line 1750 def header_convert(name = nil, &converter) add_converter( :header_converters, self.class::HeaderConverters, name, &converter ) end

header_converters() Show source

返回对头文件有效的转换器的当前列表。详情请参阅:: new。内置转换器将按名称返回,而其他转换器将按原样返回。

# File lib/csv.rb, line 1631 def header_converters @header_converters.map do |converter| name = HeaderConverters.rassoc(converter) name ? name.first : converter end end

header_row?() Show source

如果读取的下一行将是标题行,则返回true。

# File lib/csv.rb, line 1792 def header_row? @use_headers and @headers.nil? end

headers() Show source

如果标题不被使用,则返回nil;如果它们尚未被读取,则返回true,或者读取后的实际标头。 详情请参阅:: new。

# File lib/csv.rb, line 1616 def headers @headers || true if @use_headers end

inspect() Show source

以ASCII兼容字符串的形式返回关键CSV属性的简单描述。

# File lib/csv.rb, line 1960 def inspect str = ["<#", self.class.to_s, " io_type:"] # show type of wrapped IO if @io == $stdout then str << "$stdout" elsif @io == $stdin then str << "$stdin" elsif @io == $stderr then str << "$stderr" else str << @io.class.to_s end # show IO.path(), if available if @io.respond_to?(:path) and (p = @io.path) str << " io_path:" << p.inspect end # show encoding str << " encoding:" << @encoding.name # show other attributes %w[ lineno col_sep row_sep quote_char skip_blanks liberal_parsing ].each do |attr_name| if a = instance_variable_get("@#{attr_name}") str << " " << attr_name << ":" << a.inspect end end if @use_headers str << " headers:" << headers.inspect end str << ">" begin str.join('') rescue # any encoding error str.map do |s| e = Encoding::Converter.asciicompat_encoding(s.encoding) e ? s.encode(e) : s.force_encoding("ASCII-8BIT") end.join('') end end

liberal_parsing?() Show source

如果处理了非法输入,则返回true。 详情请参阅:: new。

# File lib/csv.rb, line 1645 def liberal_parsing?() @liberal_parsing end

puts(row)

别名:<<

read() Show source

啜饮剩余的行并返回一个数组Array。

数据源必须打开以供阅读。

# File lib/csv.rb, line 1781 def read rows = to_a if @use_headers Table.new(rows) else rows end end

另外别名为:readlines

readline()

别名:shift

readlines()

别名为:read

return_headers?() Show source

如果标题将作为一行结果返回,则返回true。 详情请参阅:: new。

# File lib/csv.rb, line 1623 def return_headers?() @return_headers end

rewind() Show source

回滚底层IO对象并重置CSV的lineno()计数器。

# File lib/csv.rb, line 1670 def rewind @headers = nil @lineno = 0 @io.rewind end

shift() Show source

包装字符串和IO的主要读取方法是从数据源中提取单行,解析并返回字段数组(如果未使用标题行)或CSV ::行(使用标题行时)。

数据源必须打开以供阅读。

# File lib/csv.rb, line 1803 def shift ######################################################################### ### This method is purposefully kept a bit long as simple conditional ### ### checks are faster than numerous (expensive) method calls. ### ######################################################################### # handle headers not based on document content if header_row? and @return_headers and [Array, String].include? @use_headers.class if @unconverted_fields return add_unconverted_fields(parse_headers, Array.new) else return parse_headers end end # # it can take multiple calls to <tt>@io.gets()</tt> to get a full line, # because of \r and/or \n characters embedded in quoted fields # in_extended_col = false csv = Array.new loop do # add another read to the line unless parse = @io.gets(@row_sep) return nil end parse.sub!(@parsers[:line_end], "") if csv.empty? # # I believe a blank line should be an <tt>Array.new</tt>, not Ruby 1.8 # CSV's <tt>[nil]</tt> # if parse.empty? @lineno += 1 if @skip_blanks next elsif @unconverted_fields return add_unconverted_fields(Array.new, Array.new) elsif @use_headers return self.class::Row.new(Array.new, Array.new) else return Array.new end end end next if @skip_lines and @skip_lines.match parse parts = parse.split(@col_sep, -1) if parts.empty? if in_extended_col csv[-1] << @col_sep # will be replaced with a @row_sep after the parts.each loop else csv << nil end end # This loop is the hot path of csv parsing. Some things may be non-dry # for a reason. Make sure to benchmark when refactoring. parts.each do |part| if in_extended_col # If we are continuing a previous column if part[-1] == @quote_char && part.count(@quote_char) % 2 != 0 # extended column ends csv[-1] = csv[-1].push(part[0..-2]).join("") if csv.last =~ @parsers[:stray_quote] raise MalformedCSVError, "Missing or stray quote in line #{lineno + 1}" end csv.last.gsub!(@quote_char * 2, @quote_char) in_extended_col = false else csv.last.push(part, @col_sep) end elsif part[0] == @quote_char # If we are starting a new quoted column if part.count(@quote_char) % 2 != 0 # start an extended column csv << [part[1..-1], @col_sep] in_extended_col = true elsif part[-1] == @quote_char # regular quoted column csv << part[1..-2] if csv.last =~ @parsers[:stray_quote] raise MalformedCSVError, "Missing or stray quote in line #{lineno + 1}" end csv.last.gsub!(@quote_char * 2, @quote_char) elsif @liberal_parsing csv << part else raise MalformedCSVError, "Missing or stray quote in line #{lineno + 1}" end elsif part =~ @parsers[:quote_or_nl] # Unquoted field with bad characters. if part =~ @parsers[:nl_or_lf] raise MalformedCSVError, "Unquoted fields do not allow " + "\\r or \\n (line #{lineno + 1})." else if @liberal_parsing csv << part else raise MalformedCSVError, "Illegal quoting in line #{lineno + 1}." end end else # Regular ole unquoted field. csv << (part.empty? ? nil : part) end end # Replace tacked on @col_sep with @row_sep if we are still in an extended # column. csv[-1][-1] = @row_sep if in_extended_col if in_extended_col # if we're at eof?(), a quoted field wasn't closed... if @io.eof? raise MalformedCSVError, "Unclosed quoted field on line #{lineno + 1}." elsif @field_size_limit and csv.last.sum(&:size) >= @field_size_limit raise MalformedCSVError, "Field size exceeded on line #{lineno + 1}." end # otherwise, we need to loop and pull some more data to complete the row else @lineno += 1 # save fields unconverted fields, if needed... unconverted = csv.dup if @unconverted_fields # convert fields, if needed... csv = convert_fields(csv) unless @use_headers or @converters.empty? # parse out header rows and handle CSV::Row conversions... csv = parse_headers(csv) if @use_headers # inject unconverted fields and accessor, if requested... if @unconverted_fields and not csv.respond_to? :unconverted_fields add_unconverted_fields(csv, unconverted) end # return the results break csv end end end

另外别名为:gets,readline

skip_blanks?() Show source

返回真正的空白行被解析器跳过。 详情请参阅:: new。

# File lib/csv.rb, line 1641 def skip_blanks?() @skip_blanks end

unconverted_fields?() Show source

如果未对解析结果进行unconverted_fields(),则返回true。 详情请参阅:: new。

# File lib/csv.rb, line 1610 def unconverted_fields?() @unconverted_fields end

write_headers?() Show source

如果标题写入输出中,则返回true。 详情请参阅:: new。

# File lib/csv.rb, line 1625 def write_headers?() @write_headers end

私有实例方法

add_converter(var_name, const, name = nil, &converter) Show source

添加转换器的实际工作方法,由#convert和#header_convert使用。

此方法需要实例变量的var_name放置转换器,const哈希来查找已命名的转换器,以及#convert和#header_convert方法的常规参数。

# File lib/csv.rb, line 2200 def add_converter(var_name, const, name = nil, &converter) if name.nil? # custom converter instance_variable_get("@#{var_name}") << converter else # named converter combo = const[name] case combo when Array # combo converter combo.each do |converter_name| add_converter(var_name, const, converter_name) end else # individual named converter instance_variable_get("@#{var_name}") << combo end end end

add_unconverted_fields(row, fields) Show source

此方法将实例变量unconverted_fields插入行,并将行的访问器方法插入名为unconverted_fields()的方法。 该变量被设置为字段的内容。

# File lib/csv.rb, line 2287 def add_unconverted_fields(row, fields) class << row attr_reader :unconverted_fields end row.instance_eval { @unconverted_fields = fields } row end

convert_fields(fields, headers = false) Show source

使用@converters或@header_converters处理字段,如果标头传递为true,则返回转换后的字段集。 任何将该字段更改为字符串之外的转换器会暂停该字段的转换管道。 这主要是效率捷径。

# File lib/csv.rb, line 2223 def convert_fields(fields, headers = false) # see if we are converting headers or fields converters = headers ? @header_converters : @converters fields.map.with_index do |field, index| converters.each do |converter| break if field.nil? field = if converter.arity == 1 # straight field converter converter[field] else # FieldInfo converter header = @use_headers && !headers ? @headers[index] : nil converter[field, FieldInfo.new(index, lineno, header)] end break unless field.is_a? String # short-circuit pipeline for speed end field # final state of each field, converted or original end end

encode_re(*chunks) Show source

在中建立一个正则表达式@encoding。所有chunks将被转码为该编码。

# File lib/csv.rb, line 2310 def encode_re(*chunks) Regexp.new(encode_str(*chunks)) end

encode_str(*chunks) Show source

在@encoding中构建一个字符串。 所有块将被转码为该编码。

# File lib/csv.rb, line 2318 def encode_str(*chunks) chunks.map { |chunk| chunk.encode(@encoding.name) }.join('') end

escape_re(str) Show source

此方法是Regexp.escape的编码安全版本。 它会转义任何会改变str编码中正则表达式含义的字符。 无法转码为目标编码的正则表达式字符将被跳过,如果反斜线不能转码,则不会执行转义。

# File lib/csv.rb, line 2302 def escape_re(str) str.gsub(@re_chars) {|c| @re_esc + c} end

init_comments(options) Show source

存储评论模式以跳过所提供的选项。

模式必须响应.match,否则引发ArgumentError。字符串被转换为正则表达式。

另见:: new

# File lib/csv.rb, line 2185 def init_comments(options) @skip_lines = options.delete(:skip_lines) @skip_lines = Regexp.new(@skip_lines) if @skip_lines.is_a? String if @skip_lines and not @skip_lines.respond_to?(:match) raise ArgumentError, ":skip_lines has to respond to matches" end end

init_converters(options, field_name = :converters) Show source

装载施工期间要求的任何转换器。

如果设置了field_name:设置了转换器(默认)字段转换器。 当field_name是:header_converters标题转换器被添加。

如果需要,该:unconverted_fields选项也可用于:converters呼叫。

# File lib/csv.rb, line 2138 def init_converters(options, field_name = :converters) if field_name == :converters @unconverted_fields = options.delete(:unconverted_fields) end instance_variable_set("@#{field_name}", Array.new) # find the correct method to add the converters convert = method(field_name.to_s.sub(/ers\Z/, "")) # load converters unless options[field_name].nil? # allow a single converter not wrapped in an Array unless options[field_name].is_a? Array options[field_name] = [options[field_name]] end # load each converter... options[field_name].each do |converter| if converter.is_a? Proc # custom code block convert.call(&converter) else # by name convert.call(converter) end end end options.delete(field_name) end

init_headers(options) Show source

存储标题行设置并在需要时加载标题转换器。

# File lib/csv.rb, line 2168 def init_headers(options) @use_headers = options.delete(:headers) @return_headers = options.delete(:return_headers) @write_headers = options.delete(:write_headers) # headers must be delayed until shift(), in case they need a row of content @headers = nil init_converters(options, :header_converters) end

init_parsers(options) Show source

预编译解析器并按名称存储它们以便在读取期间进行访问。

# File lib/csv.rb, line 2106 def init_parsers(options) # store the parser behaviors @skip_blanks = options.delete(:skip_blanks) @field_size_limit = options.delete(:field_size_limit) @liberal_parsing = options.delete(:liberal_parsing) # prebuild Regexps for faster parsing esc_row_sep = escape_re(@row_sep) esc_quote = escape_re(@quote_char) @parsers = { # for detecting parse errors quote_or_nl: encode_re("[", esc_quote, "\r\n]"), nl_or_lf: encode_re("[\r\n]"), stray_quote: encode_re( "[^", esc_quote, "]", esc_quote, "[^", esc_quote, "]" ), # safer than chomp!() line_end: encode_re(esc_row_sep, "\\z"), # illegal unquoted characters return_newline: encode_str("\r\n") } end

init_separators(options) Show source

存储指示的分隔符以备后用。

如果为@row_sep请求自动发现,则此方法将在@io中读取并尝试找到一个。 ARGF,STDIN,STDOUT,STDERR和任何只有$ INPUT_RECORD_SEPARATOR($ /)默认@row_sep才会输出的流。

此方法还可以建立用于CSV输出的引用规则。

# File lib/csv.rb, line 2007 def init_separators(options) # store the selected separators @col_sep = options.delete(:col_sep).to_s.encode(@encoding) @row_sep = options.delete(:row_sep) # encode after resolving :auto @quote_char = options.delete(:quote_char).to_s.encode(@encoding) if @quote_char.length != 1 raise ArgumentError, ":quote_char has to be a single character String" end # # automatically discover row separator when requested # (not fully encoding safe) # if @row_sep == :auto if [ARGF, STDIN, STDOUT, STDERR].include?(@io) or (defined?(Zlib) and @io.class == Zlib::GzipWriter) @row_sep = $INPUT_RECORD_SEPARATOR else begin # # remember where we were (pos() will raise an exception if @io is pipe # or not opened for reading) # saved_pos = @io.pos while @row_sep == :auto # # if we run out of data, it's probably a single line # (ensure will set default value) # break unless sample = @io.gets(nil, 1024) # extend sample if we're unsure of the line ending if sample.end_with? encode_str("\r") sample << (@io.gets(nil, 1) || "") end # try to find a standard separator if sample =~ encode_re("\r\n?|\n") @row_sep = $& break end end # tricky seek() clone to work around GzipReader's lack of seek() @io.rewind # reset back to the remembered position while saved_pos > 1024 # avoid loading a lot of data into memory @io.read(1024) saved_pos -= 1024 end @io.read(saved_pos) if saved_pos.nonzero? rescue IOError # not opened for reading # do nothing: ensure will set default rescue NoMethodError # Zlib::GzipWriter doesn't have some IO methods # do nothing: ensure will set default rescue SystemCallError # pipe # do nothing: ensure will set default ensure # # set default if we failed to detect # (stream not opened for reading, a pipe, or a single line of data) # @row_sep = $INPUT_RECORD_SEPARATOR if @row_sep == :auto end end end @row_sep = @row_sep.to_s.encode(@encoding) # establish quoting rules @force_quotes = options.delete(:force_quotes) do_quote = lambda do |field| field = String(field) encoded_quote = @quote_char.encode(field.encoding) encoded_quote + field.gsub(encoded_quote, encoded_quote * 2) + encoded_quote end quotable_chars = encode_str("\r\n", @col_sep, @quote_char) @quote = if @force_quotes do_quote else lambda do |field| if field.nil? # represent +nil+ fields as empty unquoted fields "" else field = String(field) # Stringify fields # represent empty fields as empty quoted fields if field.empty? or field.count(quotable_chars).nonzero? do_quote.call(field) else field # unquoted field end end end end end

parse_headers(row = nil) Show source

此方法用于将完成的行转换为CSV :: Row。 标题行也可以在这里处理,或者通过返回具有相同标题和字段的CSV :: Row(除了字段不通过转换器)或通过读取它们返回字段行。 标题还保存在@headers中以供将来的行使用。

nilrow假定为不是基于流的实际行的标题行。

# File lib/csv.rb, line 2252 def parse_headers(row = nil) if @headers.nil? # header row @headers = case @use_headers # save headers # Array of headers when Array then @use_headers # CSV header String when String self.class.parse_line( @use_headers, col_sep: @col_sep, row_sep: @row_sep, quote_char: @quote_char ) # first row is headers else row end # prepare converted and unconverted copies row = @headers if row.nil? @headers = convert_fields(@headers, true) @headers.each { |h| h.freeze if h.is_a? String } if @return_headers # return headers return self.class::Row.new(@headers, row, true) elsif not [Array, String].include? @use_headers.class # skip to field row return shift end end self.class::Row.new(@headers, convert_fields(row)) # field row end

raw_encoding(default = Encoding::ASCII_8BIT) Show source

返回内部IO对象default的编码或者编码无法确定的编码。

# File lib/csv.rb, line 2328 def raw_encoding(default = Encoding::ASCII_8BIT) if @io.respond_to? :internal_encoding @io.internal_encoding || @io.external_encoding elsif @io.is_a? StringIO @io.string.encoding elsif @io.respond_to? :encoding @io.encoding else default end end