非常教程

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
CSV
CSV::MalformedCSVError 详细
CSV::Row 详细
CSV::Table 详细
Ruby 2.4

Ruby 是一种面向对象、命令式、函数式、动态的通用编程语言,是世界上最优美而巧妙的语言。

主页 https://www.ruby-lang.org/
源码 https://github.com/ruby/ruby
版本 2.4
发布版本 2.4.1

Ruby 2.4目录

1.缩略 | Abbrev
2.ARGF
3.数组 | Array
4.Base64
5.基本对象 | BasicObject
6.基准测试 | Benchmark
7.BigDecimal
8.绑定 | Binding
9.CGI
10.类 | Class
11.比较 | Comparable
12.负责 | Complex
13.计算续体 | Continuation
14.覆盖 | Coverage
15.CSV
16.日期 | Date
17.日期时间 | DateTime
18.DBM
19.代理 | Delegator
20.摘要 | Digest
21.Dir
22.DRb
23.编码 | Encoding
24.枚举 | Enumerable
25.枚举 | Enumerator
26.ENV
27.ERB
28.错误 | Errors
29.Etc
30.期望值 | Exception
31.错误类 | FalseClass
32.Fiber
33.Fiddle
34.文件 | File
35.文件实用程序 | FileUtils
36.查找 | Find
37.浮点 | Float
38.Forwardable
39.GC
40.GDBM
41.GetoptLong
42.Hash
43.Integer
44.IO
45.IPAddr
46.JSON
47.Kernel
48.语言 | 3Language
49.记录 | Logger
50.编排 | Marshal
51.MatchData
52.数学 | Math
53.矩阵 | Matrix
54.方法 | Method
55.模型 | Module
56.监控 | Monitor
57. 互斥 | Mutex
58.Net
59.Net::FTP
60.Net::HTTP
61.Net::IMAP
62.Net::SMTP
63.NilClass
64.数字 | Numeric
65.对象 | Object
66.ObjectSpace
67.Observable
68.Open3
69.OpenSSL
70.OpenStruct
71.OpenURI
72.OptionParser
73.路径名 | Pathname
74.完整输出 | PrettyPrint
75.Prime
76.Proc
77.过程 | Process
78.PStore
79.PTY
80.队列 | Queue
81.随机 | Random
82.范围 | Range
83.合理的 | Rational
84.Readline
85.Regexp
86.Resolv
87.Ripper
88.RubyVM
89.Scanf
90.SDBM
91.SecureRandom
92.Set
93.Shell
94.信号 | Signal
95.Singleton
96.套接字 | Socket
97.字符串 | String
98.StringIO
99.StringScanner
100.结构 | Struct