Как записать маркер спецификации в файл в Ruby - PullRequest
12 голосов
/ 27 марта 2012

У меня есть некоторый рабочий код с костылем, чтобы добавить маркер спецификации в новый файл.

  #writing
  File.open name, 'w', 0644 do |file|
    file.write "\uFEFF"
    file.write @data
  end

  #reading
  File.open name, 'r:bom|utf-8' do |file|
    file.read
  end

Есть ли способ автоматически добавить маркер без написания загадочного "\uFEFF" перед данными? Что-то вроде File.open name, 'w:bom' # this mode has no effect может быть?

Ответы [ 2 ]

9 голосов
/ 27 марта 2012

**** Этот ответ привел к появлению нового камня: file_with_bom ****

В прошлом у меня была похожая проблема, и я расширил File.open дополнительными вариантами кодирования.для w -режима:

class File
  BOM_LIST_hex = {
      Encoding::UTF_8      => "\xEF\xBB\xBF", #"\uEFBBBF"
      Encoding::UTF_16BE => "\xFE\xFF", #"\uFEFF",
      Encoding::UTF_16LE => "\xFF\xFE",
      Encoding::UTF_32BE => "\x00\x00\xFE\xFF",
      Encoding::UTF_32LE => "\xFE\xFF\x00\x00",
    }
  BOM_LIST_hex.freeze
  def utf_bom_hex(encoding = external_encoding)
    BOM_LIST_hex[encoding]
  end

class << self
  alias :open_old :open
  def open(filename, mode_string = 'r', options = {}, &block)
    #check for bom-flag in mode_string
    options[:bom] = true if mode_string.sub!(/-bom/i,'')

    f = open_old(filename, mode_string, options)
    if options[:bom]
      case mode_string
        #r|bom already standard since 1.9.2
        when /\Ar/   #read mode -> remove BOM
          #remove BOM
          bom = f.read(f.utf_bom_hex.bytesize) 
          #check, if it was really a bom
          if bom != f.utf_bom_hex.force_encoding(bom.encoding)
            f.rewind  #return to position 0 if BOM was no BOM
          end
        when /\Aw/  #write mode -> attach BOM
          f = open_old(filename, mode_string, options)
          f << f.utf_bom_hex.force_encoding(f.external_encoding)
        end #mode_string
    end

    if block_given?
      yield f 
      f.close
    end
  end
  end
end #File

Тестовый код:

EXAMPLE_TEXT = 'some content öäü'
File.open("file_utf16le.txt", "w:utf-16le|bom"){|f| f << EXAMPLE_TEXT }
File.open("file_utf16le.txt", "r:utf-16le|bom:utf-8"){|f| p f.read }
File.open("file_utf16le.txt", "r:utf-16le:utf-8",  :bom => true ){|f| p f.read }
File.open("file_utf16le.txt", "r:utf-16le:utf-8"){|f| p f.read }

File.open("file_utf8.txt", "w:utf-8", :bom => true ){|f| f << EXAMPLE_TEXT }
File.open("file_utf8.txt", "r:utf-8", :bom => true ){|f| p f.read }
File.open("file_utf8.txt", "r:utf-8|bom",              ){|f| p f.read }
File.open("file_utf8.txt", "r:utf-8",                     ){|f| p f.read }

Некоторые замечания:

  • Код от 1,9 раза (ноэто все еще работает).
  • Я использовал -bom в качестве индикатора бомбы (ruby 1.9 использует |bom.

Некоторые исправления, необходимые для улучшения:

  • используйте |bom вместо -bom
  • используйте стандарт r|bom для чтения
  • сделайте его рубином 1,8 и 1,9 включенным

Возможно, я будуЗавтра я найду время, чтобы провести рефакторинг моего кода и представить его как драгоценный камень.

4 голосов
/ 27 марта 2012

Увы, я думаю, ваш ручной подход - это путь, по крайней мере, я не знаю лучшего пути:

http://blog.grayproductions.net/articles/miscellaneous_m17n_details

Цитата из статьи JEG2:

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

...