Ruby操作CSV格式數(shù)據(jù)方法詳解
CSV格式的數(shù)據(jù)默認(rèn)是以逗號(hào)分隔各個(gè)字段的一條一條記錄,默認(rèn)用換行符分隔每一條記錄。此外,有的CSV有標(biāo)題行,有的沒(méi)有。還有其他一些格式, 它們都有默認(rèn)值,但都可以在讀、寫CSV數(shù)據(jù)時(shí)修改默認(rèn)設(shè)置。后文大多數(shù)時(shí)候故意忽略這些設(shè)置,因?yàn)榻^大多數(shù)讀寫操作都使用同樣的參數(shù)**options進(jìn)行格式設(shè)置。例如,在讀取csv文件中的數(shù)據(jù)時(shí)想要忽略標(biāo)題行,可以在參數(shù)中設(shè)置headers: true
可設(shè)置的項(xiàng)及其默認(rèn)值包括:
col_sep: ",", #=> 字段分隔符
row_sep: :auto, #=> 記錄分隔符
quote_char: '"', #=> 包圍字段的符號(hào)
field_size_limit: nil, #=> 限制字段的字符數(shù)量
converters: nil, #=>
unconverted_fields: nil,
headers: false, #=> 讀取時(shí)忽略標(biāo)題行,具體參考官方手冊(cè)
return_headers: false,
write_headers: nil,
header_converters: nil,
skip_blanks: false, #=> 忽略空行
force_quotes: false, #=> 設(shè)置為true時(shí),所有字段都將使用被包圍
skip_lines: nil, #=> 指定一個(gè)正則(str也會(huì)轉(zhuǎn)換為正則),
#=> 匹配的行將被當(dāng)作注釋行而忽略
liberal_parsing: false,
internal_encoding: nil,
external_encoding: nil,
encoding: nil,
nil_value: nil, #=> 使用此處設(shè)置的值替換所有nil字段
empty_value: "", #=> 使用此處設(shè)置的值替換所有空字符串字段
quote_empty: true, #=> 設(shè)置為false時(shí),空字符串字段將轉(zhuǎn)換為空字段
write_converters: nil,
write_nil_value: nil, #=> 將以此處的值替換nil字段寫入文件
write_empty_value: "",
strip: falseCSV類方法處理CSV數(shù)據(jù)
以CSV格式寫入文件
要向文件中寫入CSV格式的數(shù)據(jù):
require 'csv'
writer = CSV.open('/tmp/file.csv', 'w')
writer << ["junmajinlong", 29, 170, true]
writer << ["junma", 24, 176, false]
writer << ["jinlong", 25, 172, nil]
writer << ["majinlong", 23, 173, false]
writer.close寫入完成后,查看:
junmajinlong,29,170,true junma,24,176,false jinlong,25,172, majinlong,23,173,false
注意其中的nil對(duì)應(yīng)的寫入內(nèi)容為空。
可以直接在語(yǔ)句塊中寫入,這樣的話可以自動(dòng)關(guān)閉CSV.open()打開的IO流:
require 'csv'
CSV.open('/tmp/file.csv', 'w') do |writer|
writer << ["junmajinlong", 29, 170, true]
writer << ["junma", 24, 176, false]
writer << ["jinlong", 25, 172, nil]
writer << ["majinlong", 23, 173, false]
endCSV.open()打開的是一個(gè)封裝后的IO流對(duì)象,它除了可以使用CSV單獨(dú)為其提供的一些方法(比如這里的<<)外,還可以使用很多IO流對(duì)象的方法,比如seek()、tell()、flush()、eof?()、fsync()等等。
這里使用的<<方法是單獨(dú)為其提供的,它涉及兩個(gè)執(zhí)行過(guò)程:
- 將數(shù)組中各元素全部轉(zhuǎn)換成字符串類型并使用逗號(hào)連接
- 按行寫入到csv打開的文件中
轉(zhuǎn)換為CSV格式的字符串
如果只是想執(zhí)行第一個(gè)過(guò)程,即將數(shù)據(jù)轉(zhuǎn)換成CSV格式的字符串而不寫入,可使用類方法generate_line():
p CSV.generate_line ["junmajinlong", 29, 170, true] p CSV.generate_line ["jun ma", 24, 176, false] p CSV.generate_line ["jinlong", 25, 172, nil] p CSV.generate_line ["jin, long", 23, 173, false] =begin "junmajinlong,29,170,true\n" "jun ma,24,176,false\n" "jinlong,25,172,\n" "\"jin, long\",23,173,false\n" =end
從CSV格式的文件中讀數(shù)據(jù)
如果想要讀取CSV文件,可使用類方法read()或別名readlines():
pp CSV.readlines('/tmp/file.csv')
=begin
[["junmajinlong", "29", "170", "true"],
["junma", "24", "176", "false"],
["jinlong", "25", "172", nil],
["majinlong", "23", "173", "false"]]
=end注意:
- 讀取CSV文件內(nèi)容時(shí),每行保存為一個(gè)數(shù)組,每個(gè)字段是這個(gè)數(shù)組中的一個(gè)元素
- 讀取CSV文件內(nèi)容時(shí),除了不存在的字段轉(zhuǎn)換為nil外,其它所有的數(shù)據(jù)都轉(zhuǎn)換成了字符串類型。所以有時(shí)候可能需要去轉(zhuǎn)換讀取時(shí)的數(shù)據(jù)類型。關(guān)于類型轉(zhuǎn)換,見(jiàn)后文
如果要按行讀取CSV文件的內(nèi)容,使用類方法foreach():
CSV.foreach('/tmp/file.csv') do |row|
p row
end
=begin
["junmajinlong", "29", "170", "true"]
["junma", "24", "176", "false"]
["jinlong", "25", "172", nil]
["majinlong", "23", "173", "false"]
=end從CSV格式的字符串中讀數(shù)據(jù)
如果想要從字符串中讀取CSV格式的數(shù)據(jù),使用parse()和parse_line(),分別用于解析多行字符串和解析單行字符串(超出一行的自動(dòng)被忽略)。
- parse()不指定語(yǔ)句塊時(shí),返回包含解析每一行得到的數(shù)組,即一個(gè)數(shù)組的數(shù)組,它是一個(gè)csv table類型,有很多自己的方法
- 指定語(yǔ)句塊時(shí),每一行對(duì)應(yīng)的數(shù)組傳遞給語(yǔ)句塊控制變量
str1=<<-eof
junmajinlong,29,170,true
jun ma,24,176,false
jinlong,25,172,
"jin, long",23,173,false
eof
# 不指定語(yǔ)句塊時(shí),parse返回?cái)?shù)組
pp CSV.parse str1
=begin
[["junmajinlong", "29", "170", "true"],
["jun ma", "24", "176", "false"],
["jinlong", "25", "172", nil],
["jin, long", "23", "173", "false"]]
=end
# 指定語(yǔ)句塊時(shí),parse將每行對(duì)應(yīng)的數(shù)組傳遞給語(yǔ)句塊
CSV.parse(str1) {|row| p row}
=begin
["junmajinlong", "29", "170", "true"]
["jun ma", "24", "176", "false"]
["jinlong", "25", "172", nil]
["jin, long", "23", "173", "false"]
=end
str2="junmajinlong,29,170,true"
p CSV.parse_line str2
["junmajinlong", "29", "170", "true"]CSV實(shí)例方法處理CSV數(shù)據(jù)
CSV.new()、CSV.open()可以創(chuàng)建csv對(duì)象(即一行一行csv格式的數(shù)據(jù))CSV.generate()可將字符串轉(zhuǎn)換成csv對(duì)象并將該對(duì)象傳遞給語(yǔ)句塊<<、puts()或add_row()可向CSV目標(biāo)中(字符串格式的CSV或CSV IO流)寫入行,它們是別名關(guān)系gets()、shift()、readline()可從csv對(duì)象中讀取一行數(shù)據(jù)read()、readlines()可以讀取csv對(duì)象中的所有數(shù)據(jù)each()可以從csv對(duì)象中迭代每一行eof()或eof?()可以判斷是否讀完所有數(shù)據(jù)rewind()可以重置當(dāng)前csv對(duì)象的偏移指針line()可以獲取最近一次讀取的一行數(shù)據(jù)lineno()可以獲取當(dāng)前已讀取的行數(shù)path()可以獲取當(dāng)前讀取的csv文件名
CSV table
CSV.parse()、CSV.read()、CSV.table()等方法返回的都是數(shù)組的數(shù)組(二維數(shù)組),它們是CSV Table。
CSV table按照表的方式來(lái)處理csv數(shù)據(jù),比如關(guān)注于行、關(guān)注于字段的一些操作可以采用csv table相關(guān)的方法來(lái)處理。
# 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字段類型轉(zhuǎn)換
讀取CSV數(shù)據(jù)時(shí),所有的數(shù)據(jù)都會(huì)轉(zhuǎn)換為字符串格式。
# Without any converters:
CSV.parse('Bob,2018-03-01,100')
#=> [["Bob", "2018-03-01", "100"]]可以在迭代每一行的語(yǔ)句塊中對(duì)字段做必要的類型轉(zhuǎn)換。
但如果類型轉(zhuǎn)換方式比較簡(jiǎn)單,可以在讀取數(shù)據(jù)時(shí)指定converters屬性進(jìn)行轉(zhuǎn)換。該屬性的值要么是CSV的內(nèi)置類型符號(hào),要么是符號(hào)數(shù)組,要么是一個(gè)lambda表達(dá)式。有如下內(nèi)置類型:
Integer Float Numeric (Float + Integer) Date DateTime All
當(dāng)指定了類型轉(zhuǎn)換后,每個(gè)字段將針對(duì)converters的值嘗試做轉(zhuǎn)換,轉(zhuǎn)換失敗則保留字段的值不變,所以如果通過(guò)lambda自定義類型轉(zhuǎn)換時(shí)也一定要保證這一點(diǎn)。
CSV.parse("1,2,3,4,5", converters: :numeric)
#=> [[1, 2, 3, 4, 5]]
# With built-in converters:
ct = CSV.parse('Bob,2018-03-01,100', converters: %i[numeric date])
#=> [["Bob", #<Date: 2018-03-01>, 100]]
ct.first[1] + 1 # 日期對(duì)象,加1天
#=> #<Date: 2018-03-02 ((2458180j,0s,0n),+0s,2299161j)>
# With custom converters:
CSV.parse('Bob,2018-03-01,100', converters: [->(v) { Time.parse(v) rescue v }])
#=> [["Bob", 2018-03-01 00:00:00 +0200, "100"]]更多關(guān)于Ruby操作CSV格式數(shù)據(jù)方法請(qǐng)查看下面的相關(guān)鏈接
相關(guān)文章
借助RubyGnome2庫(kù)進(jìn)行GTK下的Ruby GUI編程的基本方法
這篇文章主要介紹了借助RubyGnome2庫(kù)進(jìn)行GTK下的Ruby GUI編程的基本方法,介紹了基本的UI和事件響應(yīng)的相關(guān)實(shí)現(xiàn),需要的朋友可以參考下2015-12-12
Ruby和Shell腳本實(shí)現(xiàn)判斷成績(jī)及格功能
這篇文章主要介紹了Ruby和Shell腳本實(shí)現(xiàn)判斷成績(jī)及格功能,使用Ruby實(shí)現(xiàn)這個(gè)功能非常簡(jiǎn)潔優(yōu)雅,而Shell的實(shí)現(xiàn)就比較傳統(tǒng)了,需要的朋友可以參考下2015-01-01
Ruby中的反射(Reflection)應(yīng)用實(shí)例
這篇文章主要介紹了Ruby中的反射(Reflection)應(yīng)用實(shí)例,實(shí)現(xiàn)通過(guò)一個(gè)類名字符串構(gòu)造一個(gè)類對(duì)象和訪問(wèn)成員變量和私有方法 ,需要的朋友可以參考下2014-06-06
Ruby實(shí)現(xiàn)的最短編輯距離計(jì)算方法
這篇文章主要介紹了Ruby實(shí)現(xiàn)的最短編輯距離計(jì)算方法,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-05-05
ruby迭代map的簡(jiǎn)潔寫法實(shí)現(xiàn)原理分析
這篇文章主要介紹了ruby迭代map的簡(jiǎn)潔寫法實(shí)現(xiàn)原理分析,本文著重對(duì)簡(jiǎn)潔寫法的原理進(jìn)行解析,需要的朋友可以參考下2014-11-11
Ruby中使用連續(xù)體Continuation實(shí)現(xiàn)生成器
這篇文章主要介紹了Ruby中使用連續(xù)體Continuation實(shí)現(xiàn)生成器,本文先是介紹了生成器的概念,然后給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-01-01

