Удалить первые две строки и добавить две строки в файл
У меня есть текстовый файл, который начинается с:
Title
aaa
bbb
ccc
Я не знаю, что будет в строке, но я знаю, что структура файла будет Title
, затем пустая строка, затем фактические строки. Я хочу изменить это на:
New Title
fff
aaa
bbb
ccc
Я имел в виду это:
lineArray = File.readlines(destinationFile).drop(2)
lineArray.insert(0, 'fff\n')
lineArray.insert(0, '\n')
lineArray.insert(0, 'new Title\n')
File.writelines(destinationFile, lineArray)
но writelines
не существует
`writelines' for File:Class (NoMethodError)
Есть ли способ удалить первые две строки файла и добавить три новые строки?
4 ответа
Я бы начал с чего-то вроде этого:
NEWLINES = {
0 => "New Title",
1 => "\nfff"
}
File.open('test.txt.new', 'w') do |fo|
File.foreach('test.txt').with_index do |li, ln|
fo.puts (NEWLINES[ln] || li)
end
end
Вот содержимое test.txt.new
после запуска:
New Title
fff
aaa
bbb
ccc
Идея состоит в том, чтобы предоставить список строк замены в NEWLINES
хэш. Поскольку каждая строка считывается из исходного файла, номер строки проверяется в хэше, и если строка существует, то используется соответствующее значение, в противном случае используется исходная строка.
Если вы хотите прочитать весь файл, а затем заменить, это немного уменьшает код, но у кода будут проблемы с масштабируемостью:
NEWLINES = [
"New Title",
"",
"fff"
]
file = File.readlines('test.txt')
File.open('test.txt.new', 'w') do |fo|
fo.puts NEWLINES
fo.puts file[(NEWLINES.size - 1) .. -1]
end
Это не очень умно, но это будет работать для простых замен.
Если вы действительно хотите сделать это правильно, узнайте, как diff
работает, создайте файл diff, а затем позвольте ему выполнять тяжелую работу, поскольку он предназначен для решения подобных задач, работает очень быстро и используется миллионы раз в день в системах *nix по всему миру.
Используйте put со всем массивом:
File.open("destinationFile", "w+") do |f|
f.puts(lineArray)
end
Если у вас большие файлы, стоит задуматься о производительности и последствиях их чтения в памяти. Если это проблема, то лучше всего рассматривать файлы как потоки. Вот как бы я это сделал.
Сначала определите текст замены:
require "stringio"
replacement = StringOI.new <<END
New Title
fff
END
Я сделал это объектом StringIO, но это также может быть объект File, если ваш текст замены находится в файле.
Теперь откройте файл назначения (новый файл) и запишите в него каждую строку из текста замены.
dest = File.open(dest_fn, 'wb') do |dest|
replacement.each_line {|ln| dest << ln }
Мы могли бы сделать это более эффективно, но есть веская причина сделать это следующим образом: теперь мы можем позвонить replacement.lineno
чтобы получить количество прочитанных строк, вместо того, чтобы повторять их во второй раз для подсчета строк.
Затем откройте исходный файл и ищите вперед, позвонив gets
replacement.lineno
раз:
orig = File.open(orig_fn, 'r')
replacement.lineno.times { orig.gets }
Наконец, запишите оставшиеся строки из исходного файла в новый файл. Мы сделаем это более эффективно на этот раз с File.copy_stream
:
File.copy_stream(orig, dest)
orig.close
dest.close
Вот и все. Конечно, это перетаскивание, закрывающее эти файлы вручную (и когда мы это сделаем, мы должны сделать это в ensure
блок), так что лучше использовать блочную форму File.open
чтобы автоматически закрыть их. Кроме того, мы можем переместить orig.gets
звонки в replacement.each_line
цикл:
File.open(dest_fn, 'wb') do |dest|
File.open(orig_fn, 'r') do |orig|
replacement.each_line {|ln| dest << ln; orig.gets }
File.copy_stream(orig, dest)
end
end
Сначала создайте входной тестовый файл.
FNameIn = "test_in"
text = <<_
Title
How now,
brown cow?
_
#=> "Title\n\nHow now,\nbrown cow?\n"
File.write(FNameIn, text)
#=> 27
Теперь читайте и пишите построчно.
FNameOut = "test_out"
File.open(FNameIn) do |fin|
fin.gets; fin.gets
File.open(FNameOut, 'w') do |fout|
fout.puts "New Title"
fout.puts
fout.puts "fff"
until fin.eof?
fout.puts fin.gets
end
end
end
Проверьте результат:
puts File.read(FNameOut)
# New Title
#
# fff
# How now,
# brown cow?
Ruby закроет каждый из двух файлов, когда его блок завершится.
Если файлы не большие, вы можете написать:
File.write(FNameOut,
["New Title\n", "\n", "fff\n"].concat(File.readlines(FNameIn).drop(2)).join)