类 CSV
CSV¶ ↑
CSV 数据¶ ↑
CSV(逗号分隔值)数据是表的文本表示
-
行分隔符分隔表格行。常见的行分隔符是换行符
"\n"
。 -
列分隔符分隔行中的字段。常见的列分隔符是逗号字符
","
。
此 CSV 字符串,行分隔符为 "\n"
,列分隔符为 ","
,具有三行和两列
"foo,0\nbar,1\nbaz,2\n"
尽管名为 CSV,但 CSV 表示可以使用不同的分隔符。
有关表的更多信息,请参阅 Wikipedia 文章“表(信息)”,尤其是其“简单表”部分
类 CSV¶ ↑
类 CSV 提供以下方法:
-
从 String 对象、文件(通过其文件路径)或 IO 对象解析 CSV 数据。
-
将 CSV 数据生成为 String 对象。
要使 CSV 可用,请执行以下操作:
require 'csv'
此处的示例都假定已完成此操作。
保持简单¶ ↑
CSV 对象有数十个实例方法,可以精细控制 CSV 数据的解析和生成。但是,对于许多需求,更简单的方法就足够了。
本节总结了 CSV 中的单例方法,这些方法允许您解析和生成数据,而无需显式创建 CSV 对象。有关详细信息,请单击链接。
简单解析¶ ↑
解析方法通常返回以下任一内容:
-
字符串数组的数组
-
外部数组是整个“表”。
-
每个内部数组都是一行。
-
每个字符串都是一个字段。
-
-
CSV::Table
对象。有关详细信息,请参阅带标题的 CSV。
解析字符串¶ ↑
要解析的输入可以是字符串
string = "foo,0\nbar,1\nbaz,2\n"
方法 CSV.parse
返回整个 CSV 数据
CSV.parse(string) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
方法 CSV.parse_line
仅返回第一行
CSV.parse_line(string) # => ["foo", "0"]
CSV 使用实例方法 String#parse_csv
扩展了 String 类,该方法也仅返回第一行
string.parse_csv # => ["foo", "0"]
通过文件路径解析¶ ↑
要解析的输入可以在文件中
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string)
方法 CSV.read
返回整个 CSV 数据
CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
方法 CSV.foreach
迭代,将每一行传递给给定的块
CSV.foreach(path) do |row| p row end
输出
["foo", "0"] ["bar", "1"] ["baz", "2"]
方法 CSV.table
将整个 CSV 数据作为 CSV::Table
对象返回
CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:3>
从打开的 IO 流解析¶ ↑
要解析的输入可以在打开的 IO 流中
方法 CSV.read
返回整个 CSV 数据
File.open(path) do |file| CSV.read(file) end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
方法 CSV.parse
也这样做
File.open(path) do |file| CSV.parse(file) end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
方法 CSV.parse_line
仅返回第一行
File.open(path) do |file| CSV.parse_line(file) end # => ["foo", "0"]
方法 CSV.foreach
迭代,将每一行传递给给定的块
File.open(path) do |file| CSV.foreach(file) do |row| p row end end
输出
["foo", "0"] ["bar", "1"] ["baz", "2"]
方法 CSV.table
将整个 CSV 数据作为 CSV::Table
对象返回
File.open(path) do |file| CSV.table(file) end # => #<CSV::Table mode:col_or_row row_count:3>
简单生成¶ ↑
方法 CSV.generate
返回一个 String;此示例使用方法 CSV#<<
追加要生成的行
output_string = CSV.generate do |csv| csv << ['foo', 0] csv << ['bar', 1] csv << ['baz', 2] end output_string # => "foo,0\nbar,1\nbaz,2\n"
方法 CSV.generate_line
返回一个字符串,其中包含从数组构造的单行
CSV.generate_line(['foo', '0']) # => "foo,0\n"
CSV 使用实例方法 Array#to_csv
扩展了 Array 类,该方法将 Array 转换为字符串
['foo', '0'].to_csv # => "foo,0\n"
“过滤” CSV¶ ↑
方法 CSV.filter
为 CSV 数据提供 Unix 样式过滤器。输入数据经过处理以形成输出数据
in_string = "foo,0\nbar,1\nbaz,2\n" out_string = '' CSV.filter(in_string, out_string) do |row| row[0] = row[0].upcase row[1] *= 4 end out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
CSV 对象¶ ↑
有三种创建 CSV 对象的方法:
-
方法
CSV.new
返回新的 CSV 对象。 -
方法
CSV.instance
返回新的或缓存的 CSV 对象。 -
方法 CSV() 也返回新的或缓存的 CSV 对象。
实例方法¶ ↑
CSV 有三组实例方法:
-
它自己内部定义的实例方法。
-
模块 Enumerable 包含的方法。
-
委托给类 IO 的方法。请参见下文。
委托方法¶ ↑
为了方便起见,CSV
对象将委托给类 IO 中的许多方法。(少数在 CSV 中具有包装“保护代码”。)您可以调用:
-
IO#binmode
-
IO#close
-
IO#close_read
-
IO#close_write
-
IO#closed?
-
IO#external_encoding
-
IO#fcntl
-
IO#fileno
-
IO#flush
-
IO#fsync
-
IO#internal_encoding
-
IO#isatty
-
IO#pid
-
IO#pos
-
IO#pos=
-
IO#reopen
-
IO#seek
-
IO#string
-
IO#sync
-
IO#sync=
-
IO#tell
-
IO#truncate
-
IO#tty?
选项¶ ↑
选项的默认值是:
DEFAULT_OPTIONS = { # For both parsing and generating. col_sep: ",", row_sep: :auto, quote_char: '"', # For parsing. field_size_limit: nil, converters: nil, unconverted_fields: nil, headers: false, return_headers: false, header_converters: nil, skip_blanks: false, skip_lines: nil, liberal_parsing: false, nil_value: nil, empty_value: "", strip: false, # For generating. write_headers: nil, quote_empty: true, force_quotes: false, write_converters: nil, write_nil_value: nil, write_empty_value: "", }
解析选项¶ ↑
下面详细介绍了解析选项,其中包括:
-
row_sep
:指定行分隔符;用于分隔行。 -
col_sep
:指定列分隔符;用于分隔字段。 -
quote_char
:指定引用字符;用于引用字段。 -
field_size_limit
:指定允许的最大字段大小 + 1。自 3.2.3 起已弃用。请改用max_field_size
。 -
max_field_size
:指定允许的最大字段大小。 -
converters
:指定要使用的字段转换器。 -
unconverted_fields
:指定是否提供未转换的字段。 -
headers
:指定数据是否包含标题,或指定标题本身。 -
return_headers
:指定是否返回标题。 -
header_converters
:指定要使用的标题转换器。 -
skip_blanks
:指定是否忽略空行。 -
skip_lines
:指定如何识别注释行。 -
strip
:指定是否从字段中剥离前导和尾随空格。这必须与col_sep
兼容;如果不兼容,则会引发ArgumentError
异常。 -
liberal_parsing
:指定 CSV 是否应尝试解析不合规的数据。 -
nil_value
:指定要替换每个空(无文本)字段的对象。 -
empty_value
:指定要替换每个空字段的对象。
选项 row_sep
¶ ↑
指定行分隔符,一个 String 或符号 :auto
(请参见下文),用于解析和生成。
默认值
CSV::DEFAULT_OPTIONS.fetch(:row_sep) # => :auto
当 row_sep
是字符串时,该字符串将成为行分隔符。String
将在使用前转码为数据的编码。
使用 "\n"
row_sep = "\n" str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 |
(管道)
row_sep = '|' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0|bar,1|baz,2|" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 --
(两个连字符)
row_sep = '--' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0--bar,1--baz,2--" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 ''
(空字符串)
row_sep = '' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0bar,1baz,2" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0bar", "1baz", "2"]]
当 row_sep
是符号 :auto
(默认值)时,生成使用 "\n"
作为行分隔符
str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n"
另一方面,解析会调用行分隔符的自动发现。
自动发现会在数据中向前读取,以查找下一个 \r\n
、\n
或 \r
序列。即使该序列出现在引用的字段中,也会选择该序列,前提是您在那里具有相同的行尾。
示例
str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
如果满足以下任一条件,则使用默认的 $INPUT_RECORD_SEPARATOR
($/
):
-
未找到这些序列中的任何一个。
-
数据为
ARGF
、STDIN
、STDOUT
或STDERR
。 -
流仅可用于输出。
显然,发现需要一些时间。如果速度很重要,请手动设置。另请注意,如果将使用此功能,则应在 Windows 上以二进制模式打开 IO 对象,因为行尾转换可能会导致将文档位置重置到读取前的位置出现问题。
选项 col_sep
¶ ↑
指定用于解析和生成的字符串列分隔符。在使用前,该字符串将被转码为数据所使用的编码。
默认值
CSV::DEFAULT_OPTIONS.fetch(:col_sep) # => "," (comma)
使用默认值(逗号)
str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 :
(冒号)
col_sep = ':' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo:0\nbar:1\nbaz:2\n" ary = CSV.parse(str, col_sep: col_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 ::
(两个冒号)
col_sep = '::' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo::0\nbar::1\nbaz::2\n" ary = CSV.parse(str, col_sep: col_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 ''
(空字符串)
col_sep = '' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo0\nbar1\nbaz2\n"
如果使用空字符串进行解析,则会引发异常
col_sep = '' # Raises ArgumentError (:col_sep must be 1 or more characters: "") CSV.parse("foo0\nbar1\nbaz2\n", col_sep: col_sep)
选项 quote_char
¶ ↑
指定用于在解析和生成中引用字段的字符(长度为 1 的字符串)。在使用前,此 String
将被转码为数据所使用的编码。
默认值
CSV::DEFAULT_OPTIONS.fetch(:quote_char) # => "\"" (double quote)
这对于错误地使用 '
(单引号)而不是正确的 "
(双引号)来引用字段的应用程序很有用。
使用默认值(双引号)
str = CSV.generate do |csv| csv << ['foo', 0] csv << ["'bar'", 1] csv << ['"baz"', 2] end str # => "foo,0\n'bar',1\n\"\"\"baz\"\"\",2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
使用 '
(单引号)
quote_char = "'" str = CSV.generate(quote_char: quote_char) do |csv| csv << ['foo', 0] csv << ["'bar'", 1] csv << ['"baz"', 2] end str # => "foo,0\n'''bar''',1\n\"baz\",2\n" ary = CSV.parse(str, quote_char: quote_char) ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
如果字符串长度大于 1,则会引发异常
# Raises ArgumentError (:quote_char has to be nil or a single character String) CSV.new('', quote_char: 'xx')
如果该值不是字符串,则会引发异常
# Raises ArgumentError (:quote_char has to be nil or a single character String) CSV.new('', quote_char: :foo)
选项 field_size_limit
¶ ↑
指定整数类型的字段大小限制。
默认值
CSV::DEFAULT_OPTIONS.fetch(:field_size_limit) # => nil
这是 CSV
在查找字段的结束引号时向前读取的最大大小。(实际上,它会读取到超出此大小的第一个行尾。)如果在限制范围内找不到引号,CSV
将引发 MalformedCSVError
,假设数据存在错误。您可以使用此限制来防止对解析器进行的有效 DoS 攻击。但是,此限制可能会导致合法解析失败;因此,默认值为 nil
(无限制)。
对于本节中的示例
str = <<~EOT "a","b" " 2345 ","" EOT str # => "\"a\",\"b\"\n\"\n2345\n\",\"\"\n"
使用默认值 nil
ary = CSV.parse(str) ary # => [["a", "b"], ["\n2345\n", ""]]
使用 50
field_size_limit = 50 ary = CSV.parse(str, field_size_limit: field_size_limit) ary # => [["a", "b"], ["\n2345\n", ""]]
如果字段过长,则会引发异常
big_str = "123456789\n" * 1024 # Raises CSV::MalformedCSVError (Field size exceeded in line 1.) CSV.parse('valid,fields,"' + big_str + '"', field_size_limit: 2048)
选项 converters
¶ ↑
指定在解析字段时要使用的转换器。请参阅 字段转换器
默认值
CSV::DEFAULT_OPTIONS.fetch(:converters) # => nil
该值可以是字段转换器名称(请参阅 存储的转换器)
str = '1,2,3' # Without a converter array = CSV.parse_line(str) array # => ["1", "2", "3"] # With built-in converter :integer array = CSV.parse_line(str, converters: :integer) array # => [1, 2, 3]
该值可以是转换器列表(请参阅 转换器列表)
str = '1,3.14159' # Without converters array = CSV.parse_line(str) array # => ["1", "3.14159"] # With built-in converters array = CSV.parse_line(str, converters: [:integer, :float]) array # => [1, 3.14159]
该值可以是 Proc 自定义转换器:(请参阅 自定义字段转换器)
str = ' foo , bar , baz ' # Without a converter array = CSV.parse_line(str) array # => [" foo ", " bar ", " baz "] # With a custom converter array = CSV.parse_line(str, converters: proc {|field| field.strip }) array # => ["foo", "bar", "baz"]
另请参阅 自定义字段转换器
如果转换器不是转换器名称或 Proc,则会引发异常
str = 'foo,0' # Raises NoMethodError (undefined method `arity' for nil:NilClass) CSV.parse(str, converters: :foo)
选项 unconverted_fields
¶ ↑
指定布尔值,该值确定是否可以使用未转换的字段值。
默认值
CSV::DEFAULT_OPTIONS.fetch(:unconverted_fields) # => nil
未转换的字段值是指在通过选项 converters
执行任何转换之前,在源数据中找到的值。
当选项 unconverted_fields
为 true
时,每个返回的行(数组或 CSV::Row)都添加了一个方法 unconverted_fields
,该方法返回未转换的字段值
str = <<-EOT foo,0 bar,1 baz,2 EOT # Without unconverted_fields csv = CSV.parse(str, converters: :integer) csv # => [["foo", 0], ["bar", 1], ["baz", 2]] csv.first.respond_to?(:unconverted_fields) # => false # With unconverted_fields csv = CSV.parse(str, converters: :integer, unconverted_fields: true) csv # => [["foo", 0], ["bar", 1], ["baz", 2]] csv.first.respond_to?(:unconverted_fields) # => true csv.first.unconverted_fields # => ["foo", "0"]
选项 headers
¶ ↑
指定用于定义列标题的布尔值、符号、数组或字符串。
默认值
CSV::DEFAULT_OPTIONS.fetch(:headers) # => false
没有 headers
str = <<-EOT Name,Count foo,0 bar,1 bax,2 EOT csv = CSV.new(str) csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\""> csv.headers # => nil csv.shift # => ["Name", "Count"]
如果设置为 true
或符号 :first_row
,则数据的第一个行被视为标题行
str = <<-EOT Name,Count foo,0 bar,1 bax,2 EOT csv = CSV.new(str, headers: true) csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:2 col_sep:"," row_sep:"\n" quote_char:"\"" headers:["Name", "Count"]> csv.headers # => ["Name", "Count"] csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
如果设置为数组,则数组元素被视为标题
str = <<-EOT foo,0 bar,1 bax,2 EOT csv = CSV.new(str, headers: ['Name', 'Count']) csv csv.headers # => ["Name", "Count"] csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
如果设置为字符串 str
,则会使用当前 options
调用方法 CSV::parse_line(str, options)
,并且返回的数组被视为标题
str = <<-EOT foo,0 bar,1 bax,2 EOT csv = CSV.new(str, headers: 'Name,Count') csv csv.headers # => ["Name", "Count"] csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
选项 return_headers
¶ ↑
指定布尔值,该值确定方法 shift
是返回还是忽略标题行。
默认值
CSV::DEFAULT_OPTIONS.fetch(:return_headers) # => false
示例
str = <<-EOT Name,Count foo,0 bar,1 bax,2 EOT # Without return_headers first row is str. csv = CSV.new(str, headers: true) csv.shift # => #<CSV::Row "Name":"foo" "Count":"0"> # With return_headers first row is headers. csv = CSV.new(str, headers: true, return_headers: true) csv.shift # => #<CSV::Row "Name":"Name" "Count":"Count">
选项 header_converters
¶ ↑
指定在解析标题时要使用的转换器。请参阅 标题转换器
默认值
CSV::DEFAULT_OPTIONS.fetch(:header_converters) # => nil
与选项 converters 的功能相同,只不过
-
转换器仅适用于标题行。
-
内置的标题转换器为
:downcase
和:symbol
。
本节假设之前执行了
str = <<-EOT Name,Value foo,0 bar,1 baz,2 EOT # With no header converter table = CSV.parse(str, headers: true) table.headers # => ["Name", "Value"]
该值可以是标题转换器名称(请参阅 存储的转换器)
table = CSV.parse(str, headers: true, header_converters: :downcase) table.headers # => ["name", "value"]
该值可以是转换器列表(请参阅 转换器列表)
header_converters = [:downcase, :symbol] table = CSV.parse(str, headers: true, header_converters: header_converters) table.headers # => [:name, :value]
该值可以是 Proc 自定义转换器(请参阅 自定义标题转换器)
upcase_converter = proc {|field| field.upcase } table = CSV.parse(str, headers: true, header_converters: upcase_converter) table.headers # => ["NAME", "VALUE"]
另请参阅 自定义标题转换器
选项 skip_blanks
¶ ↑
指定一个布尔值,该值确定是否忽略输入中的空白行;包含列分隔符的行不被视为空白行。
默认值
CSV::DEFAULT_OPTIONS.fetch(:skip_blanks) # => false
另请参阅选项 skiplines。
对于本节中的示例
str = <<-EOT foo,0 bar,1 baz,2 , EOT
使用默认值 false
ary = CSV.parse(str) ary # => [["foo", "0"], [], ["bar", "1"], ["baz", "2"], [], [nil, nil]]
使用 true
ary = CSV.parse(str, skip_blanks: true) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"], [nil, nil]]
使用真值
ary = CSV.parse(str, skip_blanks: :foo) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"], [nil, nil]]
选项 skip_lines
¶ ↑
指定一个对象,用于标识输入中要忽略的注释行
-
如果是正则表达式,则忽略与其匹配的行。
-
如果是字符串,则将其转换为正则表达式,并忽略与其匹配的行。
-
如果为
nil
,则不认为任何行为注释。
默认值
CSV::DEFAULT_OPTIONS.fetch(:skip_lines) # => nil
对于本节中的示例
str = <<-EOT # Comment foo,0 bar,1 baz,2 # Another comment EOT str # => "# Comment\nfoo,0\nbar,1\nbaz,2\n# Another comment\n"
使用默认值 nil
ary = CSV.parse(str) ary # => [["# Comment"], ["foo", "0"], ["bar", "1"], ["baz", "2"], ["# Another comment"]]
使用正则表达式
ary = CSV.parse(str, skip_lines: /^#/) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用字符串
ary = CSV.parse(str, skip_lines: '#') ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
如果给定的对象不是正则表达式、字符串或 nil
,则会引发异常
# Raises ArgumentError (:skip_lines has to respond to #match: 0) CSV.parse(str, skip_lines: 0)
选项 strip
¶ ↑
指定一个布尔值,该值确定是否从每个输入字段中去除空格。
默认值
CSV::DEFAULT_OPTIONS.fetch(:strip) # => false
使用默认值 false
ary = CSV.parse_line(' a , b ') ary # => [" a ", " b "]
使用值 true
ary = CSV.parse_line(' a , b ', strip: true) ary # => ["a", "b"]
选项 liberal_parsing
¶ ↑
指定一个布尔值或哈希值,该值确定 CSV
是否会尝试解析不符合 RFC 4180 的输入,例如未加引号的字段中的双引号。
默认值
CSV::DEFAULT_OPTIONS.fetch(:liberal_parsing) # => false
对于接下来的两个示例
str = 'is,this "three, or four",fields'
没有 liberal_parsing
# Raises CSV::MalformedCSVError (Illegal quoting in str 1.) CSV.parse_line(str)
使用 liberal_parsing
ary = CSV.parse_line(str, liberal_parsing: true) ary # => ["is", "this \"three", " or four\"", "fields"]
使用 backslash_quote
子选项来解析使用反斜杠转义双引号字符的值。这会导致解析器将 \"
视为 ""
。
对于接下来的两个示例
str = 'Show,"Harry \"Handcuff\" Houdini, the one and only","Tampa Theater"'
使用 liberal_parsing
,但不使用 backslash_quote
子选项
# Incorrect interpretation of backslash; incorrectly interprets the quoted comma as a field separator. ary = CSV.parse_line(str, liberal_parsing: true) ary # => ["Show", "\"Harry \\\"Handcuff\\\" Houdini", " the one and only\"", "Tampa Theater"] puts ary[1] # => "Harry \"Handcuff\" Houdini
使用 liberal_parsing
及其 backslash_quote
子选项
ary = CSV.parse_line(str, liberal_parsing: { backslash_quote: true }) ary # => ["Show", "Harry \"Handcuff\" Houdini, the one and only", "Tampa Theater"] puts ary[1] # => Harry "Handcuff" Houdini, the one and only
选项 nil_value
¶ ↑
指定要替换每个空(无文本)字段的对象。
默认值
CSV::DEFAULT_OPTIONS.fetch(:nil_value) # => nil
使用默认值 nil
CSV.parse_line('a,,b,,c') # => ["a", nil, "b", nil, "c"]
使用不同的对象
CSV.parse_line('a,,b,,c', nil_value: 0) # => ["a", 0, "b", 0, "c"]
选项 empty_value
¶ ↑
指定要替换每个空字符串字段的对象。
默认值
CSV::DEFAULT_OPTIONS.fetch(:empty_value) # => "" (empty string)
使用默认值 ""
CSV.parse_line('a,"",b,"",c') # => ["a", "", "b", "", "c"]
使用不同的对象
CSV.parse_line('a,"",b,"",c', empty_value: 'x') # => ["a", "x", "b", "x", "c"]
生成选项¶ ↑
下面详细描述的生成选项包括
-
row_sep
:指定行分隔符;用于分隔行。 -
col_sep
:指定列分隔符;用于分隔字段。 -
quote_char
:指定引用字符;用于引用字段。 -
write_headers
:指定是否写入标题。 -
force_quotes
:指定是否引用每个输出字段。 -
quote_empty
:指定是否引用每个空输出字段。 -
write_converters
:指定在写入时要使用的字段转换器。 -
write_nil_value
:指定要替换每个nil
值字段的对象。 -
write_empty_value
:指定要替换每个空字段的对象。
选项 row_sep
¶ ↑
指定行分隔符,一个 String 或符号 :auto
(请参见下文),用于解析和生成。
默认值
CSV::DEFAULT_OPTIONS.fetch(:row_sep) # => :auto
当 row_sep
是字符串时,该字符串将成为行分隔符。String
将在使用前转码为数据的编码。
使用 "\n"
row_sep = "\n" str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 |
(管道)
row_sep = '|' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0|bar,1|baz,2|" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 --
(两个连字符)
row_sep = '--' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0--bar,1--baz,2--" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 ''
(空字符串)
row_sep = '' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0bar,1baz,2" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0bar", "1baz", "2"]]
当 row_sep
是符号 :auto
(默认值)时,生成使用 "\n"
作为行分隔符
str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n"
另一方面,解析会调用行分隔符的自动发现。
自动发现会在数据中向前读取,以查找下一个 \r\n
、\n
或 \r
序列。即使该序列出现在引用的字段中,也会选择该序列,前提是您在那里具有相同的行尾。
示例
str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
如果满足以下任一条件,则使用默认的 $INPUT_RECORD_SEPARATOR
($/
):
-
未找到这些序列中的任何一个。
-
数据为
ARGF
、STDIN
、STDOUT
或STDERR
。 -
流仅可用于输出。
显然,发现需要一些时间。如果速度很重要,请手动设置。另请注意,如果将使用此功能,则应在 Windows 上以二进制模式打开 IO 对象,因为行尾转换可能会导致将文档位置重置到读取前的位置出现问题。
选项 col_sep
¶ ↑
指定用于解析和生成的字符串列分隔符。在使用前,该字符串将被转码为数据所使用的编码。
默认值
CSV::DEFAULT_OPTIONS.fetch(:col_sep) # => "," (comma)
使用默认值(逗号)
str = CSV.generate do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 :
(冒号)
col_sep = ':' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo:0\nbar:1\nbaz:2\n" ary = CSV.parse(str, col_sep: col_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 ::
(两个冒号)
col_sep = '::' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo::0\nbar::1\nbaz::2\n" ary = CSV.parse(str, col_sep: col_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
使用 ''
(空字符串)
col_sep = '' str = CSV.generate(col_sep: col_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo0\nbar1\nbaz2\n"
如果使用空字符串进行解析,则会引发异常
col_sep = '' # Raises ArgumentError (:col_sep must be 1 or more characters: "") CSV.parse("foo0\nbar1\nbaz2\n", col_sep: col_sep)
选项 quote_char
¶ ↑
指定用于在解析和生成中引用字段的字符(长度为 1 的字符串)。在使用前,此 String
将被转码为数据所使用的编码。
默认值
CSV::DEFAULT_OPTIONS.fetch(:quote_char) # => "\"" (double quote)
这对于错误地使用 '
(单引号)而不是正确的 "
(双引号)来引用字段的应用程序很有用。
使用默认值(双引号)
str = CSV.generate do |csv| csv << ['foo', 0] csv << ["'bar'", 1] csv << ['"baz"', 2] end str # => "foo,0\n'bar',1\n\"\"\"baz\"\"\",2\n" ary = CSV.parse(str) ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
使用 '
(单引号)
quote_char = "'" str = CSV.generate(quote_char: quote_char) do |csv| csv << ['foo', 0] csv << ["'bar'", 1] csv << ['"baz"', 2] end str # => "foo,0\n'''bar''',1\n\"baz\",2\n" ary = CSV.parse(str, quote_char: quote_char) ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
如果字符串长度大于 1,则会引发异常
# Raises ArgumentError (:quote_char has to be nil or a single character String) CSV.new('', quote_char: 'xx')
如果该值不是字符串,则会引发异常
# Raises ArgumentError (:quote_char has to be nil or a single character String) CSV.new('', quote_char: :foo)
选项 write_headers
¶ ↑
指定一个布尔值,该值确定输出中是否包含标题行;如果不存在标题,则忽略此选项。
默认值
CSV::DEFAULT_OPTIONS.fetch(:write_headers) # => nil
没有 write_headers
file_path = 't.csv' CSV.open(file_path,'w', :headers => ['Name','Value'] ) do |csv| csv << ['foo', '0'] end CSV.open(file_path) do |csv| csv.shift end # => ["foo", "0"]
使用 write_headers
CSV.open(file_path,'w', :write_headers => true, :headers => ['Name','Value'] ) do |csv| csv << ['foo', '0'] end CSV.open(file_path) do |csv| csv.shift end # => ["Name", "Value"]
选项 force_quotes
¶ ↑
指定一个布尔值,该值确定是否用双引号引住每个输出字段。
默认值
CSV::DEFAULT_OPTIONS.fetch(:force_quotes) # => false
对于本节中的示例
ary = ['foo', 0, nil]
使用默认值 false
str = CSV.generate_line(ary) str # => "foo,0,\n"
使用 true
str = CSV.generate_line(ary, force_quotes: true) str # => "\"foo\",\"0\",\"\"\n"
选项 quote_empty
¶ ↑
指定一个布尔值,该值确定是否用双引号引住空值。
默认值
CSV::DEFAULT_OPTIONS.fetch(:quote_empty) # => true
使用默认值 true
CSV.generate_line(['"', ""]) # => "\"\"\"\",\"\"\n"
使用 false
CSV.generate_line(['"', ""], quote_empty: false) # => "\"\"\"\",\n"
选项 write_converters
¶ ↑
指定在生成字段时要使用的转换器。请参阅 写入转换器
默认值
CSV::DEFAULT_OPTIONS.fetch(:write_converters) # => nil
没有写入转换器
str = CSV.generate_line(["\na\n", "\tb\t", " c "]) str # => "\"\na\n\",\tb\t, c \n"
有一个写入转换器
strip_converter = proc {|field| field.strip } str = CSV.generate_line(["\na\n", "\tb\t", " c "], write_converters: strip_converter) str # => "a,b,c\n"
有两个写入转换器(按顺序调用)
upcase_converter = proc {|field| field.upcase } downcase_converter = proc {|field| field.downcase } write_converters = [upcase_converter, downcase_converter] str = CSV.generate_line(['a', 'b', 'c'], write_converters: write_converters) str # => "a,b,c\n"
另请参阅 写入转换器
选项 write_nil_value
¶ ↑
指定要替换每个 nil
值字段的对象。
默认值
CSV::DEFAULT_OPTIONS.fetch(:write_nil_value) # => nil
不使用此选项
str = CSV.generate_line(['a', nil, 'c', nil]) str # => "a,,c,\n"
使用此选项
str = CSV.generate_line(['a', nil, 'c', nil], write_nil_value: "x") str # => "a,x,c,x\n"
选项 write_empty_value
¶ ↑
指定要替换每个空字符串字段的对象。
默认值
CSV::DEFAULT_OPTIONS.fetch(:write_empty_value) # => ""
不使用此选项
str = CSV.generate_line(['a', '', 'c', '']) str # => "a,\"\",c,\"\"\n"
使用此选项
str = CSV.generate_line(['a', '', 'c', ''], write_empty_value: "x") str # => "a,x,c,x\n"
带标题的 CSV¶ ↑
CSV
允许指定 CSV
文件的列名,无论它们是在数据中还是单独提供的。如果指定了标题,则读取方法会返回一个 CSV::Table
的实例,其中包含 CSV::Row
。
# Headers are part of data data = CSV.parse(<<~ROWS, headers: true) Name,Department,Salary Bob,Engineering,1000 Jane,Sales,2000 John,Management,5000 ROWS data.class #=> CSV::Table data.first #=> #<CSV::Row "Name":"Bob" "Department":"Engineering" "Salary":"1000"> data.first.to_h #=> {"Name"=>"Bob", "Department"=>"Engineering", "Salary"=>"1000"} # Headers provided by developer data = CSV.parse('Bob,Engineering,1000', headers: %i[name department salary]) data.first #=> #<CSV::Row name:"Bob" department:"Engineering" salary:"1000">
转换器¶ ↑
默认情况下,由 CSV 解析的每个值(字段或标题)都会被转换为字符串。您可以使用字段转换器或标题转换器来拦截和修改解析的值
此外,默认情况下,在生成过程中写入的每个值都按原样写入。您可以使用写入转换器在写入前修改值。
-
请参阅 写入转换器。
指定转换器¶ ↑
您可以在各种 CSV 方法的 options
参数中指定用于解析或生成的转换器
-
用于转换解析的字段值的选项
converters
。 -
用于转换解析的标题值的选项
header_converters
。 -
用于转换要写入(生成)的值的选项
write_converters
。
指定转换器有三种形式
-
转换器 Proc:用于转换的可执行代码。
-
转换器名称:存储的转换器的名称。
-
转换器列表:转换器 Proc、转换器名称和转换器列表的数组。
转换器 Proc¶ ↑
此转换器 Proc strip_converter
接受值 field
并返回 field.strip
strip_converter = proc {|field| field.strip }
在此调用 CSV.parse
时,关键字参数 converters: string_converter
指定
-
对于每个解析的字段,都将调用 Proc
string_converter
。 -
转换器的返回值将替换
field
值。
示例
string = " foo , 0 \n bar , 1 \n baz , 2 \n" array = CSV.parse(string, converters: strip_converter) array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
转换器 Proc 可以接收第二个参数 field_info
,其中包含有关字段的详细信息。此修改后的 strip_converter
显示其参数
strip_converter = proc do |field, field_info| p [field, field_info] field.strip end string = " foo , 0 \n bar , 1 \n baz , 2 \n" array = CSV.parse(string, converters: strip_converter) array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
输出
[" foo ", #<struct CSV::FieldInfo index=0, line=1, header=nil>] [" 0 ", #<struct CSV::FieldInfo index=1, line=1, header=nil>] [" bar ", #<struct CSV::FieldInfo index=0, line=2, header=nil>] [" 1 ", #<struct CSV::FieldInfo index=1, line=2, header=nil>] [" baz ", #<struct CSV::FieldInfo index=0, line=3, header=nil>] [" 2 ", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
每个 CSV::FieldInfo
对象显示:
-
从 0 开始的字段索引。
-
从 1 开始的行索引。
-
字段的标题(如果有)。
已存储的转换器¶ ↑
可以为转换器指定一个名称,并将其存储在一个结构中,以便解析方法可以通过名称找到它。
字段转换器的存储结构是哈希表 CSV::Converters
。它有几个内置的转换器 Proc:
-
:integer
:将每个嵌入在字符串中的整数转换为真正的 Integer。 -
:float
:将每个嵌入在字符串中的浮点数转换为真正的 Float。 -
:date
:将每个嵌入在字符串中的日期转换为真正的 Date。 -
:date_time
:将每个嵌入在字符串中的日期时间转换为真正的 DateTime。 -
:time
:将每个嵌入在字符串中的时间转换为真正的 Time。
此示例创建一个转换器 Proc,然后将其存储:
strip_converter = proc {|field| field.strip } CSV::Converters[:strip] = strip_converter
然后,解析方法调用可以通过其名称 :strip
引用该转换器。
string = " foo , 0 \n bar , 1 \n baz , 2 \n" array = CSV.parse(string, converters: :strip) array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
标题转换器的存储结构是哈希表 CSV::HeaderConverters
,其工作方式相同。它也有内置的转换器 Proc:
-
:downcase
:将每个标题转换为小写。 -
:symbol
:将每个标题转换为 Symbol。
写入标题没有这样的存储结构。
为了使解析方法能够在非主 Ractor 中访问存储的转换器,必须首先使存储结构可共享。因此,必须在创建使用存储在这些结构中的转换器的 Ractor 之前调用 Ractor.make_shareable(CSV::Converters)
和 Ractor.make_shareable(CSV::HeaderConverters)
。(由于使存储结构可共享涉及到冻结它们,因此必须首先添加要使用的任何自定义转换器。)
转换器列表¶ ↑
转换器列表是一个数组,其中可能包含以下任意组合:
-
转换器 Proc。
-
已存储转换器的名称。
-
嵌套的转换器列表。
示例
numeric_converters = [:integer, :float] date_converters = [:date, :date_time] [numeric_converters, strip_converter] [strip_converter, date_converters, :float]
像转换器 Proc 一样,可以命名转换器列表,并将其存储在 CSV::Converters 或 CSV::HeaderConverters
中。
CSV::Converters[:custom] = [strip_converter, date_converters, :float] CSV::HeaderConverters[:custom] = [:downcase, :symbol]
有两个内置的转换器列表:
CSV::Converters[:numeric] # => [:integer, :float] CSV::Converters[:all] # => [:date_time, :numeric]
字段转换器¶ ↑
如果不进行转换,则所有行中的所有解析字段都将变为字符串。
string = "foo,0\nbar,1\nbaz,2\n" ary = CSV.parse(string) ary # => # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
指定字段转换器后,每个解析后的字段都会传递给转换器;其返回值将成为该字段的存储值。例如,转换器可以将嵌入在字符串中的整数转换为真正的 Integer。(实际上,这就是内置字段转换器 :integer
的作用。)
有三种使用字段转换器的方法。
-
使用带有解析方法的选项 converters。
ary = CSV.parse(string, converters: :integer) ary # => [0, 1, 2] # => [["foo", 0], ["bar", 1], ["baz", 2]]
-
使用带有新 CSV 实例的选项 converters。
csv = CSV.new(string, converters: :integer) # Field converters in effect: csv.converters # => [:integer] csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
-
使用方法
convert
将字段转换器添加到 CSV 实例。csv = CSV.new(string) # Add a converter. csv.convert(:integer) csv.converters # => [:integer] csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
安装字段转换器不会影响已读取的行。
csv = CSV.new(string) csv.shift # => ["foo", "0"] # Add a converter. csv.convert(:integer) csv.converters # => [:integer] csv.read # => [["bar", 1], ["baz", 2]]
还有其他内置的转换器,并且还支持自定义转换器。
内置字段转换器¶ ↑
内置的字段转换器位于哈希表 CSV::Converters
中。
-
每个键都是一个字段转换器名称。
-
每个值都是以下之一:
-
Proc 字段转换器。
-
字段转换器名称的数组。
-
显示
CSV::Converters.each_pair do |name, value| if value.kind_of?(Proc) p [name, value.class] else p [name, value] end end
输出
[:integer, Proc] [:float, Proc] [:numeric, [:integer, :float]] [:date, Proc] [:date_time, Proc] [:time, Proc] [:all, [:date_time, :numeric]]
这些转换器中的每一个都会在尝试转换之前将值转码为 UTF-8。如果值无法转码为 UTF-8,则转换将失败,并且该值将保持未转换状态。
转换器 :integer
转换 Integer() 接受的每个字段。
data = '0,1,2,x' # Without the converter csv = CSV.parse_line(data) csv # => ["0", "1", "2", "x"] # With the converter csv = CSV.parse_line(data, converters: :integer) csv # => [0, 1, 2, "x"]
转换器 :float
转换 Float() 接受的每个字段。
data = '1.0,3.14159,x' # Without the converter csv = CSV.parse_line(data) csv # => ["1.0", "3.14159", "x"] # With the converter csv = CSV.parse_line(data, converters: :float) csv # => [1.0, 3.14159, "x"]
转换器 :numeric
同时使用 :integer
和 :float
进行转换。
转换器 :date
转换 Date::parse 接受的每个字段。
data = '2001-02-03,x' # Without the converter csv = CSV.parse_line(data) csv # => ["2001-02-03", "x"] # With the converter csv = CSV.parse_line(data, converters: :date) csv # => [#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>, "x"]
转换器 :date_time
转换 DateTime::parse 接受的每个字段。
data = '2020-05-07T14:59:00-05:00,x' # Without the converter csv = CSV.parse_line(data) csv # => ["2020-05-07T14:59:00-05:00", "x"] # With the converter csv = CSV.parse_line(data, converters: :date_time) csv # => [#<DateTime: 2020-05-07T14:59:00-05:00 ((2458977j,71940s,0n),-18000s,2299161j)>, "x"]
转换器 time
转换 Time::parse 接受的每个字段。
data = '2020-05-07T14:59:00-05:00,x' # Without the converter csv = CSV.parse_line(data) csv # => ["2020-05-07T14:59:00-05:00", "x"] # With the converter csv = CSV.parse_line(data, converters: :time) csv # => [2020-05-07 14:59:00 -0500, "x"]
转换器 :numeric
同时使用 :date_time
和 :numeric
进行转换。
如上所示,方法 convert
将转换器添加到 CSV 实例,而方法 converters
返回有效转换器的数组。
csv = CSV.new('0,1,2') csv.converters # => [] csv.convert(:integer) csv.converters # => [:integer] csv.convert(:date) csv.converters # => [:integer, :date]
自定义字段转换器¶ ↑
您可以定义自定义字段转换器。
strip_converter = proc {|field| field.strip } string = " foo , 0 \n bar , 1 \n baz , 2 \n" array = CSV.parse(string, converters: strip_converter) array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
您可以在 Converters 哈希表中注册转换器,这使您可以通过名称引用它。
CSV::Converters[:strip] = strip_converter string = " foo , 0 \n bar , 1 \n baz , 2 \n" array = CSV.parse(string, converters: :strip) array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
标题转换器¶ ↑
标题转换器仅对标题进行操作(而不是对其他行进行操作)。
有三种使用标题转换器的方法;这些示例使用内置标题转换器 :downcase
,它将每个解析后的标题转换为小写。
-
带有单例解析方法的选项
header_converters
。string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2" tbl = CSV.parse(string, headers: true, header_converters: :downcase) tbl.class # => CSV::Table tbl.headers # => ["name", "count"]
-
带有新 CSV 实例的选项
header_converters
。csv = CSV.new(string, header_converters: :downcase) # Header converters in effect: csv.header_converters # => [:downcase] tbl = CSV.parse(string, headers: true) tbl.headers # => ["Name", "Count"]
-
方法
header_convert
将标题转换器添加到 CSV 实例。csv = CSV.new(string) # Add a header converter. csv.header_convert(:downcase) csv.header_converters # => [:downcase] tbl = CSV.parse(string, headers: true) tbl.headers # => ["Name", "Count"]
内置标题转换器¶ ↑
内置的标题转换器位于哈希表 CSV::HeaderConverters
中。那里的键是转换器的名称。
CSV::HeaderConverters.keys # => [:downcase, :symbol]
转换器 :downcase
通过将每个标题转换为小写来转换它。
string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2" tbl = CSV.parse(string, headers: true, header_converters: :downcase) tbl.class # => CSV::Table tbl.headers # => ["name", "count"]
转换器 :symbol
通过将每个标题转换为 Symbol 来转换它。
string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2" tbl = CSV.parse(string, headers: true, header_converters: :symbol) tbl.headers # => [:name, :count]
详细信息
-
删除开头和结尾的空格。
-
将标题转换为小写。
-
将嵌入的空格替换为下划线。
-
删除非单词字符。
-
将字符串转换为 Symbol。
自定义标题转换器¶ ↑
您可以定义自定义标题转换器。
upcase_converter = proc {|header| header.upcase } string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(string, headers: true, header_converters: upcase_converter) table # => #<CSV::Table mode:col_or_row row_count:4> table.headers # => ["NAME", "VALUE"]
您可以在 HeaderConverters 哈希表中注册转换器,这使您可以通过名称引用它。
CSV::HeaderConverters[:upcase] = upcase_converter table = CSV.parse(string, headers: true, header_converters: :upcase) table # => #<CSV::Table mode:col_or_row row_count:4> table.headers # => ["NAME", "VALUE"]
写入转换器¶ ↑
指定用于生成 CSV 的写入转换器时,每个要写入的字段都会传递给转换器;其返回值将成为该字段的新值。例如,转换器可以从字段中删除空格。
不使用写入转换器(所有字段均未修改)。
output_string = CSV.generate do |csv| csv << [' foo ', 0] csv << [' bar ', 1] csv << [' baz ', 2] end output_string # => " foo ,0\n bar ,1\n baz ,2\n"
使用带有两个自定义写入转换器的选项 write_converters
。
strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field } upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field } write_converters = [strip_converter, upcase_converter] output_string = CSV.generate(write_converters: write_converters) do |csv| csv << [' foo ', 0] csv << [' bar ', 1] csv << [' baz ', 2] end output_string # => "FOO,0\nBAR,1\nBAZ,2\n"
字符编码(M17n 或多语言化)¶ ↑
这个新的 CSV
解析器具有 M17n 功能。解析器在正在读取或写入的 IO 或 String
对象的编码中工作。您的数据永远不会被转码(除非您要求 Ruby 为您转码),并且将按照其所在的编码进行字面解析。因此,CSV
将以数据的编码返回字符串数组或行。这是通过将解析器本身转码为您的编码来实现的。
当然,必须进行一些转码才能实现此多编码支持。例如,必须将 :col_sep
、:row_sep
和 :quote_char
转码以匹配您的数据。希望这使整个过程感觉透明,因为 CSV 的默认值应该只是神奇地为您的数据工作。但是,您可以在目标编码中手动设置这些值以避免转换。
同样重要的是要注意,尽管 CSV 的所有核心解析器现在都与编码无关,但某些功能并非如此。例如,内置的转换器将尝试在进行转换之前将数据转码为 UTF-8。同样,您可以提供了解您的编码的自定义转换器,以避免这种转换。对于我来说,要支持 Ruby 所有编码中的本机转换太难了。
无论如何,这方面的实际操作很简单:确保传递到 CSV
中的 IO 和 String
对象设置了正确的编码,一切都应该可以正常工作。CSV
允许您打开 IO 对象的方法(CSV::foreach()
、CSV::open()
、CSV::read()
和 CSV::readlines()
)允许您指定编码。
当使用与 ASCII 不兼容的编码将 CSV
生成到 String
中时,会出现一个小例外。没有 CSV
可以用来准备自己的现有数据,因此您可能需要在大多数情况下手动指定所需的编码。但是,当使用 CSV::generate_line()
或 Array#to_csv()
时,它会尝试使用输出行中的字段进行猜测。
当我发现任何其他编码问题时,我都会在方法的文档中指出。
我已经尽力使用 Ruby 附带的所有非“虚拟”编码对其进行了测试。但是,这是勇敢的新代码,可能存在一些错误。如果您发现任何问题,请随时 报告。
常量
- ConverterEncoding
所有转换器使用的编码。
- 转换器
一个哈希表,其中包含内置字段转换器的名称和 Proc。请参阅内置字段转换器。
此哈希表有意保持未冻结状态,并且可以使用自定义字段转换器进行扩展。请参阅自定义字段转换器。
- DEFAULT_OPTIONS
方法选项的默认值。
- DateMatcher
用于查找和转换一些常见日期格式的正则表达式。
- DateTimeMatcher
用于查找和转换一些常见(日期)时间格式的正则表达式。
- FieldInfo
FieldInfo
结构包含有关字段在其读取的数据源中的位置的详细信息。CSV
会将此结构传递给一些基于字段结构做出决定的代码块。有关示例,请参阅CSV.convert_fields()
。index
-
该字段在其行中的从零开始的索引。
line
-
此行所在的数据源的行。
header
-
列的标题(如果可用)。
quoted?
-
真或假,指示原始值是否被引用。
- HeaderConverters
一个哈希表,其中包含内置标题转换器的名称和 Proc。请参阅内置标题转换器。
此哈希表有意保持未冻结状态,并且可以使用自定义字段转换器进行扩展。请参阅自定义标题转换器。
- ON_WINDOWS
- VERSION
已安装库的版本。
属性
:call-seq
csv.encoding -> encoding
返回用于解析和生成的编码;请参阅字符编码(M17n 或多语言化)
CSV.new('').encoding # => #<Encoding:UTF-8>
公共类方法
-
从源(字符串、IO 流或 ARGF)解析 CSV。
-
使用每个解析的行调用给定的代码块。
-
如果没有标题,则每行都是一个数组。
-
如果有标题,则每行都是一个
CSV::Row
。
-
-
将 CSV 生成到输出(字符串、IO 流或 STDOUT)。
-
返回已解析的源。
-
如果没有标题,则返回一个数组的数组。
-
使用标题行时,会返回一个
CSV::Table
。
-
当提供了 in_string_or_io
但没有提供 out_string_or_io
时,会从给定的 in_string_or_io
解析数据,并将结果输出到 STDOUT。
不带标题行的字符串输入
in_string = "foo,0\nbar,1\nbaz,2" CSV.filter(in_string) do |row| row[0].upcase! row[1] = - row[1].to_i end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
输出(到 STDOUT)
FOO,0 BAR,-1 BAZ,-2
带有标题行的字符串输入
in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2" CSV.filter(in_string, headers: true) do |row| row[0].upcase! row[1] = - row[1].to_i end # => #<CSV::Table mode:col_or_row row_count:4>
输出(到 STDOUT)
Name,Value FOO,0 BAR,-1 BAZ,-2
不带标题行的 IO 流输入
File.write('t.csv', "foo,0\nbar,1\nbaz,2") File.open('t.csv') do |in_io| CSV.filter(in_io) do |row| row[0].upcase! row[1] = - row[1].to_i end end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
输出(到 STDOUT)
FOO,0 BAR,-1 BAZ,-2
带有标题行的 IO 流输入
File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2") File.open('t.csv') do |in_io| CSV.filter(in_io, headers: true) do |row| row[0].upcase! row[1] = - row[1].to_i end end # => #<CSV::Table mode:col_or_row row_count:4>
输出(到 STDOUT)
Name,Value FOO,0 BAR,-1 BAZ,-2
当同时提供了 in_string_or_io
和 out_string_or_io
时,会从 in_string_or_io
解析数据,并将结果输出到 out_string_or_io
。
不带标题行的字符串输出
in_string = "foo,0\nbar,1\nbaz,2" out_string = '' CSV.filter(in_string, out_string) do |row| row[0].upcase! row[1] = - row[1].to_i end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]] out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"
带有标题行的字符串输出
in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2" out_string = '' CSV.filter(in_string, out_string, headers: true) do |row| row[0].upcase! row[1] = - row[1].to_i end # => #<CSV::Table mode:col_or_row row_count:4> out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
不带标题行的 IO 流输出
in_string = "foo,0\nbar,1\nbaz,2" File.open('t.csv', 'w') do |out_io| CSV.filter(in_string, out_io) do |row| row[0].upcase! row[1] = - row[1].to_i end end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]] File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"
带有标题行的 IO 流输出
in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2" File.open('t.csv', 'w') do |out_io| CSV.filter(in_string, out_io, headers: true) do |row| row[0].upcase! row[1] = - row[1].to_i end end # => #<CSV::Table mode:col_or_row row_count:4> File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
当既没有提供 in_string_or_io
也没有提供 out_string_or_io
时,会从 ARGF 解析数据,并将结果输出到 STDOUT。
不带标题行
# Put Ruby code into a file. ruby = <<-EOT require 'csv' CSV.filter do |row| row[0].upcase! row[1] = - row[1].to_i end EOT File.write('t.rb', ruby) # Put some CSV into a file. File.write('t.csv', "foo,0\nbar,1\nbaz,2") # Run the Ruby code with CSV filename as argument. system(Gem.ruby, "t.rb", "t.csv")
输出(到 STDOUT)
FOO,0 BAR,-1 BAZ,-2
带标题行
# Put Ruby code into a file. ruby = <<-EOT require 'csv' CSV.filter(headers: true) do |row| row[0].upcase! row[1] = - row[1].to_i end EOT File.write('t.rb', ruby) # Put some CSV into a file. File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2") # Run the Ruby code with CSV filename as argument. system(Gem.ruby, "t.rb", "t.csv")
输出(到 STDOUT)
Name,Value FOO,0 BAR,-1 BAZ,-2
参数
-
参数
in_string_or_io
必须是 String 或 IO 流。 -
参数
out_string_or_io
必须是 String 或 IO 流。 -
参数
**options
必须是关键字选项。
但是,有三个选项可以同时用于解析和生成:col_sep
、quote_char
和 row_sep
。
因此,对于方法 filter
(且仅对于方法 filter
),有一些特殊选项允许分别指定这些解析和生成选项
-
选项
input_col_sep
和output_col_sep
(以及它们的别名in_col_sep
和out_col_sep
)指定了解析和生成时的列分隔符。 -
选项
input_quote_char
和output_quote_char
(以及它们的别名in_quote_char
和out_quote_char
)指定了解析和生成时的引号字符。 -
选项
input_row_sep
和output_row_sep
(以及它们的别名in_row_sep
和out_row_sep
)指定了解析和生成时的行分隔符。
选项示例(用于列分隔符)
CSV.filter # Default for both parsing and generating. CSV.filter(in_col_sep: ';') # ';' for parsing, default for generating. CSV.filter(out_col_sep: '|') # Default for parsing, '|' for generating. CSV.filter(in_col_sep: ';', out_col_sep: '|') # ';' for parsing, '|' for generating.
请注意,对于特殊选项(例如,input_col_sep
)及其对应的“常规”选项(例如,col_sep
),两者是互斥覆盖的。
另一个示例(可能令人惊讶)
CSV.filter(in_col_sep: ';', col_sep: '|') # '|' for both parsing(!) and generating.
# File csv-3.3.2/lib/csv.rb, line 1259 def filter(input=nil, output=nil, **options) # parse options for input, output, or both in_options, out_options = Hash.new, {row_sep: InputRecordSeparator.value} options.each do |key, value| case key 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 # build input and output wrappers input = new(input || ARGF, **in_options) output = new(output || $stdout, **out_options) # process headers need_manual_header_output = (in_options[:headers] and out_options[:headers] == true and out_options[:write_headers]) if need_manual_header_output first_row = input.shift if first_row if first_row.is_a?(Row) headers = first_row.headers yield headers output << headers end yield first_row output << first_row end end # read, yield, write input.each do |row| yield row output << row end end
使用从源 path_or_io
读取的每一行调用代码块。
不带标题行的路径输入
string = "foo,0\nbar,1\nbaz,2\n" in_path = 't.csv' File.write(in_path, string) CSV.foreach(in_path) {|row| p row }
输出
["foo", "0"] ["bar", "1"] ["baz", "2"]
带有标题行的路径输入
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" in_path = 't.csv' File.write(in_path, string) CSV.foreach(in_path, headers: true) {|row| p row }
输出
<CSV::Row "Name":"foo" "Value":"0"> <CSV::Row "Name":"bar" "Value":"1"> <CSV::Row "Name":"baz" "Value":"2">
不带标题行的 IO 流输入
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) File.open('t.csv') do |in_io| CSV.foreach(in_io) {|row| p row } end
输出
["foo", "0"] ["bar", "1"] ["baz", "2"]
带有标题行的 IO 流输入
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) File.open('t.csv') do |in_io| CSV.foreach(in_io, headers: true) {|row| p row } end
输出
<CSV::Row "Name":"foo" "Value":"0"> <CSV::Row "Name":"bar" "Value":"1"> <CSV::Row "Name":"baz" "Value":"2">
如果没有给出代码块,则返回一个 Enumerator
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
参数
-
参数
path_or_io
必须是文件路径或 IO 流。 -
参数
mode
(如果给出)必须是文件模式。请参阅 访问模式。 -
参数
**options
必须是关键字选项。请参阅 解析选项。 -
此方法可以选择接受一个额外的
:encoding
选项,您可以使用它来指定从path
或io
读取的数据的编码。除非您的数据采用Encoding::default_external
给出的编码,否则您必须提供此选项。解析将使用此选项来确定如何解析数据。您可以提供第二个编码,以便在读取数据时对其进行转码。例如,encoding: 'UTF-32BE:UTF-8'
将从文件中读取
UTF-32BE
数据,但在解析之前将其转码为UTF-8
。
# File csv-3.3.2/lib/csv.rb, line 1389 def foreach(path, mode="r", **options, &block) return to_enum(__method__, path, mode, **options) unless block_given? open(path, mode, **options) do |csv| csv.each(&block) end end
-
参数
csv_string
(如果给出)必须是 String 对象;默认为新的空 String。 -
参数
options
(如果给出)应该是生成选项。请参阅 生成选项。
通过 CSV.new(csv_string, **options)
创建一个新的 CSV 对象;使用 CSV 对象调用代码块,该代码块可以修改 CSV 对象;返回从 CSV 对象生成的 String。
请注意,传递的 String **会被**此方法修改。如果必须保留 String,请传递 csv_string
.dup。
此方法有一个额外的选项::encoding
,它为输出设置基本编码(如果未指定 str
)。如果计划输出非 ASCII 兼容的数据,CSV
需要此提示。
添加行
input_string = "foo,0\nbar,1\nbaz,2\n" output_string = CSV.generate(input_string) do |csv| csv << ['bat', 3] csv << ['bam', 4] end output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n" input_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n" output_string.equal?(input_string) # => true # Same string, modified
将行添加到新字符串中,保留旧字符串
input_string = "foo,0\nbar,1\nbaz,2\n" output_string = CSV.generate(input_string.dup) do |csv| csv << ['bat', 3] csv << ['bam', 4] end output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n" input_string # => "foo,0\nbar,1\nbaz,2\n" output_string.equal?(input_string) # => false # Different strings
从无到有创建行
output_string = CSV.generate do |csv| csv << ['foo', 0] csv << ['bar', 1] csv << ['baz', 2] end output_string # => "foo,0\nbar,1\nbaz,2\n"
如果 csv_string
不是 String 对象,则引发异常
# Raises TypeError (no implicit conversion of Integer into String) CSV.generate(0)
# File csv-3.3.2/lib/csv.rb, line 1455 def generate(str=nil, **options) encoding = options[:encoding] # add a default empty String, if none was given if str str = StringIO.new(str) str.seek(0, IO::SEEK_END) str.set_encoding(encoding) if encoding else str = +"" str.force_encoding(encoding) if encoding end csv = new(str, **options) # wrap yield csv # yield for appending csv.string # return final String end
返回通过使用指定的 options
从 ary
生成 CSV 而创建的 String。
参数 ary
必须是 Array。
特殊选项
-
选项
:row_sep
在 Ruby 3.0 或更高版本上默认为"\n">
,否则为$INPUT_RECORD_SEPARATOR
($/
)。$INPUT_RECORD_SEPARATOR # => "\n"
-
此方法接受一个额外的选项
:encoding
,它为输出设置基本编码。如果可能,此方法将尝试从row
中的第一个非nil
字段猜测您的编码,但您可能需要使用此参数作为备用方案。
对于其他 options
,请参阅 生成选项。
返回从 Array 生成的 String
CSV.generate_line(['foo', '0']) # => "foo,0\n"
如果 ary
不是 Array,则引发异常
# Raises NoMethodError (undefined method `find' for :foo:Symbol) CSV.generate_line(:foo)
# File csv-3.3.2/lib/csv.rb, line 1503 def generate_line(row, **options) options = {row_sep: InputRecordSeparator.value}.merge(options) str = +"" if options[:encoding] str.force_encoding(options[:encoding]) else fallback_encoding = nil output_encoding = nil row.each do |field| next unless field.is_a?(String) fallback_encoding ||= field.encoding next if field.ascii_only? output_encoding = field.encoding break end output_encoding ||= fallback_encoding if output_encoding str.force_encoding(output_encoding) end end (new(str, **options) << row).string end
返回通过使用指定的 options
从生成 CSV 创建的 String。
参数 rows
必须是行的 Array。 Row
是 String 或 CSV::Row 的 Array。
特殊选项
-
选项
:row_sep
在 Ruby 3.0 或更高版本上默认为"\n"
,否则为$INPUT_RECORD_SEPARATOR
($/
)。$INPUT_RECORD_SEPARATOR # => "\n"
-
此方法接受一个额外的选项
:encoding
,它为输出设置基本编码。如果可能,此方法将尝试从row
中的第一个非nil
字段猜测您的编码,但您可能需要使用此参数作为备用方案。
对于其他 options
,请参阅 生成选项。
返回从生成的 String
CSV.generate_lines([['foo', '0'], ['bar', '1'], ['baz', '2']]) # => "foo,0\nbar,1\nbaz,2\n"
引发异常
# Raises NoMethodError (undefined method `each' for :foo:Symbol) CSV.generate_lines(:foo)
# File csv-3.3.2/lib/csv.rb, line 1558 def generate_lines(rows, **options) self.generate(**options) do |csv| rows.each do |row| csv << row end end end
创建或检索缓存的 CSV 对象。有关参数和选项,请参阅 CSV.new
。
此 API 不是 Ractor 安全的。
如果没有给出代码块,则返回一个 CSV 对象。
首次调用 instance
会创建并缓存一个 CSV 对象
s0 = 's0' csv0 = CSV.instance(s0) csv0.class # => CSV
后续使用 _相同_ string
或 io
调用 instance
会检索相同的缓存对象
csv1 = CSV.instance(s0) csv1.class # => CSV csv1.equal?(csv0) # => true # Same CSV object
后续使用 _不同_ string
或 io
调用 instance
会创建并缓存一个 _不同_ 的 CSV 对象。
s1 = 's1' csv2 = CSV.instance(s1) csv2.equal?(csv0) # => false # Different CSV object
所有缓存的对象仍然可用
csv3 = CSV.instance(s0) csv3.equal?(csv0) # true # Same CSV object csv4 = CSV.instance(s1) csv4.equal?(csv2) # true # Same CSV object
当给出代码块时,使用创建或检索的 CSV 对象调用代码块;返回代码块的返回值
CSV.instance(s0) {|csv| :foo } # => :foo
# File csv-3.3.2/lib/csv.rb, line 1026 def instance(data = $stdout, **options) # create a _signature_ for this method call, data object and options sig = [data.object_id] + options.values_at(*DEFAULT_OPTIONS.keys) # 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
返回使用 string
或 io
和指定的 options
创建的新 CSV 对象。
-
参数
string
应为 String 对象;它将放入一个新的 StringIO 对象中,并位于开头。 -
参数
io
应为 IO 对象,该对象-
打开以进行读取;返回时,该 IO 对象将关闭。
-
位于开头。要定位到末尾进行追加,请使用方法
CSV.generate
。对于任何其他定位,请传递预设的 StringIO 对象。
-
-
参数
options
:请参阅出于性能原因,无法在 CSV 对象中覆盖选项,因此此处指定的选项将一直有效。
除了 CSV 实例方法之外,还委托了一些 IO 方法。请参阅 委托方法。
从 String 对象创建 CSV 对象
csv = CSV.new('foo,0') csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
从 File 对象创建 CSV 对象
File.write('t.csv', 'foo,0') csv = CSV.new(File.open('t.csv')) csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
如果参数为 nil
,则引发异常
# Raises ArgumentError (Cannot parse nil as CSV): CSV.new(nil)
# File csv-3.3.2/lib/csv.rb, line 2034 def initialize(data, col_sep: ",", row_sep: :auto, quote_char: '"', field_size_limit: nil, max_field_size: nil, converters: nil, unconverted_fields: nil, headers: false, return_headers: false, write_headers: nil, header_converters: nil, skip_blanks: false, force_quotes: false, skip_lines: nil, liberal_parsing: false, internal_encoding: nil, external_encoding: nil, encoding: nil, nil_value: nil, empty_value: "", strip: false, quote_empty: true, write_converters: nil, write_nil_value: nil, write_empty_value: "") raise ArgumentError.new("Cannot parse nil as CSV") if data.nil? if data.is_a?(String) if encoding if encoding.is_a?(String) data_external_encoding, data_internal_encoding = encoding.split(":", 2) if data_internal_encoding data = data.encode(data_internal_encoding, data_external_encoding) else data = data.dup.force_encoding(data_external_encoding) end else data = data.dup.force_encoding(encoding) end end @io = StringIO.new(data) else @io = data end @encoding = determine_encoding(encoding, internal_encoding) @base_fields_converter_options = { nil_value: nil_value, empty_value: empty_value, } @write_fields_converter_options = { nil_value: write_nil_value, empty_value: write_empty_value, } @initial_converters = converters @initial_header_converters = header_converters @initial_write_converters = write_converters if max_field_size.nil? and field_size_limit max_field_size = field_size_limit - 1 end @parser_options = { column_separator: col_sep, row_separator: row_sep, quote_character: quote_char, max_field_size: max_field_size, unconverted_fields: unconverted_fields, headers: headers, return_headers: return_headers, skip_blanks: skip_blanks, skip_lines: skip_lines, liberal_parsing: liberal_parsing, encoding: @encoding, nil_value: nil_value, empty_value: empty_value, strip: strip, } @parser = nil @parser_enumerator = nil @eof_error = nil @writer_options = { encoding: @encoding, force_encoding: (not encoding.nil?), force_quotes: force_quotes, headers: headers, write_headers: write_headers, column_separator: col_sep, row_separator: row_sep, quote_character: quote_char, quote_empty: quote_empty, } @writer = nil writer if @writer_options[:write_headers] end
可能的选项元素
keyword form: :invalid => nil # raise error on invalid byte sequence (default) :invalid => :replace # replace invalid byte sequence :undef => :replace # replace undefined conversion :replace => string # replacement string ("?" or "\uFFFD" if not specified)
-
参数
path_or_io
必须是文件路径或 IO 流。 -
参数
io
应为 IO 对象,该对象-
打开以进行读取;返回时,该 IO 对象将关闭。
-
位于开头。要定位到末尾进行追加,请使用方法
CSV.generate
。对于任何其他定位,请传递预设的 StringIO 对象。
-
-
参数
mode
(如果给出)必须是文件模式。请参阅 访问模式。 -
参数
**options
必须是关键字选项。请参阅 生成选项。 -
此方法可以选择接受一个额外的
:encoding
选项,您可以使用它来指定从path
或io
读取的数据的编码。除非您的数据采用Encoding::default_external
给出的编码,否则您必须提供此选项。解析将使用此选项来确定如何解析数据。您可以提供第二个编码,以便在读取数据时对其进行转码。例如,encoding: 'UTF-32BE:UTF-8'
将从文件中读取
UTF-32BE
数据,但在解析之前将其转码为UTF-8
。
以下示例假设先前执行了
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) string_io = StringIO.new string_io << "foo,0\nbar,1\nbaz,2\n"
如果没有给出代码块,则返回一个新的 CSV 对象。
使用文件路径创建 CSV 对象
csv = CSV.open(path) csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
使用打开的 File 创建 CSV 对象
csv = CSV.open(File.open(path)) csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
使用 StringIO 创建 CSV 对象
csv = CSV.open(string_io) csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
当给出代码块时,使用创建的 CSV 对象调用代码块;返回代码块的返回值
使用文件路径
csv = CSV.open(path) {|csv| p csv} csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
输出
#<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
使用打开的 File
csv = CSV.open(File.open(path)) {|csv| p csv} csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
输出
#<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
使用 StringIO
csv = CSV.open(string_io) {|csv| p csv} csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
输出
#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
如果参数不是 String 对象或 IO 对象,则引发异常
# Raises TypeError (no implicit conversion of Symbol into String) CSV.open(:foo)
# File csv-3.3.2/lib/csv.rb, line 1647 def open(filename_or_io, mode="r", **options) # wrap a File opened with the remaining +args+ with no newline # decorator file_opts = {} may_enable_bom_detection_automatically(filename_or_io, mode, options, file_opts) file_opts.merge!(options) unless file_opts.key?(:newline) file_opts[:universal_newline] ||= false end options.delete(:invalid) options.delete(:undef) options.delete(:replace) options.delete_if {|k, _| /newline\z/.match?(k)} if filename_or_io.is_a?(StringIO) f = create_stringio(filename_or_io.string, mode, **file_opts) else begin f = File.open(filename_or_io, mode, **file_opts) rescue ArgumentError => e raise unless /needs binmode/.match?(e.message) and mode == "r" mode = "rb" file_opts = {encoding: Encoding.default_external}.merge(file_opts) retry end 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
使用指定的 options
解析 string
或 io
。
-
参数
string
应为 String 对象;它将放入一个新的 StringIO 对象中,并位于开头。 -
参数
io
应为 IO 对象,该对象-
打开以进行读取;返回时,该 IO 对象将关闭。
-
位于开头。要定位到末尾进行追加,请使用方法
CSV.generate
。对于任何其他定位,请传递预设的 StringIO 对象。
-
-
参数
options
:请参阅 解析选项
不带选项 headers
¶ ↑
没有 {选项 headers
} 的情况。
以下示例假设先前执行了
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string)
如果没有给出代码块,则返回一个由源形成的 Array 的 Array。
解析 String
a_of_a = CSV.parse(string) a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
解析打开的 File
a_of_a = File.open(path) do |file| CSV.parse(file) end a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
当给出代码块时,使用每个解析的行调用代码块
解析 String
CSV.parse(string) {|row| p row }
输出
["foo", "0"] ["bar", "1"] ["baz", "2"]
解析打开的 File
File.open(path) do |file| CSV.parse(file) {|row| p row } end
输出
["foo", "0"] ["bar", "1"] ["baz", "2"]
带选项 headers
¶ ↑
有 {选项 headers
} 的情况。
以下示例假设先前执行了
string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string)
如果没有给出代码块,则返回一个由源形成的 CSV::Table
对象。
解析 String
csv_table = CSV.parse(string, headers: ['Name', 'Count']) csv_table # => #<CSV::Table mode:col_or_row row_count:5>
解析打开的 File
csv_table = File.open(path) do |file| CSV.parse(file, headers: ['Name', 'Count']) end csv_table # => #<CSV::Table mode:col_or_row row_count:4>
当给出代码块时,使用每个解析的行调用代码块,这些行已被形成一个 CSV::Row
对象
解析 String
CSV.parse(string, headers: ['Name', 'Count']) {|row| p row }
输出
# <CSV::Row "Name":"foo" "Count":"0"> # <CSV::Row "Name":"bar" "Count":"1"> # <CSV::Row "Name":"baz" "Count":"2">
解析打开的 File
File.open(path) do |file| CSV.parse(file, headers: ['Name', 'Count']) {|row| p row } end
输出
# <CSV::Row "Name":"foo" "Count":"0"> # <CSV::Row "Name":"bar" "Count":"1"> # <CSV::Row "Name":"baz" "Count":"2">
如果参数不是 String 对象或 IO 对象,则引发异常
# Raises NoMethodError (undefined method `close' for :foo:Symbol) CSV.parse(:foo)
请确保您的文本是否包含 BOM。 CSV.parse
不会自动删除 BOM。您可能需要在调用 CSV.parse
之前删除 BOM
# remove BOM on calling File.open File.open(path, encoding: 'bom|utf-8') do |file| CSV.parse(file, headers: true) do |row| # you can get value by column name because BOM is removed p row['Name'] end end
输出
# "foo" # "bar" # "baz"
# File csv-3.3.2/lib/csv.rb, line 1825 def parse(str, **options, &block) csv = new(str, **options) return csv.each(&block) if block_given? # slurp contents, if no block is given begin csv.read ensure csv.close end end
返回使用指定的 options
解析 string
或 io
的第一行数据所创建的数据。
-
参数
string
应为 String 对象;它将放入一个新的 StringIO 对象中,并位于开头。 -
参数
io
应为 IO 对象,该对象-
打开以进行读取;返回时,该 IO 对象将关闭。
-
位于开头。要定位到末尾进行追加,请使用方法
CSV.generate
。对于任何其他定位,请传递预设的 StringIO 对象。
-
-
参数
options
:请参阅 解析选项
不使用 headers
选项¶ ↑
不使用 headers
选项时,将第一行作为新的数组返回。
以下示例假设先前执行了
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string)
从 String 对象解析第一行
CSV.parse_line(string) # => ["foo", "0"]
从 File 对象解析第一行
File.open(path) do |file| CSV.parse_line(file) # => ["foo", "0"] end # => ["foo", "0"]
如果参数是空字符串,则返回 nil
CSV.parse_line('') # => nil
使用 headers
选项¶ ↑
使用 {选项 headers
},将第一行作为 CSV::Row
对象返回。
以下示例假设先前执行了
string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string)
从 String 对象解析第一行
CSV.parse_line(string, headers: true) # => #<CSV::Row "Name":"foo" "Count":"0">
从 File 对象解析第一行
File.open(path) do |file| CSV.parse_line(file, headers: true) end # => #<CSV::Row "Name":"foo" "Count":"0">
如果参数为 nil
,则引发异常
# Raises ArgumentError (Cannot parse nil as CSV): CSV.parse_line(nil)
# File csv-3.3.2/lib/csv.rb, line 1898 def parse_line(line, **options) new(line, **options).each.first end
使用给定的 options
打开给定的 source
(请参阅 CSV.open
),读取 source(请参阅 CSV#read
),并返回结果,结果将是数组的数组或 CSV::Table
。
不带标题行
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
带标题行
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.read(path, headers: true) # => #<CSV::Table mode:col_or_row row_count:4>
# File csv-3.3.2/lib/csv.rb, line 1922 def read(path, **options) open(path, **options) { |csv| csv.read } end
CSV.read
的别名。
# File csv-3.3.2/lib/csv.rb, line 1930 def readlines(path, **options) read(path, **options) end
使用 source
,options
和某些默认选项调用 CSV.read
-
headers
:true
-
converters
::numeric
-
header_converters
::symbol
返回 CSV::Table
对象。
示例
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:4>
# File csv-3.3.2/lib/csv.rb, line 1949 def table(path, **options) default_options = { headers: true, converters: :numeric, header_converters: :symbol, } options = default_options.merge(options) read(path, **options) end
私有类方法
# File csv-3.3.2/lib/csv.rb, line 1984 def create_stringio(str, mode, opts) opts.delete_if {|k, _| k == :universal_newline or DEFAULT_OPTIONS.key?(k)} raise ArgumentError, "Unsupported options parsing StringIO: #{opts.keys}" unless opts.empty? StringIO.new(str, mode) end
# File csv-3.3.2/lib/csv.rb, line 1963 def may_enable_bom_detection_automatically(filename_or_io, mode, options, file_opts) if filename_or_io.is_a?(StringIO) # Support to StringIO was dropped for Ruby 2.6 and earlier without BOM support: # https://github.com/ruby/stringio/pull/47 return if RUBY_VERSION < "2.7" else # "bom|utf-8" may be buggy on Windows: # https://bugs.ruby-lang.org/issues/20526 return if ON_WINDOWS end return unless Encoding.default_external == Encoding::UTF_8 return if options.key?(:encoding) return if options.key?(:external_encoding) return if mode.include?(":") file_opts[:encoding] = "bom|utf-8" end
公共实例方法
将行附加到 self
。
-
参数
row
必须是 Array 对象或CSV::Row
对象。 -
输出流必须打开以进行写入。
附加数组
CSV.generate do |csv| csv << ['foo', 0] csv << ['bar', 1] csv << ['baz', 2] end # => "foo,0\nbar,1\nbaz,2\n"
附加 CSV::Rows
headers = [] CSV.generate do |csv| csv << CSV::Row.new(headers, ['foo', 0]) csv << CSV::Row.new(headers, ['bar', 1]) csv << CSV::Row.new(headers, ['baz', 2]) end # => "foo,0\nbar,1\nbaz,2\n"
CSV::Row
对象中的标题不会被附加
headers = ['Name', 'Count'] CSV.generate do |csv| csv << CSV::Row.new(headers, ['foo', 0]) csv << CSV::Row.new(headers, ['bar', 1]) csv << CSV::Row.new(headers, ['baz', 2]) end # => "foo,0\nbar,1\nbaz,2\n"
如果 row
不是数组或 CSV::Row,则会引发异常
CSV.generate do |csv| # Raises NoMethodError (undefined method `collect' for :foo:Symbol) csv << :foo end
如果输出流未打开以进行写入,则会引发异常
path = 't.csv' File.write(path, '') File.open(path) do |file| CSV.open(file) do |csv| # Raises IOError (not opened for writing) csv << ['foo', 0] end end
# File csv-3.3.2/lib/csv.rb, line 2507 def <<(row) writer << row self end
# File csv-3.3.2/lib/csv.rb, line 2396 def binmode? if @io.respond_to?(:binmode?) @io.binmode? else false end end
返回编码的列分隔符;用于解析和写入;请参阅 {选项 col_sep
}
CSV.new('').col_sep # => ","
# File csv-3.3.2/lib/csv.rb, line 2144 def col_sep parser.column_separator end
-
如果没有块,则安装字段转换器(Proc)。
-
如果有块,则定义并安装自定义字段转换器。
-
返回已安装的字段转换器数组。
-
如果给定参数
converter_name
,则它应该是现有字段转换器的名称。
请参阅 字段转换器。
如果没有块,则安装字段转换器
csv = CSV.new('') csv.convert(:integer) csv.convert(:float) csv.convert(:date) csv.converters # => [:integer, :float, :date]
如果给定块,则会为每个字段调用该块
-
参数
field
是字段值。 -
参数
field_info
是一个CSV::FieldInfo
对象,其中包含有关字段的详细信息。
此处的示例假设先前执行了
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string)
给出块的示例
csv = CSV.open(path) csv.convert {|field, field_info| p [field, field_info]; field.upcase } csv.read # => [["FOO", "0"], ["BAR", "1"], ["BAZ", "2"]]
输出
["foo", #<struct CSV::FieldInfo index=0, line=1, header=nil>] ["0", #<struct CSV::FieldInfo index=1, line=1, header=nil>] ["bar", #<struct CSV::FieldInfo index=0, line=2, header=nil>] ["1", #<struct CSV::FieldInfo index=1, line=2, header=nil>] ["baz", #<struct CSV::FieldInfo index=0, line=3, header=nil>] ["2", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
该块不必返回 String 对象
csv = CSV.open(path) csv.convert {|field, field_info| field.to_sym } csv.read # => [[:foo, :"0"], [:bar, :"1"], [:baz, :"2"]]
如果给定 converter_name
,则不会调用该块
csv = CSV.open(path) csv.convert(:integer) {|field, field_info| fail 'Cannot happen' } csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
如果 converter_name
不是内置字段转换器的名称,则会引发解析时异常
csv = CSV.open(path) csv.convert(:nosuch) => [nil] # Raises NoMethodError (undefined method `arity' for nil:NilClass) csv.read
# File csv-3.3.2/lib/csv.rb, line 2578 def convert(name = nil, &converter) parser_fields_converter.add_converter(name, &converter) end
返回包含字段转换器的数组;请参阅 字段转换器
csv = CSV.new('') csv.converters # => [] csv.convert(:integer) csv.converters # => [:integer] csv.convert(proc {|x| x.to_s }) csv.converters
请注意,您需要在主 Ractor 上调用 +Ractor.make_shareable(CSV::Converters
)+ 才能使用此方法。
# File csv-3.3.2/lib/csv.rb, line 2217 def converters parser_fields_converter.map do |converter| name = Converters.rassoc(converter) name ? name.first : converter end end
使用每个连续的行调用该块。必须打开数据源以进行读取。
不带标题行
string = "foo,0\nbar,1\nbaz,2\n" csv = CSV.new(string) csv.each do |row| p row end
输出
["foo", "0"] ["bar", "1"] ["baz", "2"]
带标题行
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" csv = CSV.new(string, headers: true) csv.each do |row| p row end
输出
<CSV::Row "Name":"foo" "Value":"0"> <CSV::Row "Name":"bar" "Value":"1"> <CSV::Row "Name":"baz" "Value":"2">
如果未打开源进行读取,则会引发异常
string = "foo,0\nbar,1\nbaz,2\n" csv = CSV.new(string) csv.close # Raises IOError (not opened for reading) csv.each do |row| p row end
# File csv-3.3.2/lib/csv.rb, line 2689 def each(&block) return to_enum(__method__) unless block_given? begin while true yield(parser_enumerator.next) end rescue StopIteration end end
# File csv-3.3.2/lib/csv.rb, line 2432 def eof? return false if @eof_error begin parser_enumerator.peek false rescue MalformedCSVError => error @eof_error = error false rescue StopIteration true end end
返回字段大小的限制;用于解析;请参阅 {选项 field_size_limit
}
CSV.new('').field_size_limit # => nil
自 3.2.3 起已弃用。请改用 max_field_size
。
# File csv-3.3.2/lib/csv.rb, line 2176 def field_size_limit parser.field_size_limit end
# File csv-3.3.2/lib/csv.rb, line 2404 def flock(*args) raise NotImplementedError unless @io.respond_to?(:flock) @io.flock(*args) end
返回确定是否要引用所有输出字段的值;用于生成;请参阅 {选项 force_quotes
}
CSV.new('').force_quotes? # => false
# File csv-3.3.2/lib/csv.rb, line 2307 def force_quotes? @writer_options[:force_quotes] end
该块不必返回 String 对象
csv = CSV.open(path, headers: true) csv.header_convert {|header, field_info| header.to_sym } table = csv.read table.headers # => [:Name, :Value]
如果给定 converter_name
,则不会调用该块
csv = CSV.open(path, headers: true) csv.header_convert(:downcase) {|header, field_info| fail 'Cannot happen' } table = csv.read table.headers # => ["name", "value"]
如果 converter_name
不是内置字段转换器的名称,则会引发解析时异常
csv = CSV.open(path, headers: true) csv.header_convert(:nosuch) # Raises NoMethodError (undefined method `arity' for nil:NilClass) csv.read
# File csv-3.3.2/lib/csv.rb, line 2644 def header_convert(name = nil, &converter) header_fields_converter.add_converter(name, &converter) end
返回包含标题转换器的数组;用于解析;请参阅 标题转换器
CSV.new('').header_converters # => []
请注意,您需要在主 Ractor 上调用 +Ractor.make_shareable(CSV::HeaderConverters
)+ 才能使用此方法。
# File csv-3.3.2/lib/csv.rb, line 2283 def header_converters header_fields_converter.map do |converter| name = HeaderConverters.rassoc(converter) name ? name.first : converter end end
如果接下来要读取的行是标题行,则返回 true
;否则返回 false
。
不带标题行
string = "foo,0\nbar,1\nbaz,2\n" csv = CSV.new(string) csv.header_row? # => false
带标题行
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" csv = CSV.new(string, headers: true) csv.header_row? # => true csv.shift # => #<CSV::Row "Name":"foo" "Value":"0"> csv.header_row? # => false
如果未打开源进行读取,则会引发异常
string = "foo,0\nbar,1\nbaz,2\n" csv = CSV.new(string) csv.close # Raises IOError (not opened for reading) csv.header_row?
# File csv-3.3.2/lib/csv.rb, line 2766 def header_row? parser.header_row? end
返回确定是否使用标题的值;用于解析;请参阅 {选项 headers
}
CSV.new('').headers # => nil
# File csv-3.3.2/lib/csv.rb, line 2241 def headers if @writer @writer.headers else parsed_headers = parser.headers return parsed_headers if parsed_headers raw_headers = @parser_options[:headers] raw_headers = nil if raw_headers == false raw_headers end end
返回一个 String,显示 self
的某些属性
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" csv = CSV.new(string, headers: true) s = csv.inspect s # => "#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:\",\" row_sep:\"\\n\" quote_char:\"\\\"\" headers:true>"
# File csv-3.3.2/lib/csv.rb, line 2825 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 ["lineno", "col_sep", "row_sep", "quote_char"].each do |attr_name| if a = __send__(attr_name) str << " " << attr_name << ":" << a.inspect end end ["skip_blanks", "liberal_parsing"].each do |attr_name| if a = __send__("#{attr_name}?") str << " " << attr_name << ":" << a.inspect end end _headers = headers str << " headers:" << _headers.inspect if _headers 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
# File csv-3.3.2/lib/csv.rb, line 2409 def ioctl(*args) raise NotImplementedError unless @io.respond_to?(:ioctl) @io.ioctl(*args) end
返回确定是否要处理非法输入的值;用于解析;请参阅 {选项 liberal_parsing
}
CSV.new('').liberal_parsing? # => false
# File csv-3.3.2/lib/csv.rb, line 2317 def liberal_parsing? parser.liberal_parsing? end
返回最近读取的行
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.open(path) do |csv| csv.each do |row| p [csv.lineno, csv.line] end end
输出
[1, "foo,0\n"] [2, "bar,1\n"] [3, "baz,2\n"]
# File csv-3.3.2/lib/csv.rb, line 2382 def line parser.line end
返回已解析或生成的行数。
解析
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) CSV.open(path) do |csv| csv.each do |row| p [csv.lineno, row] end end
输出
[1, ["foo", "0"]] [2, ["bar", "1"]] [3, ["baz", "2"]]
生成
CSV.generate do |csv| p csv.lineno; csv << ['foo', 0] p csv.lineno; csv << ['bar', 1] p csv.lineno; csv << ['baz', 2] end
输出
0 1 2
# File csv-3.3.2/lib/csv.rb, line 2358 def lineno if @writer @writer.lineno else parser.lineno end end
返回字段大小的限制;用于解析;请参阅 {选项 max_field_size
}
CSV.new('').max_field_size # => nil
自 3.2.3 起。
# File csv-3.3.2/lib/csv.rb, line 2188 def max_field_size parser.max_field_size end
# File csv-3.3.2/lib/csv.rb, line 2414 def path @io.path if @io.respond_to?(:path) end
返回编码的引号字符;用于解析和写入;请参阅 {选项 quote_char
}
CSV.new('').quote_char # => "\""
# File csv-3.3.2/lib/csv.rb, line 2164 def quote_char parser.quote_character end
从 self
中形成剩余的行到
-
如果正在使用标题,则为
CSV::Table
对象。 -
否则为数组的数组。
必须打开数据源以进行读取。
不带标题行
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) csv = CSV.open(path) csv.read # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
带标题行
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string) csv = CSV.open(path, headers: true) csv.read # => #<CSV::Table mode:col_or_row row_count:4>
如果未打开源进行读取,则会引发异常
string = "foo,0\nbar,1\nbaz,2\n" csv = CSV.new(string) csv.close # Raises IOError (not opened for reading) csv.read
# File csv-3.3.2/lib/csv.rb, line 2730 def read rows = to_a if parser.use_headers? Table.new(rows, headers: parser.headers) else rows end end
返回确定是否要返回标题的值;用于解析;请参阅 {选项 return_headers
}
CSV.new('').return_headers? # => false
# File csv-3.3.2/lib/csv.rb, line 2259 def return_headers? parser.return_headers? end
倒回基础 IO 对象并重置 CSV 的 lineno() 计数器。
# File csv-3.3.2/lib/csv.rb, line 2447 def rewind @parser = nil @parser_enumerator = nil @eof_error = nil @writer.rewind if @writer @io.rewind end
返回编码的行分隔符;用于解析和写入;请参阅 {选项 row_sep
}
CSV.new('').row_sep # => "\n"
# File csv-3.3.2/lib/csv.rb, line 2154 def row_sep parser.row_separator end
将下一行数据作为
-
如果不使用标题,则为数组。
-
如果使用了标题,则为
CSV::Row
对象。
必须打开数据源以进行读取。
不带标题行
string = "foo,0\nbar,1\nbaz,2\n" csv = CSV.new(string) csv.shift # => ["foo", "0"] csv.shift # => ["bar", "1"] csv.shift # => ["baz", "2"] csv.shift # => nil
带标题行
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" csv = CSV.new(string, headers: true) csv.shift # => #<CSV::Row "Name":"foo" "Value":"0"> csv.shift # => #<CSV::Row "Name":"bar" "Value":"1"> csv.shift # => #<CSV::Row "Name":"baz" "Value":"2"> csv.shift # => nil
如果未打开源进行读取,则会引发异常
string = "foo,0\nbar,1\nbaz,2\n" csv = CSV.new(string) csv.close # Raises IOError (not opened for reading) csv.shift
# File csv-3.3.2/lib/csv.rb, line 2803 def shift if @eof_error eof_error, @eof_error = @eof_error, nil raise eof_error end begin parser_enumerator.next rescue StopIteration nil end end
返回确定是否要忽略空白行的值;用于解析;请参阅 {选项 skip_blanks
}
CSV.new('').skip_blanks? # => false
# File csv-3.3.2/lib/csv.rb, line 2296 def skip_blanks? parser.skip_blanks? end
返回用于识别注释行的正则表达式;用于解析;请参阅 {选项 skip_lines
}
CSV.new('').skip_lines # => nil
# File csv-3.3.2/lib/csv.rb, line 2198 def skip_lines parser.skip_lines end
# File csv-3.3.2/lib/csv.rb, line 2418 def stat(*args) raise NotImplementedError unless @io.respond_to?(:stat) @io.stat(*args) end
# File csv-3.3.2/lib/csv.rb, line 2423 def to_i raise NotImplementedError unless @io.respond_to?(:to_i) @io.to_i end
# File csv-3.3.2/lib/csv.rb, line 2428 def to_io @io.respond_to?(:to_io) ? @io.to_io : @io end
返回一个值,该值确定是否提供未转换的字段;用于解析;请参阅 {选项 unconverted_fields
}
CSV.new('').unconverted_fields? # => nil
# File csv-3.3.2/lib/csv.rb, line 2231 def unconverted_fields? parser.unconverted_fields? end
返回一个值,该值确定是否写入标题;用于生成;请参阅 {选项 write_headers
}
CSV.new('').write_headers? # => nil
# File csv-3.3.2/lib/csv.rb, line 2269 def write_headers? @writer_options[:write_headers] end
私有实例方法
# File csv-3.3.2/lib/csv.rb, line 2957 def build_fields_converter(initial_converters, options) fields_converter = FieldsConverter.new(options) normalize_converters(initial_converters).each do |name, converter| fields_converter.add_converter(name, &converter) end fields_converter end
# File csv-3.3.2/lib/csv.rb, line 2939 def build_header_fields_converter specific_options = { builtin_converters_name: :HeaderConverters, accept_nil: true, } options = @base_fields_converter_options.merge(specific_options) build_fields_converter(@initial_header_converters, options) end
# File csv-3.3.2/lib/csv.rb, line 2927 def build_parser_fields_converter specific_options = { builtin_converters_name: :Converters, } options = @base_fields_converter_options.merge(specific_options) build_fields_converter(@initial_converters, options) end
# File csv-3.3.2/lib/csv.rb, line 2952 def build_writer_fields_converter build_fields_converter(@initial_write_converters, @write_fields_converter_options) end
使用 @converters
处理 fields
,如果将 headers
传递为 true
,则使用 @header_converters
处理,返回转换后的字段集。任何将字段更改为非 String
类型的值的转换器都会停止该字段的转换管道。这主要是为了提高效率的快捷方式。
# File csv-3.3.2/lib/csv.rb, line 2902 def convert_fields(fields, headers = false) if headers header_fields_converter.convert(fields, nil, 0) else parser_fields_converter.convert(fields, @headers, lineno) end end
# File csv-3.3.2/lib/csv.rb, line 2865 def determine_encoding(encoding, internal_encoding) # honor the IO encoding if we can, otherwise default to ASCII-8BIT io_encoding = raw_encoding return io_encoding if io_encoding return Encoding.find(internal_encoding) if internal_encoding if encoding encoding, = encoding.split(":", 2) if encoding.is_a?(String) return Encoding.find(encoding) end Encoding.default_internal || Encoding.default_external end
# File csv-3.3.2/lib/csv.rb, line 2935 def header_fields_converter @header_fields_converter ||= build_header_fields_converter end
# File csv-3.3.2/lib/csv.rb, line 2880 def normalize_converters(converters) converters ||= [] unless converters.is_a?(Array) converters = [converters] end converters.collect do |converter| case converter when Proc # custom code block [nil, converter] else # by name [converter, nil] end end end
# File csv-3.3.2/lib/csv.rb, line 2965 def parser @parser ||= Parser.new(@io, parser_options) end
# File csv-3.3.2/lib/csv.rb, line 2974 def parser_enumerator @parser_enumerator ||= parser.parse end
# File csv-3.3.2/lib/csv.rb, line 2923 def parser_fields_converter @parser_fields_converter ||= build_parser_fields_converter end
# File csv-3.3.2/lib/csv.rb, line 2969 def parser_options @parser_options.merge(header_fields_converter: header_fields_converter, fields_converter: parser_fields_converter) end
返回内部 IO 对象的编码。
# File csv-3.3.2/lib/csv.rb, line 2913 def raw_encoding if @io.respond_to? :internal_encoding @io.internal_encoding || @io.external_encoding elsif @io.respond_to? :encoding @io.encoding else nil end end
# File csv-3.3.2/lib/csv.rb, line 2978 def writer @writer ||= Writer.new(@io, writer_options) end
# File csv-3.3.2/lib/csv.rb, line 2948 def writer_fields_converter @writer_fields_converter ||= build_writer_fields_converter end
# File csv-3.3.2/lib/csv.rb, line 2982 def writer_options @writer_options.merge(header_fields_converter: header_fields_converter, fields_converter: writer_fields_converter) end