web-dev-qa-db-ja.com

ファイルを読み取るときにUTF-8BOMにつまずかないようにする方法

最近UnicodeBOMヘッダー(U + FEFF)を追加したデータフィードを使用していますが、私のrakeタスクは今では混乱しています。

file.gets[3..-1]で最初の3バイトをスキップできますが、BOMが存在するかどうかに関係なく、これを正しく処理できるRubyのファイルを読み取るより洗練された方法はありますか?

43
Andrew Vit

Ruby 1.9.2では、モード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
}

または

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

または

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

BOMがファイルで使用可能かどうかは関係ありません。


他のコマンドでエンコードオプションを使用することもできます。

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

(すべての行を含む配列を取得します)。

またはCSVの場合:

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

最初の3バイトをやみくもにスキップすることはありません。プロデューサーが停止 BOMを再度追加した場合はどうなりますか?あなたがすべきことはexamine最初の数バイトであり、それらが0xEF 0xBB 0xBFである場合、それらを無視します。これは、BOM文字(U + FEFF)がUTF-8で取る形式です。 BOMの処理は、ある言語/ツール/フレームワーク間で非常に一貫性がないため、ストリームをデコードする前に処理することを好みます。

実際、BOMを処理する方法は想定です。ファイルがUTF-16として提供されている場合は、デコードを開始する前に最初の2バイトを調べて、ファイルをビッグエンディアンとリトルエンディアンのどちらとして読み取るかを判断する必要があります。もちろん、UTF-8 BOMはバイトオーダーとは関係ありません。まだ知らなかった場合に備えて、エンコーディングがUTF-8であることを通知するためだけにあります。

11
Alan Moore

0xEF 0xBB 0xBFのBOMが存在する場合、UTF-8としてエンコードされるファイルを「信頼」しません。失敗する可能性があります。通常、UTF-8 BOMを検出する場合、もちろん実際にはUTF-8でエンコードされたファイルである必要があります。ただし、たとえば誰かがUTF-8 BOMをISOファイルに追加したばかりの場合、0x0Fを超えるバイトが含まれていると、そのようなファイルのエンコードに失敗します。内部に0x0Fまでのバイトしかない場合は、ファイルを信頼できます。この場合、ファイルはUTF-8互換のASCIIファイルであると同時に、有効なUTF-8ファイルであるためです。

ファイル内(BOMの後)にバイト<= 0x0Fだけがない場合は、UTF-8で正しくエンコードされていることを確認するために、有効なシーケンスを確認する必要があります。また、すべてのシーケンスが有効な場合でも、それぞれが有効かどうかも確認する必要があります。シーケンスからのコードポイントは、可能な限り最短のシーケンスを使用し、高サロゲートまたは低サロゲートに一致するコードポイントがないかどうかも確認します。また、シーケンスの最大バイトが4以下で、最大コードポイントが0x10FFFFであるかどうかを確認します。最高のコードポイントは、スタートバイトのペイロードビットも0x4以下に制限し、次の最初のバイトのペイロードは0xF以下に制限します。上記のすべてのチェックに合格すると、UTF-8BOMが真実を伝えます。

0
brighty