2009-02-12 30 views
29

Tôi đang tiêu thụ một nguồn cấp dữ liệu gần đây đã thêm một tiêu đề Unicode BOM (U + FEFF), và nhiệm vụ rake của tôi bây giờ là điều sai lầm bởi nó.Làm thế nào để tránh vấp ngã trên UTF-8 BOM khi đọc các tập tin

Tôi có thể bỏ qua 3 byte đầu tiên với file.gets[3..-1] nhưng có cách nào thanh lịch hơn để đọc tệp trong Ruby có thể xử lý chính xác, cho dù BOM có hiện diện hay không?

+2

Thats Unicode BOM không phải là UTF-8. – AnthonyWJones

+0

Cảm ơn, tôi vừa mới nhận ra điều đó. Nó thực sự là 3 byte, không phải một ... Tôi đã chỉnh sửa câu hỏi để nói nhiều. –

Trả lời

48

Với ruby ​​1.9.2 bạn có thể sử dụng chế độ r:bom|utf-8

text_without_bom = nil #define the variable outside the block to keep the data 
File.open('file.txt', "r:bom|utf-8"){|file| 
    text_without_bom = file.read 
} 

hoặc

text_without_bom = File.read('file.txt', encoding: 'bom|utf-8') 

hoặc

text_without_bom = File.read('file.txt', mode: 'r:bom|utf-8') 

Nó không quan trọng, nếu BOM có sẵn trong tập tin hay không.


Bạn cũng có thể sử dụng tùy chọn mã hóa với các lệnh khác:

text_without_bom = File.readlines(@filename, "r:utf-8") 

(Bạn sẽ có được một mảng với tất cả các dòng).

Hoặc với CSV:

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

Có cách nào để làm điều này với các tệp CSV sử dụng thư viện CSV được tích hợp vào ruby ​​không? Tôi đã cố gắng truyền ': encoding =>" r: bom | utf-8 "' vào foreach của CSV nhưng nó vẫn đọc BOM như thể nó là một phần của cột đầu tiên của tiêu đề. – Aaron

+2

Tôi nghĩ điều đó là có thể. Với 'CVS.read (tên tệp,: encoding => 'utf-8')' bạn có thể đặt mã hóa bằng CSV (hoặc là nó là 'CSV.load'?). Tôi nghĩ điều này cũng có thể xảy ra với lô-gic-bom: ': encoding => 'bom | utf-8')'. Tôi không thể kiểm tra nó thực sự bản thân mình - xin lỗi. – knut

+0

Làm như sau cho tôi: 'file = File.open (@filename, 'r: bom | utf-8')' 'csv = CSV.new (file, faster_csv_options)' 'csv.each do | row | ' ' ... ' ' file.close' – Aaron

10

Tôi sẽ không bỏ qua một cách mù quáng ba byte đầu tiên; nếu nhà sản xuất dừng lại, hãy thêm lại BOM? Những gì bạn nên làm là kiểm tra một vài byte đầu tiên và nếu chúng là 0xEF 0xBB 0xBF, hãy bỏ qua chúng. Đó là dạng ký tự BOM (U + FEFF) có trong UTF-8; Tôi thích đối phó với nó trước khi cố gắng giải mã luồng vì việc xử lý BOM quá mâu thuẫn với một ngôn ngữ/công cụ/khung công tác cho ngôn ngữ kế tiếp.

Thực tế, đó là cách bạn đang giả định để giải quyết với BOM. Nếu một tệp đã được phân phát dưới dạng UTF-16, bạn phải kiểm tra hai byte đầu tiên trước khi bạn bắt đầu giải mã để bạn biết có nên đọc nó như là một người lớn hay nhỏ. Tất nhiên, UTF-8 BOM không có gì để làm với thứ tự byte, nó chỉ có để cho bạn biết rằng mã hóa là UTF-8, trong trường hợp bạn chưa biết điều đó.

0

tôi không "tin tưởng" một số tập tin được mã hóa theo UTF-8 khi một BOM của 0xEF 0xBB 0xBF là hiện tại, bạn có thể thất bại. Thông thường khi phát hiện UTF-8 BOM, nó thực sự phải là một tập tin mã hóa UTF-8 tất nhiên. Nhưng, ví dụ, nếu ai đó vừa thêm UTF-8 BOM vào một tập tin ISO, bạn sẽ không mã hóa tập tin như vậy xấu nếu có các byte trong nó ở trên 0x0F. Bạn có thể tin tưởng tệp nếu bạn chỉ có byte tối đa 0x0F bên trong, bởi vì trong trường hợp này đó là tệp ASCII tương thích UTF-8 và đồng thời đó là tệp UTF-8 hợp lệ.

Nếu không chỉ có byte < = 0x0F trong tệp (sau BOM), để đảm bảo mã UTF-8 được mã hóa đúng, bạn sẽ phải kiểm tra chuỗi hợp lệ và - ngay cả khi tất cả các chuỗi đều hợp lệ - kiểm tra xem liệu mỗi điểm mã từ một chuỗi có sử dụng chuỗi ngắn nhất có thể không và kiểm tra xem nếu không có điểm mã nào khớp với giá trị thay thế cao hay thấp. Ngoài ra kiểm tra xem các byte tối đa của một chuỗi không nhiều hơn 4 và điểm mã cao nhất là 0x10FFFF. Giới hạn codepoint cao nhất cũng là các bit trọng tải của startbyte không cao hơn 0x4 và tải trọng của byte đầu tiên sau không cao hơn 0xF. Nếu tất cả các kiểm tra đã đề cập đều thành công, thì UTF-8 BOM của bạn sẽ nói lên sự thật.

Các vấn đề liên quan