2009-02-12 6 views
29

Ich verwende einen Datenfeed, der kürzlich einen Unicode-BOM-Header (U + FEFF) hinzugefügt hat, und meine Rake-Aufgabe ist nun durcheinander geraten.Vermeiden von Stolpern über UTF-8-Stückliste beim Lesen von Dateien

Ich kann die ersten 3 Bytes mit file.gets[3..-1] überspringen, aber gibt es eine elegantere Art, Dateien in Ruby zu lesen, die dies richtig behandeln können, ob eine Stückliste vorhanden ist oder nicht?

+2

Das ist eine Unicode-Stückliste nicht eine UTF-8. – AnthonyWJones

+0

Danke, das habe ich gerade gemerkt. Es ist eigentlich 3 Bytes, nicht eins ... Ich habe die Frage bearbeitet, um so viel zu sagen. –

Antwort

48

mit Ruby 1.9.2 Sie den Modus verwenden können 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 
} 

oder

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

oder

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

Es spielt keine Rolle, wenn die Stückliste ist verfügbar in die Datei oder nicht.


Sie können auch die Codierungsoption mit anderen Befehlen verwenden:

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

(Sie erhalten ein Array mit allen Linien).

Oder mit CSV:

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

Gibt es eine Möglichkeit, dies mit CSV-Dateien mithilfe der in Ruby integrierten CSV-Bibliothek zu tun? Ich habe versucht, ': encoding =>" r: bom | utf-8 "' an CSVs Foreach zu übergeben, liest aber immer noch die Stückliste, als ob sie Teil der ersten Spalte des Headers wäre. – Aaron

+2

Ich denke, es ist möglich. Mit 'CVS.read (Dateiname,: encoding => 'utf-8')' können Sie die Kodierung mit CSV einstellen (oder ist es 'CSV.load'?). Ich denke, das wird auch mit der bom-Logik möglich sein: ': encoding => 'bom | utf-8')'. Ich kann es selbst nicht testen - tut mir leid. – knut

+0

Folgendes funktionierte für mich: 'file = File.open (@dateiname, 'r: bom | utf-8')' 'csv = CSV.new (datei, faster_csv_options)' 'csv.each do | row | ' ' ... ' ' file.close' – Aaron

10

Ich würde nicht blind die ersten drei Bytes überspringen; Was ist, wenn der Hersteller stoppt Hinzufügen der Stückliste wieder? Was Sie tun sollten, ist untersuchen die ersten paar Bytes, und wenn sie 0xEF 0xBB 0xBF sind, ignorieren Sie sie. Dies ist die Form, die das Stücklistenzeichen (U + FEFF) in UTF-8 einnimmt; Ich ziehe es vor, mich damit zu befassen, bevor ich versuche, den Stream zu decodieren, weil die BOM-Behandlung von einer Sprache/einem Tool/Framework zu der nächsten so inkonsistent ist.

In der Tat, das ist, wie Sie angenommen mit einer Stückliste umgehen. Wenn eine Datei als UTF-16 bereitgestellt wurde, müssen Sie die ersten zwei Bytes untersuchen, bevor Sie mit der Decodierung beginnen, damit Sie wissen, ob Sie sie als Big-Endian oder Little-Endian lesen sollen. Natürlich hat die UTF-8-Stückliste nichts mit der Byte-Reihenfolge zu tun, sondern ist nur dazu da, um Sie wissen zu lassen, dass die Kodierung UTF-8 ist, falls Sie das nicht schon wussten.

0

Ich würde nicht „Vertrauen“ einige Datei als UTF-8 codiert werden, wenn eine Stückliste von 0xEF 0xBB 0xBF vorhanden ist, könnten Sie scheitern. Normalerweise sollte es beim Erkennen der UTF-8-Stückliste eine UTF-8-codierte Datei sein. Wenn jedoch beispielsweise jemand die UTF-8-Stückliste zu einer ISO-Datei hinzugefügt hat, können Sie eine solche Datei nicht so schlecht codieren, wenn sich darin Bytes befinden, die über 0x0F liegen. Sie können der Datei vertrauen, wenn Sie nur Bytes bis zu 0x0F haben, da es sich in diesem Fall um eine UTF-8-kompatible ASCII-Datei handelt und es sich gleichzeitig um eine gültige UTF-8-Datei handelt.

Wenn nicht nur Bytes < = 0x0F innerhalb der Datei vorhanden sind (nach der BOM), um sicher zu sein, dass es richtig UTF-8 codiert ist, müssen Sie nach gültigen Sequenzen suchen und - auch wenn alle Sequenzen gültig sind - Überprüfen Sie auch, ob jeder Codepunkt einer Sequenz die kürzest mögliche Sequenz verwendet, und prüfen Sie auch, ob kein Codepunkt vorhanden ist, der einem High- oder Low-Surrogat entspricht. Überprüfen Sie auch, ob die maximale Anzahl der Bytes einer Sequenz nicht mehr als 4 und der höchste Codepunkt 0x10FFFF ist. Der höchste Codepoint begrenzt auch die Payload-Bits des Startbytes auf nicht mehr als 0x4 und die Nutzdaten des ersten folgenden Bytes nicht höher als 0xF. Wenn alle genannten Prüfungen erfolgreich bestanden wurden, sagt Ihre UTF-8-Stückliste die Wahrheit.