класс CSV
CSV
CSV
Данные CSV (значения, разделённые запятыми) представляют собой текстовое представление таблицы:
-
Разделитель строк определяет строки таблицы. Общим разделителем строк является символ новой строки
"\n". -
Разделитель столбцов определяет поля в строке. Общим разделителем столбцов является запятая
",".
Эта строка CSV с разделителем строк "\n" и разделителем столбцов ",", содержит три строки и два столбца:
"foo,0\nbar,1\nbaz,2\n"
Несмотря на название CSV, в представлении CSV могут использоваться разные разделители.
Для получения дополнительной информации о таблицах, см. статью Википедии «Таблица (информация)», особенно раздел «Простая таблица»
Класс CSV
Class CSV предоставляет методы для:
-
Разбора данных CSV из объекта String, файла (через его путь) или объекта IO.
-
Генерации данных 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 методом экземпляра String#parse_csv, который также возвращает только первую строку:
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 возвращает строку; в этом примере используется метод 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 методом экземпляра Array#to_csv, который преобразует массив в строку:
['foo', '0'].to_csv # => "foo,0\n"
Фильтрация CSV
Метод CSV.filter предоставляет фильтр в стиле Unix для данных CSV. Данные входных данных обрабатываются для формирования данных выходных данных:
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#string
-
IO#truncate
Параметры
Значения параметров по умолчанию:
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
Указывает разделитель строк, строку или символ :auto (см. ниже), который будет использоваться как для разбора, так и для генерации.
Значение по умолчанию:
CSV::DEFAULT_OPTIONS.fetch(:row_sep) # => :auto
Когда row_sep является строкой, эта строка становится разделителем строк. String будет преобразовано в Encoding данных перед использованием.
Использование "\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"]]
Использование | (pipe):
row_sep = '|' str = CSV.generate(row_sep: row_sep) do |csv| csv << [:foo, 0] csv << [:bar, 1] csv << [:baz, 2] end str # => "foo,0|bar,1|baz,2|" ary = CSV.parse(str, row_sep: row_sep) ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
Использование -- (два дефиса):
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 ($/) используется, если выполняется одно из следующих условий:
-
Ни одна из этих последовательностей не найдена.
-
DataявляетсяARGF,STDIN,STDOUT, илиSTDERR. -
Поток доступен только для вывода.
Очевидно, что обнаружение занимает некоторое время. Set вручную, если скорость важна. Также обратите внимание, что объекты IO на Windows должны быть открыты в двоичном режиме, если эта функция будет использоваться, так как перевод окончания строки может вызвать проблемы с переустановкой позиции документа к положению до считывания вперёд.
Параметр 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, вызывается метод CSV::parse_line(str, options) с текущим 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
Устанавливает объект для определения строк комментариев во входных данных, которые должны быть проигнорированы:
-
Если Regexp, игнорирует строки, которые ему соответствуют.
-
Если строка, преобразует её в Regexp, игнорирует строки, которые ему соответствуют.
-
Если
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"]]
Использование Regexp:
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"]]
Возникает исключение, если задан объект, который не является Regexp, строкой или 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
Указывает разделитель строк, строку или символ :auto (см. ниже), который используется для разбора и генерации.
Значение по умолчанию:
CSV::DEFAULT_OPTIONS.fetch(:row_sep) # => :auto
Когда row_sep является строкой, эта строка становится разделителем строк. String будет преобразована в Encoding данных перед использованием.
Использование "\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 ($/) используется, если выполняется одно из следующих условий:
-
Ни одна из этих последовательностей не найдена.
-
DataравноARGF,STDIN,STDOUT, илиSTDERR. -
Поток доступен только для вывода.
Очевидно, обнаружение занимает немного времени. Set вручную, если скорость важна. Также обратите внимание, что объекты IO должны открываться в двоичном режиме на Windows, если эта функция будет использоваться, так как преобразование окончания строк может вызвать проблемы с возвращением позиции документа к исходному состоянию до считывания.
Параметр col_sep
Указывает разделитель полей типа String, который используется для разбора и генерации. Строка будет преобразована в кодировку данных перед использованием.
Значение по умолчанию:
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, формируется в строку. Вы можете использовать преобразователь поля или преобразователь заголовка, чтобы перехватить и изменить проанализированные значения:
Также по умолчанию каждое значение, подлежащее записи при генерации, записывается «как есть». Вы можете использовать преобразователь записи, чтобы изменить значения перед записью.
Указание преобразователей
Вы можете указать преобразователи для разбора или генерации в аргументе options различных методов CSV:
-
Параметр
convertersдля преобразования проанализированных значений полей. -
Параметр
header_convertersдля преобразования проанализированных значений заголовков. -
Параметр
write_convertersдля преобразования значений для записи (генерации).
Существуют три формы указания преобразователей:
-
Процедура преобразователя: исполняемый код, используемый для преобразования.
-
Имя преобразователя: имя хранимого преобразователя.
-
Список преобразователей: массив процедур преобразователей, имён преобразователей и списков преобразователей.
Процедуры преобразователей
Эта процедура преобразователя, strip_converter, принимает значение field и возвращает field.strip:
strip_converter = proc {|field| field.strip }
В этом вызове CSV.parse, ключевой аргумент converters: string_converter указывает, что:
-
Процедура
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"]]
Процедура преобразователя может принимать второй аргумент, 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 отображает:
-
Индекс поля с нулевым основанием.
-
Индекс строки с единичным основанием.
-
Заголовок поля, если таковой имеется.
Хранимые преобразователи
Преобразователю может быть присвоено имя и он может быть сохранён в структуре, где методы разбора могут найти его по имени.
Структура хранения для преобразователей полей — хэш CSV::Converters. В ней есть несколько встроенных процедур преобразователей:
-
:integer: Преобразует каждый целое число, встроенное в строку, в истинное целое число. -
:float: Преобразует каждое дробное число, встроенное в строку, в истинное число с плавающей запятой. -
:date: Преобразует каждую строку даты в истинную дату. -
:date_time: Преобразует каждую строку даты и времени в истинное значение DateTime.
. Этот пример создаёт процедуру преобразователя, затем сохраняет её:
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, которая работает аналогичным образом. Она также содержит встроенные процедуры преобразователей:
-
:downcase: Приводит каждый заголовок к нижнему регистру. -
:symbol: Преобразует каждый заголовок в символ.
Такой структуры хранения для преобразователей записи не существует.
Для доступа к хранимым преобразователям в неглавных Ractor'ах методам парсинга, структура хранения должна быть сначала сделана совместной. Поэтому, Ractor.make_shareable(CSV::Converters) и Ractor.make_shareable(CSV::HeaderConverters) должны быть вызваны перед созданием Ractor'ов, использующих преобразователи, хранящиеся в этих структурах. (Так как создание общих структур хранения подразумевает их заморозку, любые пользовательские преобразователи, которые должны быть использованы, должны быть добавлены сначала.)
Списки преобразователей
Список преобразователей — это массив, который может включать любые комбинации:
-
Процедуры преобразования.
-
Имена хранящихся преобразователей.
-
Вложенные списки преобразователей.
Примеры:
numeric_converters = [:integer, :float] date_converters = [:date, :date_time] [numeric_converters, strip_converter] [strip_converter, date_converters, :float]
Как процедура преобразования, список преобразователей может быть именован и сохранён в 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.)
Существует три способа использования преобразователей полей.
-
Использование опции converters с методом парсинга:
ary = CSV.parse(string, converters: :integer) ary # => [0, 1, 2] # => [["foo", 0], ["bar", 1], ["baz", 2]]
-
Использование опции converters с новым экземпляром CSV:
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:
-
Каждый ключ — это имя преобразователя поля.
-
Каждое значение — это одно из:
-
Процедура преобразования поля.
-
Массив имён преобразователей поля.
-
Вывод:
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] [: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"]
Преобразователь :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"]
-
Опция
header_convertersс новым экземпляром CSV: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"]
-
Methodheader_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 преобразует каждый заголовок в символ:
string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2" tbl = CSV.parse(string, headers: true, header_converters: :symbol) tbl.headers # => [:name, :count]
Детали:
-
Удаляет ведущие и хвостовые пробелы.
-
Преобразует заголовок в нижний регистр.
-
Заменяет встроеные пробелы на символы подчёркивания.
-
Удаляет символы, не являющиеся словами.
-
Преобразует строку в символ.
Пользовательские преобразователи заголовков
Вы можете определить пользовательский преобразователь заголовка:
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 парсер умеет работать с кодировками. Парсер работает в кодировке Encoding объекта IO или String, из которого или в который он считывается/записывается. Ваши данные никогда не транскодируются (если вы не попросите Руби их транскодировать), а будут буквально обработаны в Encoding кодировке. Таким образом, CSV вернёт массивы или строки строк в Encoding кодировке ваших данных. Это достигается путём транскодирования самого парсера в Encoding кодировку.
Некоторые транскодирования, конечно, должны произойти, чтобы реализовать поддержку множественных кодировок. Например, :col_sep, :row_sep, и :quote_char должны быть транскодированы в соответствии с вашими данными. Надеюсь, это делает весь процесс прозрачным, так как настройки по умолчанию CSV просто должны магически работать с вашими данными. Однако, вы можете установить эти значения вручную в целевой Encoding кодировке, чтобы избежать транскодирования.
Также важно отметить, что, хотя весь основной парсер CSV теперь Encoding не зависит от кодировки, некоторые функции не зависят от неё. Например, встроенные преобразователи будут пытаться транскодировать данные в UTF-8 перед выполнением преобразований. Снова, вы можете предоставить пользовательские преобразователи, которые знают о ваших кодировках, чтобы избежать этого транскодирования. Мне просто слишком сложно поддерживать родные преобразования для всех кодировок Руби.
В любом случае, практическая сторона этого проста: убедитесь, что объекты IO и String, переданные в CSV, имеют соответствующую Encoding кодировку, и всё должно работать. Методы CSV, которые позволяют вам открывать объекты IO (CSV::foreach(), CSV::open(), CSV::read() и CSV::readlines()), позволяют вам указать Encoding кодировку.
Одно небольшое исключение возникает при генерации CSV в String с Encoding кодировкой, которая несовместима с ASCII. Нет никаких имеющихся данных для CSV, которые можно использовать для подготовки, и, следовательно, вам, вероятно, придётся вручную указать желаемую Encoding кодировку для большинства таких случаев. Тем не менее, он попытается угадать, используя поля в строке вывода, когда используется CSV::generate_line() или Array#to_csv().
Я стараюсь указать любые другие Encoding проблемы в документации методов по мере их возникновения.
Это было протестировано по возможности со всеми не «фиктивными» кодировками, которые Руби поставляет. Однако, это новый код, и в нём могут быть ошибки. Пожалуйста, не стесняйтесь сообщить о любых проблемах, которые вы обнаружите.
Константы
- ConverterEncoding
-
Кодировка, используемая всеми конвертерами.
- Converters
-
Хэш, содержащий имена и Процедуры для встроенных конвертеров полей. См. Встроенные конвертеры полей.
Этот хэш намеренно оставлен незамороженным и может быть расширен с помощью пользовательских конвертеров полей. См. Пользовательские конвертеры полей.
- DEFAULT_OPTIONS
-
Значения по умолчанию для параметров метода.
- DateMatcher
-
Регулярное выражение, используемое для поиска и преобразования некоторых распространенных форматов дат.
- DateTimeMatcher
-
Регулярное выражение, используемое для поиска и преобразования некоторых распространенных форматов дат и времени.
- FieldInfo
-
Структура данных, содержащая детали о позиции поля в источнике данных, из которого оно было прочитано.
CSVпередаст эту структуру некоторым блокам, которые принимают решения, основанные на структуре поля. См.CSV.convert_fields()для примера.-
index -
Индекс поля в строке (нумерация с нуля).
-
line -
Строка источника данных, из которой взята данная строка.
-
header -
Заголовок столбца, если он доступен.
-
quoted? -
Истина или ложь, указывает, было ли исходное значение заключено в кавычки.
-
- HeaderConverters
-
Хэш, содержащий имена и Процедуры для встроенных конвертеров заголовков. См. Встроенные конвертеры заголовков.
Этот хэш намеренно оставлен незамороженным и может быть расширен с помощью пользовательских конвертеров полей. См. Пользовательские конвертеры заголовков.
- VERSION
-
Версия установленной библиотеки.
Атрибуты
:call-seq:
csv.encoding -> encoding
Возвращает кодировку, используемую для парсинга и генерации; см. Кодировки символов (M17n или Многоязычность):
CSV.new('').encoding # => #<Encoding:UTF-8>
Методы публичного класса
# File lib/csv.rb, line 1202
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 -
Парсит CSV из источника (строка, поток ввода-вывода или
ARGF). -
Вызывает заданный блок для каждой прочитанной строки:
-
Без заголовков, каждая строка — массив.
-
С заголовками, каждая строка —
CSV::Row.
-
-
Генерирует CSV в выходной поток (строка, поток ввода-вывода или стандартный вывод).
-
Возвращает прочитанный источник:
-
Без заголовков — массив массивов.
-
С заголовками —
CSV::Table.
-
Когда in_string_or_io задан, но out_string_or_io нет, парсит из заданного in_string_or_io и генерирует в стандартный вывод.
Входной строковый тип данных без заголовков:
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]]
Вывод (в стандартный вывод):
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>
Вывод (в стандартный вывод):
Name,Value FOO,0 BAR,-1 BAZ,-2
Входной поток ввода-вывода без заголовков:
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]]
Вывод (в стандартный вывод):
FOO,0 BAR,-1 BAZ,-2
Входной поток ввода-вывода с заголовками:
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>
Вывод (в стандартный вывод):
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"
Выходной поток ввода-вывода без заголовков:
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"
Выходной поток ввода-вывода с заголовками:
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 и генерирует в стандартный вывод.
Без заголовков:
# 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")
Вывод (в стандартный вывод):
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")
Вывод (в стандартный вывод):
Name,Value FOO,0 BAR,-1 BAZ,-2
Аргументы:
-
Аргумент
in_string_or_ioдолжен быть строкой или потоком ввода-вывода. -
Аргумент
out_string_or_ioдолжен быть строкой или потоком ввода-вывода. -
Аргументы
**optionsдолжны быть именованными опциями. См. Опции для парсинга.
# File lib/csv.rb, line 1332
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 Вызывает блок для каждой строки, считанной из источника 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">
Входной поток ввода-вывода без заголовков:
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"]
Входной поток ввода-вывода с заголовками:
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">
Без блока возвращает перечислитель:
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должен быть путём к файлу или потоком ввода-вывода. -
Аргумент
mode, если задан, должен быть режимом файла. См. Режимы доступа. -
Аргументы
**optionsдолжны быть именованными опциями. См. Опции для парсинга. -
Этот метод необязательно принимает дополнительную опцию
:encoding, которую можно использовать для указания кодировкиEncodingданных, считываемых изpathилиio.Необходимо предоставить, если ваши данные не в кодировке, указанной в
Encoding::default_external. Парсинг использует её для определения способа парсинга данных. Можно указать вторуюEncodingдля транскодирования данных при чтении. Например,encoding: 'UTF-32BE:UTF-8'
считает данные в формате
UTF-32BEиз файла, но транскодирует их вUTF-8перед парсингом.
# File lib/csv.rb, line 1398
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 -
Аргумент
csv_string, если задан, должен быть объектом String; по умолчанию — новая пустая строка. -
Аргументы
options, если заданы, должны быть опциями генерации. См. Опции для генерации.
Создаёт новый объект CSV через CSV.new(csv_string, **options); вызывает блок с объектом CSV, который блок может изменить; возвращает строку, сгенерированную из объекта CSV.
Обратите внимание, что переданная строка изменяется этим методом. Используйте csv_string.dup, если строку необходимо сохранить.
Этот метод имеет дополнительную опцию: :encoding, которая устанавливает базовую Encoding для вывода, если не задана str. CSV нуждается в этом подсказке, если планируется вывод данных, несовместимых с ASCII.
Добавление строк:
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 lib/csv.rb, line 1446
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 Возвращает строку, созданную путём генерации CSV из ary с указанными options.
Аргумент ary должен быть массивом.
Специальные опции:
-
Опция
:row_sepпо умолчанию"\n"> on Ruby 3.0 or later and <tt>$INPUT_RECORD_SEPARATOR($/) в противном случае:$INPUT_RECORD_SEPARATOR # => "\n"
-
Этот метод принимает дополнительную опцию
:encoding, которая устанавливает базовуюEncodingдля вывода. Этот метод попытается определить вашуEncodingиз первого не-nilполя вrow, если это возможно, но вам может потребоваться использовать этот параметр как резервный вариант.
Для других options, см. Опции для генерации.
Возвращает строку, сгенерированную из массива:
CSV.generate_line(['foo', '0']) # => "foo,0\n"
Выбрасывает исключение, если ary не является массивом:
# Raises NoMethodError (undefined method `find' for :foo:Symbol) CSV.generate_line(:foo)
# File lib/csv.rb, line 1501
def generate_lines(rows, **options)
self.generate(**options) do |csv|
rows.each do |row|
csv << row
end
end
end Возвращает строку, созданную путём генерации CSV, используя указанные options.
Аргумент rows должен быть массивом строк. Row — массив строк или CSV::Row.
Специальные опции:
-
Опция
:row_sepпо умолчанию"\n"в Ruby 3.0 и выше и$INPUT_RECORD_SEPARATOR($/) в противном случае:$INPUT_RECORD_SEPARATOR # => "\n"
-
Этот метод принимает дополнительную опцию
:encoding, которая устанавливает базовуюEncodingдля вывода. Этот метод попытается определить вашуEncodingиз первого не-nilполя вrow, если это возможно, но вам может потребоваться использовать этот параметр как резервный вариант.
Для других options, см. Опции для генерации.
Возвращает строку, сгенерированную из
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 lib/csv.rb, line 1006
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 Создаёт или получает кэшированные объекты CSV. Для аргументов и опций см. CSV.new.
Этот API не является безопасным для Ractor.
Без блока возвращает объект CSV.
Первый вызов instance создаёт и кэширует объект CSV:
s0 = 's0' csv0 = CSV.instance(s0) csv0.class # => CSV
Последующие вызовы instance с тем же string или io возвращают тот же кэшированный объект:
csv1 = CSV.instance(s0) csv1.class # => CSV csv1.equal?(csv0) # => true # Same CSV object
Последующий вызов instance с другим string или io создаёт и кэширует другой объект 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 lib/csv.rb, line 1905
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 Возвращает новый объект CSV, созданный с помощью string или io и указанными options.
-
Аргумент
stringдолжен быть объектом String; он будет помещён в новый объектStringIO, расположенный в начале. -
Аргумент
ioдолжен быть объектомIO, который:-
Открыт для чтения; по завершении, объект
IOбудет закрыт. -
Расположен в начале. Для расположения в конце, для добавления, используйте метод
CSV.generate. Для любого другого расположения, передайте предварительно настроенный объект StringIO.
-
-
Аргумент
options: См.:По соображениям производительности, опции не могут быть переопределены в объекте CSV, поэтому указанные здесь опции сохранятся.
В дополнение к методам экземпляра CSV делегированы несколько методов IO. См. Делегированные методы.
Создайте объект CSV из объекта String:
csv = CSV.new('foo,0')
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
Создайте объект CSV из объекта File:
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 lib/csv.rb, line 1581
def open(filename, mode="r", **options)
# wrap a File opened with the remaining +args+ with no newline
# decorator
file_opts = options.dup
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)}
begin
f = File.open(filename, 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
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 Возможные элементы опций:
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, если задан, должен быть путём к файлу. -
Аргумент
ioдолжен быть объектомIO, который:-
Открыт для чтения; по завершении, объект
IOбудет закрыт. -
Расположен в начале. Для расположения в конце, для добавления, используйте метод
CSV.generate. Для любого другого расположения, передайте предварительно настроенный объект StringIO.
-
-
Аргумент
mode, если задан, должен быть режимом файла. См. Режимы доступа. -
Аргументы
**optionsдолжны быть ключевыми опциями. См. Опции для генерации. -
Этот метод необязательно принимает дополнительную опцию
:encoding, которую вы можете использовать для указанияEncodingданных, считанных изpathилиioВам нужно предоставить это, если ваши данные не в кодировке, заданнойEncoding::default_external. Парсинг будет использовать это, чтобы определить, как разобрать данные. Вы можете предоставить вторуюEncoding, чтобы данные были преобразованы по мере их чтения. Например,encoding: 'UTF-32BE:UTF-8'
было бы читать данные
UTF-32BEиз файла, но перекодировать их вUTF-8перед парсингом.
Эти примеры предполагают предварительное выполнение:
string = "foo,0\nbar,1\nbaz,2\n" path = 't.csv' File.write(path, string)
Без блока возвращает новый объект 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:"\"">
Создайте объект 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:"\"">
При наличии блока вызывается блок с созданным объектом 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:"\"">
Используя открытый файл:
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:"\"">
Вызывает исключение, если аргумент не является объектом String или IO:
# Raises TypeError (no implicit conversion of Symbol into String) CSV.open(:foo)
# File lib/csv.rb, line 1732
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 Парсит string или io с использованием указанных options.
-
Аргумент
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)
Без блока возвращает массив массивов, сформированных из источника.
Разбор строки:
a_of_a = CSV.parse(string) a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
Разбор открытого файла:
a_of_a = File.open(path) do |file| CSV.parse(file) end a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
При наличии блока вызывается блок с каждым разобранным рядом:
Разбор строки:
CSV.parse(string) {|row| p row }
Вывод:
["foo", "0"] ["bar", "1"] ["baz", "2"]
Разбор открытого файла:
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, сформированный из источника.
Разбор строки:
csv_table = CSV.parse(string, headers: ['Name', 'Count']) csv_table # => #<CSV::Table mode:col_or_row row_count:5>
Разбор открытого файла:
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:
Разбор строки:
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.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)
# File lib/csv.rb, line 1805 def parse_line(line, **options) new(line, **options).each.first end
Возвращает данные, полученные при разборе первой строки string или io с использованием указанных options.
-
Аргумент
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 lib/csv.rb, line 1829
def read(path, **options)
open(path, **options) { |csv| csv.read }
end Открывает заданный source с заданными options (см. CSV.open), считывает источник (см. 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 lib/csv.rb, line 1837 def readlines(path, **options) read(path, **options) end
Псевдоним для CSV.read.
# File lib/csv.rb, line 1856
def table(path, **options)
default_options = {
headers: true,
converters: :numeric,
header_converters: :symbol,
}
options = default_options.merge(options)
read(path, **options)
end Вызывает CSV.read с source, options, и определёнными значениями по умолчанию:
-
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 lib/csv.rb, line 2372 def <<(row) writer << row self 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 lib/csv.rb, line 2261
def binmode?
if @io.respond_to?(:binmode?)
@io.binmode?
else
false
end
end # File lib/csv.rb, line 2009 def col_sep parser.column_separator end
Возвращает закодированный разделитель столбцов; используется для разбора и записи; см. {Параметр col_sep}:
CSV.new('').col_sep # => ","
# File lib/csv.rb, line 2443 def convert(name = nil, &converter) parser_fields_converter.add_converter(name, &converter) 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>]
Блок не обязательно должен возвращать строковый объект:
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 lib/csv.rb, line 2082
def converters
parser_fields_converter.map do |converter|
name = Converters.rassoc(converter)
name ? name.first : converter
end
end Возвращает массив, содержащий преобразователи поля; см. Преобразователи полей:
csv = CSV.new('')
csv.converters # => []
csv.convert(:integer)
csv.converters # => [:integer]
csv.convert(proc {|x| x.to_s })
csv.converters
Обратите внимание, что вам необходимо вызвать +Ractor.make_shareable(CSV::Converters)+ в основном ракторе Ractor, чтобы использовать этот метод.
# File lib/csv.rb, line 2554
def each(&block)
return to_enum(__method__) unless block_given?
begin
while true
yield(parser_enumerator.next)
end
rescue StopIteration
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 lib/csv.rb, line 2297
def eof?
return false if @eof_error
begin
parser_enumerator.peek
false
rescue MalformedCSVError => error
@eof_error = error
false
rescue StopIteration
true
end
end # File lib/csv.rb, line 2041 def field_size_limit parser.field_size_limit end
Возвращает ограничение размера поля; используется для разбора; см. {Параметр field_size_limit}:
CSV.new('').field_size_limit # => nil
Устарело начиная с 3.2.3. Используйте max_field_size вместо этого.
# File lib/csv.rb, line 2269 def flock(*args) raise NotImplementedError unless @io.respond_to?(:flock) @io.flock(*args) end
# File lib/csv.rb, line 2172 def force_quotes? @writer_options[:force_quotes] end
Возвращает значение, определяющее, должны ли все выходные поля быть заключены в кавычки; используется для генерации; см. {Параметр force_quotes}:
CSV.new('').force_quotes? # => false
# File lib/csv.rb, line 2509 def header_convert(name = nil, &converter) header_fields_converter.add_converter(name, &converter) end
Блок не обязательно должен возвращать строковый объект:
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 lib/csv.rb, line 2148
def header_converters
header_fields_converter.map do |converter|
name = HeaderConverters.rassoc(converter)
name ? name.first : converter
end
end Возвращает массив, содержащий преобразователи заголовков; используется для разбора; см. Преобразователи заголовков:
CSV.new('').header_converters # => []
Обратите внимание, что вам необходимо вызвать +Ractor.make_shareable(CSV::HeaderConverters)+ в основном ракторе Ractor, чтобы использовать этот метод.
# File lib/csv.rb, line 2631 def header_row? parser.header_row? 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 lib/csv.rb, line 2106
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 Возвращает значение, определяющее, используются ли заголовки; используется для разбора; см. {Параметр headers}:
CSV.new('').headers # => nil
# File lib/csv.rb, line 2690
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 Возвращает строку, отображающую определенные свойства 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 lib/csv.rb, line 2274 def ioctl(*args) raise NotImplementedError unless @io.respond_to?(:ioctl) @io.ioctl(*args) end
# File lib/csv.rb, line 2182 def liberal_parsing? parser.liberal_parsing? end
Возвращает значение, определяющее, как обрабатывать некорректный ввод; используется для парсинга; см. {Параметр liberal_parsing}:
CSV.new('').liberal_parsing? # => false
# File lib/csv.rb, line 2247 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, csv.line]
end
end
Вывод:
[1, "foo,0\n"] [2, "bar,1\n"] [3, "baz,2\n"]
# File lib/csv.rb, line 2223
def lineno
if @writer
@writer.lineno
else
parser.lineno
end
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 lib/csv.rb, line 2053 def max_field_size parser.max_field_size end
Возвращает ограничение размера поля; используется для парсинга; см. {Параметр max_field_size}:
CSV.new('').max_field_size # => nil
С версии 3.2.3.
# File lib/csv.rb, line 2279 def path @io.path if @io.respond_to?(:path) end
# File lib/csv.rb, line 2029 def quote_char parser.quote_character end
Возвращает закодированный символ кавычки; используется для парсинга и записи; см. {Параметр quote_char}:
CSV.new('').quote_char # => "\""
# File lib/csv.rb, line 2595
def read
rows = to_a
if parser.use_headers?
Table.new(rows, headers: parser.headers)
else
rows
end
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 lib/csv.rb, line 2124 def return_headers? parser.return_headers? end
Возвращает значение, определяющее, должны ли возвращаться заголовки; используется для парсинга; см. {Параметр return_headers}:
CSV.new('').return_headers? # => false
# File lib/csv.rb, line 2312 def rewind @parser = nil @parser_enumerator = nil @eof_error = nil @writer.rewind if @writer @io.rewind end
Перематывает подлежащий IO объект и сбрасывает счётчик строк CSV.
# File lib/csv.rb, line 2019 def row_sep parser.row_separator end
Возвращает закодированный разделитель строк; используется для парсинга и записи; см. {Параметр row_sep}:
CSV.new('').row_sep # => "\n"
# File lib/csv.rb, line 2668
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 Возвращает следующую строку данных как:
-
Массив, если заголовки не используются.
-
Объект
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 lib/csv.rb, line 2161 def skip_blanks? parser.skip_blanks? end
Возвращает значение, определяющее, должны ли пустые строки игнорироваться; используется для парсинга; см. {Параметр skip_blanks}:
CSV.new('').skip_blanks? # => false
# File lib/csv.rb, line 2063 def skip_lines parser.skip_lines end
Возвращает Regexp, используемый для идентификации комментариев; используется для парсинга; см. {Параметр skip_lines}:
CSV.new('').skip_lines # => nil
# File lib/csv.rb, line 2283 def stat(*args) raise NotImplementedError unless @io.respond_to?(:stat) @io.stat(*args) end
# File lib/csv.rb, line 2288 def to_i raise NotImplementedError unless @io.respond_to?(:to_i) @io.to_i end
# File lib/csv.rb, line 2293 def to_io @io.respond_to?(:to_io) ? @io.to_io : @io end
# File lib/csv.rb, line 2096 def unconverted_fields? parser.unconverted_fields? end
Возвращает значение, определяющее, должны ли быть доступны необработанные поля; используется для парсинга; см. {Параметр unconverted_fields}:
CSV.new('').unconverted_fields? # => nil
# File lib/csv.rb, line 2134 def write_headers? @writer_options[:write_headers] end
Возвращает значение, определяющее, должны ли записываться заголовки; используется для генерации; см. {Параметр write_headers}:
CSV.new('').write_headers? # => nil
Приватные методы экземпляра
# File lib/csv.rb, line 2822
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 lib/csv.rb, line 2804
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 lib/csv.rb, line 2792
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 lib/csv.rb, line 2817
def build_writer_fields_converter
build_fields_converter(@initial_write_converters,
@write_fields_converter_options)
end # File lib/csv.rb, line 2767
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 Обрабатывает fields с помощью @converters, или @header_converters если headers передано в качестве true, возвращая преобразованный набор полей. Любой конвертер, который изменяет поле на что-либо, кроме String, прерывает цепочку преобразований для этого поля. Это в первую очередь служит оптимизацией.
# File lib/csv.rb, line 2730
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 lib/csv.rb, line 2800 def header_fields_converter @header_fields_converter ||= build_header_fields_converter end
# File lib/csv.rb, line 2745
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 lib/csv.rb, line 2830 def parser @parser ||= Parser.new(@io, parser_options) end
# File lib/csv.rb, line 2839 def parser_enumerator @parser_enumerator ||= parser.parse end
# File lib/csv.rb, line 2788 def parser_fields_converter @parser_fields_converter ||= build_parser_fields_converter end
# File lib/csv.rb, line 2834
def parser_options
@parser_options.merge(header_fields_converter: header_fields_converter,
fields_converter: parser_fields_converter)
end # File lib/csv.rb, line 2778
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 Возвращает кодировку внутреннего объекта IO.
# File lib/csv.rb, line 2843 def writer @writer ||= Writer.new(@io, writer_options) end
# File lib/csv.rb, line 2813 def writer_fields_converter @writer_fields_converter ||= build_writer_fields_converter end
# File lib/csv.rb, line 2847
def writer_options
@writer_options.merge(header_fields_converter: header_fields_converter,
fields_converter: writer_fields_converter)
end
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.