CSV là viết tắt của "Comma-Separated Values".
Đây là định dạng dữ liệu phổ biến gồm các hàng với giá trị cách nhau bởi dấu phẩy. Dùng để export và import dữ liệu.
Ví dụ:
Bạn có thể export contact Gmail thành file CSV, và cũng import dùng cùng format.
File CSV trông như sau:
id,name 1,chocolate 2,bacon 3,apple 4,banana 5,almonds
Giờ bạn sẽ học dùng thư viện CSV Ruby để đọc và ghi file CSV.
Parse CSV với Ruby
Ruby có sẵn thư viện CSV built-in.
Bạn đọc file trực tiếp:
require 'csv'
CSV.read("favorite_foods.csv")
Hoặc bạn có thể parse string chứa dữ liệu CSV:
require 'csv'
CSV.parse("1,chocolate\n2,bacon\n3,apple")
Kết quả?
Bạn nhận mảng 2 chiều mà mỗi entry là một hàng trong bảng.
Trông như sau:
[ ["id", "name"], ["1", "chocolate"], ["2", "bacon"], ["3", "apple"], ["4", "banana"], ["5", "almonds"] ]
Bạn có thể dùng index mảng như data[1][1] để làm việc với dữ liệu.
Nhưng có cách tốt hơn!
Tùy chọn CSV
Nếu file có header, bạn bảo CSV parser dùng chúng.
Như sau:
table = CSV.parse(File.read("cats.csv"), headers: true)
Thay vì mảng đa chiều, bạn nhận object CSV Table.
Mô tả:
"
CSV::Tablelà cấu trúc dữ liệu 2 chiều đại diện cho tài liệu CSV. Table cho phép làm việc với dữ liệu theo hàng hoặc cột, thao tác dữ liệu, và convert kết quả về CSV."
Cho một table này, bạn lấy dữ liệu cần từ bất kỳ hàng nào.
Ví dụ:
table[0]["id"] # "1" table[0]["name"] # "chocolate"
Ở đây 0 là hàng đầu, id và name là tên cột.
Có hai chế độ table:
- by_col
- by_row
Thay đổi chế độ table (row mặc định) cho phép xem dữ liệu từ góc độ khác.
Ví dụ:
table.by_col[0] # ["1", "2", "3", "4", "5"] table.by_col[1] # ["chocolate", "bacon", "apple", "banana", "almonds"]
Ở đây 0 là cột đầu, 1 là cột hai.
Hai phương thức này trả về bản sao của table.
Nếu muốn thay đổi table gốc, dùng by_col! và by_row!.
Này tiết kiệm bộ nhớ hơn vì không tạo bản sao.
Cách dùng CSV Converters
Bạn có thể nhận ra cột id là mảng string.
Điều gì nếu cần Integer?
Bạn có thể gọi to_i trên mỗi string…
Nhưng có shortcut!
Thư viện CSV Ruby implement thứ gọi là converters.
Converter tự động transform giá trị cho bạn.
Ví dụ:
CSV.parse("1,2,3,4,5") # [["1", "2", "3", "4", "5"]]
CSV.parse("1,2,3,4,5", converters: :numeric) # [[1, 2, 3, 4, 5]]
Có 6 built-in converters:
- Integer
- Float
- Numeric (Float + Integer)
- Date
- DateTime
- All
Nhưng bạn cũng tự tạo custom converter.
Cách làm:
CSV::Converters[:symbol] = ->(value) { value.to_sym rescue value }
Bạn có thể dùng converter mới như sau:
CSV.parse("a,b,c", headers: false, converters: :symbol) # [[:a, :b, :c]]
Tạo file CSV mới
Ngoài parse và đọc CSV theo nhiều cách, bạn cũng tạo CSV từ đầu.
Cách dễ:
cats = [
[:blue, 1],
[:white, 2],
[:black_and_white, 3]
]
cats.map { |c| c.join(",") }.join("\n")
Bạn cũng dùng phương thức generate:
CSV.generate do |csv| csv << [:blue, 1] csv << [:white, 2] csv << [:black_and_white, 3] end
Này chuẩn bị data đúng format.
Nếu muốn ghi file, dùng File.write("cats.csv", data), hoặc thay generate bằng open với tên file và write mode.
Như sau:
CSV.open("cats.csv", "w") do |csv|
csv << [:white, 2]
end
Giờ bạn có file CSV mới!
CSV Gems và Performance
Thư viện built-in ổn và làm được việc.
Nhưng bạn cũng tìm thấy vài gem parse CSV với tính năng khác.
Ví dụ, gem smarter_csv convert CSV data thành array of hashes.
Ví dụ:
require 'smarter_csv'
IntegerConverter = Object.new
def IntegerConverter.convert(value)
Integer(value)
end
SmarterCSV.process('testing.csv', value_converters: { id: IntegerConverter })
# [{:id=>1, :name=>"a"}, {:id=>2, :name=>"b"}, {:id=>3, :name=>"c"}]
So sánh hiệu năng:
Comparison: CSV: 112.9 i/s Smarter CSV: 21.7 i/s - 5.21x slower Tabular: 17.3 i/s - 6.52x slower
Tóm tắt
Bạn đã học đọc và ghi file CSV trong Ruby! Cũng học về converters và gem thay thế xử lý CSV.
Nếu muốn xử lý file CSV lớn (> 10MB), bạn có thể dùng CSV.foreach(file_name) với block. Này đọc từng hàng một và tiết kiệm bộ nhớ rất nhiều.
Hãy chia sẻ bài viết để nhiều người tìm thấy!