Reputation: 6514
Is there a way to remove the BOM from a UTF-8 encoded file?
I know that all of my JSON files are encoded in UTF-8, but the data entry person who edited the JSON files saved it as UTF-8 with the BOM.
When I run my Ruby scripts to parse the JSON, it is failing with an error. I don't want to manually open 58+ JSON files and convert to UTF-8 without the BOM.
Upvotes: 39
Views: 26217
Reputation: 1334
Server side cleanup of utf-8 bom bytes that worked for me:
csv_text.delete("\uFEFF")
Upvotes: 0
Reputation: 33752
I just implemented this for the smarter_csv gem, and want to share this in case someone comes across this issue.
The problem is to remove byte-sequences independent of the string encoding. The solution is to use the methods bytes
and byteslice
from the String
class.
See: https://ruby-doc.org/core-3.1.1/String.html#method-i-bytes
UTF_8_BOM = %w[ef bb bf].freeze
def remove_bom(str)
str_as_hex = str.bytes.map{|x| x.to_s(16)}
return str.byteslice(3..-1) if str_as_hex[0..2] == UTF_8_BOM
str
end
Upvotes: 0
Reputation: 21
Server side cleanup of utf-8 bom bytes that worked for me:
csv_text.gsub!("\xEF\xBB\xBF".force_encoding(Encoding::BINARY), '')
Upvotes: 2
Reputation: 27885
With ruby >= 1.9.2 you can use the mode r:bom|utf-8
This should work (I haven't test it in combination with json):
json = nil #define the variable outside the block to keep the data
File.open('file.txt', "r:bom|utf-8"){|file|
json = JSON.parse(file.read)
}
It doesn't matter, if the BOM is available in the file or not.
Andrew remarked, that File#rewind
can't be used with BOM.
If you need a rewind-function you must remember the position and replace rewind
with pos=
:
#Prepare test file
File.open('file.txt', "w:utf-8"){|f|
f << "\xEF\xBB\xBF" #add BOM
f << 'some content'
}
#Read file and skip BOM if available
File.open('file.txt', "r:bom|utf-8"){|f|
pos =f.pos
p content = f.read #read and write file content
f.pos = pos #f.rewind goes to pos 0
p content = f.read #(re)read and write file content
}
Upvotes: 42
Reputation: 4657
the "bom|UTF-8" encoding works well if you only read the file once, but fails if you ever call File#rewind, as I was doing in my code. To address this, I did the following:
def ignore_bom
@file.ungetc if @file.pos==0 && @file.getc != "\xEF\xBB\xBF".force_encoding("UTF-8")
end
which seems to work well. Not sure if there are other similar type characters to look out for, but they could easily be built into this method that can be called any time you rewind or open.
Upvotes: 5
Reputation: 151
You can also specify encoding with the File.read
and CSV.read
methods, but you don't specify the read
mode.
File.read(path, :encoding => 'bom|utf-8')
CSV.read(path, :encoding => 'bom|utf-8')
Upvotes: 13
Reputation: 6514
So, the solution was to do a search and replace on the BOM via gsub! I forced the encoding of the string to UTF-8 and also forced the regex pattern to be encoded in UTF-8.
I was able to derive a solution by looking at http://self.d-struct.org/195/howto-remove-byte-order-mark-with-ruby-and-iconv and http://blog.grayproductions.net/articles/ruby_19s_string
def read_json_file(file_name, index)
content = ''
file = File.open("#{file_name}\\game.json", "r")
content = file.read.force_encoding("UTF-8")
content.gsub!("\xEF\xBB\xBF".force_encoding("UTF-8"), '')
json = JSON.parse(content)
print json
end
Upvotes: 30