BOM và ứng dụng export, import csv trong Rails

Giới thiệu

Đã bao giờ bạn thử mở file export có định dạng .csv trên window chưa? Mặc định khi nhấn double-click vào file .csv, window sẽ tự động mở file này trên excel. Nếu trong file có sử dụng tiếng Việt, tiếng Nhật hoặc ngôn ngữ có sử dụng bảng mã Unicode, thì khi mở file vừa được export với excel, font chữ sẽ bị lỗi do CSV không hỗ trợ định dạng Unicode. Có nhiều cách để khắc phục tình trạng này trên window như sử dụng nodepad để chỉnh sửa file csv.... Trong bài viết này, mình giới thiệu cách khắc phục lỗi font chữ này khi export csv file trong ứng dụng Rails sử dụng BOM.

BOM là gì?

Bom viết tắt của Byte Order Mark, được sử dụng để biểu thị rằng một tập tin văn bản sử dụng mã hoá Unicode. Vì Unicode nói chung hỗ trợ UTF-32BE, UTF-32LE, UTF-16BE, UTF-16LE và UTF-8, nên để có thể biết được text file trong Microsoft Windows đang chứa Unicode encoding kiểu nào, ở đầu mỗi Unicode text file có 2 hay 3 bytes gọi là BOM như sau:

Khi viết Unicode ra text file trong MSWindows, nếu dùng UTF-8 bạn chỉ cần viết ở đầu file BOM EF BB BF. Các bytes kế tiếp cứ theo đúng hoặc UTF-8 characters thì dùng 2 hay 3 bytes hoặc ANSI characters thì dùng 1 byte. Nếu dùng UTF-16LE (còn gọi gọn là Unicode trong ngôn ngữ Microsoft) thì viết BOM FF FE. Kế đó mỗi character phải viết ra 2 bytes dù là Unicode hay không. Nếu character chỉ cần 1 byte, kể cả các characters như Carriage Return (&H0D) và LineFeed (&H0A), thì viết thêm byte thứ nhì là 00 (Null byte), ví dụ:

Câu hỏi đặt ra là Text File với encoding UTF-16LE hay UTF-8 chiếm nhiều chỗ hơn. Câu trả lời là tùy theo trường hợp. Ðối với UTF-16 thì mỗi character cần 2 bytes, kể cả ANSI characters. Ðối với UTF-8 thì nếu là Unicode character thì cần 2 hay 3 bytes, còn ANSI character thì chỉ cần 1 byte.

Export CSV

Bây giờ chúng ta sẽ thử export CSV với encoding UTF-8 với BOM và UTF-8 không có BOM xem sự khác nhau của kết quả khi mở trên excel nhé. Đầu tiên là export CSV không sử dụng BOM:

require 'csv'
def csv_export
  File.open(path + 'test_doruby.csv', "w:UTF-8") do |f|
    csv_data = CSV.generate do |csv|
      csv << csv_text
    end
    f.write(csv_data)
  end
end

def csv_text
  [
    "これは",
    "UTF-8の",
    "BOMなしの",
    "testです"
  ]

Sau khi thực hiện method này, file "test_doruby.csv" được tạo ra với encoding UTF-8, giờ hãy mở nó trong Excel:

Kết quả bạn có thể nhìn thấy, những chữ tiếng Nhật đã bị thay thế bởi những chữ Kanji với rất nhiều nét.

Giờ sử dụng code bên trên nhưng export CSV sử dụng với BOM:

require 'csv'
def csv_export
  File.open(path + 'test_doruby_bom.csv', "w:UTF-8") do |f|
    bom = "\uFEFF"
    csv_data = CSV.generate(bom) do |csv|
      csv << csv_text
    end
    f.write(csv_data)
  end
end

def csv_text
  [
    "これは",
    "UTF-8の",
    "BOMありの",
    "testです"
  ]

bom = "\uFEFF"

Bạn có thể khai báo BOM như một thông số của hàm CSV.generate Và khi mở file có sử dụng BOM này trong excel:

Import CSV with BOM

Bạn đã nhìn thấy tác dụng của việc sử dụng BOM trong file csv khi export, vậy nếu import một file csv có bao gồm BOM thì sao? Bạn đơn giản chỉ cần thêm mode: "r:bom|utf-8" khi đọc file mà không cần quan tâm file có chứa BOM hay không:

csv = File.read(file.path, mode: "r:bom|utf-8")

hoặc

require 'csv'
CSV.open(@filename, 'r:bom|utf-8'){|csv|
  csv.each{ |row| p row }
}

Tổng kết

Trên đây mình vừa giới thiệu về BOM và tác dụng của nó khi export csv file, hy vọng sẽ giúp ích được cho mọi người. Cám ơn vì đã đọc bài viết của mình và rất mong được góp ý thêm!!!


All Rights Reserved