Как заменить первые несколько байтов файла в Ruby, не открывая весь файл целиком?

#ruby

Вопрос:

У меня есть XML-файл размером 30 МБ, который вначале содержит некоторую тарабарщину, и поэтому, как правило, мне приходится удалять его, чтобы Nokogiri мог правильно проанализировать XML-документ.

Вот что у меня сейчас есть:

     contents = File.open(file_path).read
    if contents[0..123].include? 'authenticate_response'
      fixed_contents = File.open(file_path).read[123..-1]
      File.open(file_path, 'w') { |f| f.write(fixed_contents) }
    end
 

Однако на самом деле это приводит к тому, что скрипт ruby дважды открывает большой XML-файл. Один раз, чтобы прочитать первые 123 символа, а в другой раз, чтобы прочитать все, кроме первых 123 символов.

Чтобы решить первую проблему, я смог выполнить это:

 contents = File.open(file_path).read(123)
 

Однако теперь мне нужно удалить эти символы из файла, не читая весь файл целиком. Как я могу «обрезать» начало этого файла, не открывая его целиком в памяти?

Комментарии:

1. «заменить» сильно отличается от «удалить». Вы не можете сделать последнее, не прочитав весь файл целиком.

Ответ №1:

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

 File.open(file_path) do |xml_file|
  if xml_file.read(123).include? 'authenticate_response'
    # header found, nothing to do
  else
    # no header found. We rewind and let nokogiri parse the whole file
    xml_file.rewind
  end

  xml = Nokogiri::XML.parse(xml_file)
  # Now to whatever you want with the parsed XML document
end
 

Пожалуйста , обратитесь к документации IO#read IO#rewind и Nokigiri::XML::Document.parse для получения подробной информации об этих методах.

Комментарии:

1. Вам следует подумать о добавлении begin ... ensure; xml_file.close; end .

2. Я обновил ответ, чтобы использовать блочный вариант File.open . Однако даже без этого файл будет неявно закрыт при следующей сборке мусора.

3. @HolgerJust что привело бы вас к мысли, что это будет GCed?

4. @enginersmnky: Потому что в первоначальной версии моего ответа файловый объект был назначен локальной переменной, которая выйдет за рамки области действия, как только выполнение вернется из метода, в котором находится код. После этого файловый объект будет собран как мусор, а файл закрыт.

5. @HolgerJust Я думаю, что это чрезвычайно зависит, так как операция и ваш первоначальный ответ технически работают в глобальной области main , поэтому этот файл будет оставаться открытым до тех пор, пока сама программа не завершится.