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
修改或不修改。
该input
和output
参数可以是任何东西::新接受(一般字符串或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
不行时被视为评论。如果传递的对象没有响应match
,ArgumentError
则抛出。
: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
并可以选择添加一个mode
Ruby的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中以供将来的行使用。
当nil
,row
假定为不是基于流的实际行的标题行。
# 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