CSV BOM Ruby | Кодировка UTF-8 для StringIO

Рубин 2.6.3.

Я пытался разобрать StringIO объект в CSV экземпляр с bom|utf-8 кодирование, так что символ спецификации (нежелательный) удаляется, а содержимое кодируется в UTF-8:

require 'csv'

CSV_READ_OPTIONS = { headers: true, encoding: 'bom|utf-8' }.freeze

content = StringIO.new("\xEF\xBB\xBFid\n123")
first_row = CSV.parse(content, CSV_READ_OPTIONS).first

first_row.headers.first.include?("\xEF\xBB\xBF")     # This returns true

Видимо bom|utf-8 кодировка не работает для StringIO объекты, но я обнаружил, что это работает для файлов, например:

require 'csv'

CSV_READ_OPTIONS = { headers: true, encoding: 'bom|utf-8' }.freeze

# File content is: "\xEF\xBB\xBFid\n12"
first_row = CSV.read('bom_content.csv', CSV_READ_OPTIONS).first

first_row.headers.first.include?("\xEF\xBB\xBF")     # This returns false

Учитывая, что мне нужно работать с StringIO прямо, почему CSV игнорирует bom|utf-8кодировка? Есть ли способ удалить символ спецификации изStringIO пример?

Спасибо!

3 ответа

Ruby 2.7 добавил set_encoding_by_bom способ IO. Этот метод использует метку порядка байтов и устанавливает кодировку.

require 'csv'
require 'stringio'

CSV_READ_OPTIONS = { headers: true }.freeze

content = StringIO.new("\xEF\xBB\xBFid\n123")
content.set_encoding_by_bom

first_row = CSV.parse(content, **CSV_READ_OPTIONS).first
first_row.headers.first.include?("\xEF\xBB\xBF")
#=> false

Я обнаружил, что принудительное кодирование в utf8 на StringIO string и удаление спецификации для создания нового StringIO сработало:

require 'csv'
CSV_READ_OPTIONS = { headers: true}.freeze
content = StringIO.new("\xEF\xBB\xBFid\n123")
csv_file = StringIO.new(content.string.force_encoding('utf-8').sub("\xEF\xBB\xBF", ''))
first_row = CSV.parse(csv_file, CSV_READ_OPTIONS).first

first_row.headers.first.include?("\xEF\xBB\xBF") # => false  

В encodingвариант больше не нужен. Возможно, это не лучший вариант с точки зрения памяти, но он работает.

Руби не любит спецификации. Он обрабатывает их только при чтении файла, больше нигде, и даже тогда он только читает их, чтобы от них можно было избавиться. Если вам нужна спецификация для вашей строки или спецификация при записи файла, вы должны обработать ее вручную.

Для этого, вероятно, есть драгоценные камни, хотя это легко сделать самому

if string[0...3] == "\xef\xbb\xbf"
  string = string[3..-1].force_encoding('UTF-8')
elsif string[0...2] == "\xff\xfe"
  string = string[2..-1].force_encoding('UTF-16LE')
# etc
Другие вопросы по тегам