Подготовьте одну строку к файлу с Ruby
Я хотел бы добавить одну строку в верхнюю часть файла с Ruby следующим образом:
# initial file contents
something
else
# file contents after prepending "hello" on its own line
hello
something
else
Следующий код просто заменяет содержимое всего файла:
f = File.new('myfile', 'w')
f.write "test string"
Ответы
Ответ 1
Это довольно распространенная задача:
original_file = './original_file'
new_file = original_file + '.new'
Настройте тест:
File.open(original_file, 'w') do |fo|
%w[something else].each { |w| fo.puts w }
end
Это фактический код:
File.open(new_file, 'w') do |fo|
fo.puts 'hello'
File.foreach(original_file) do |li|
fo.puts li
end
end
Переименуйте старый файл в безопасное:
File.rename(original_file, original_file + '.old')
File.rename(new_file, original_file)
Покажите, что он работает:
puts `cat #{original_file}`
puts '---'
puts `cat #{original_file}.old`
Какие выходы:
hello
something
else
---
something
else
Вы не хотите полностью загружать файл в память. Это будет работать до тех пор, пока вы не получите файл, размер которого больше вашего распределения памяти, и машина переходит на сканирование или, что еще хуже, сбой.
Вместо этого прочитайте его по строкам. Чтение отдельных строк по-прежнему чрезвычайно быстрое и масштабируемо. У вас должно быть достаточно места на диске для хранения исходного и временного файлов.
Ответ 2
fwiw, похоже, работает:
#!usr/bin/ruby
f = File.open("myfile", "r+")
lines = f.readlines
f.close
lines = ["something\n"] + lines
output = File.new("myfile", "w")
lines.each { |line| output.write line }
output.close
Ответ 3
Механизм не существует, чтобы делать то, что вы хотите сделать легко.
Вместо этого вам нужно будет открыть файл, удалить файл, открыть новый файл по старому имени для записи, записать свой контент и затем записать новый файл из старого содержимого файла. Это довольно запутанное звучание, но прямое в коде:
$ cat prepend.rb
#!/usr/bin/ruby
File.open("passwd", "r") do |orig|
File.unlink("passwd")
File.open("passwd", "w") do |new|
new.write "test string"
new.write(orig.read())
end
end
Обратите внимание: механизм, который я использовал, не мешает проверять ошибки - вы должны, вероятно, обрабатывать ошибки для каждого запроса File.open()
и запроса File.unlink()
- и он предполагает, что все содержимое файла будет соответствовать в памяти. Краткий пример:
$ rm passwd
$ cp /etc/passwd .
$ ./prepend.rb
$ head passwd
test stringroot:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
$
Если вы хотите обрабатывать файлы, которые могут не полностью помещаться в памяти, вы должны закодировать такой тип цикла (untested - лучше рассмотреть его псевдокод):
while (data=orig.read(4096)) {
new.write(data)
}
В качестве альтернативы вы можете записать во временный файл, и если процесс записи завершится успешно, удалите файл и переименуйте временный файл. Какой бы подход ни был для вас наиболее полезен.
Ответ 4
Вы можете попробовать следующее:
File.copy_stream(myfile,tempfile)
f = File.open(myfile,'w')
f.write("Hello\n#{File.open(tempfile,'r').read}")
f.close
File.delete(tempfile)
Ответ 5
Я придумал что-то вроде этого, это немного более описательно и менее загадочно, чем другие решения, которые я видел:
def file_prepend(file, str)
new_contents = ""
File.open(file, 'r') do |fd|
contents = fd.read
new_contents = str << contents
end
# Overwrite file but now with prepended string on it
File.open(file, 'w') do |fd|
fd.write(new_contents)
end
end
И вы можете использовать его следующим образом:
file_prepend("target_file.txt", "hello world!\n")
Ответ 6
Как говорили некоторые, вероятно, не используйте это для больших файлов, но это простой старт.
rd = IO.read 'myfile'
IO.write 'myfile', "hello\n" + rd
Ответ 7
В командной строке вы можете:
ruby -i -pe '$_= "prepended text\n"+$_ if $. == 1' myfile
или более эффективно
ruby -i -pe 'BEGIN { gets; print "prepended text\n" + $_ }; ' myfile
К сожалению, параметры -i
(in-place) на самом деле не на месте, хотя (и не имеет значения sed
на месте) - мой файл будет иметь другой индекс после op.
Это огорчило меня, потому что, фактически, я не могу отфильтровать (добавить к) огромный файл, если мне не хватает дискового пространства для двух его копий.
Это не очень сложно сделать на месте, хотя (фильтрация вообще не только для первой строки). Все, что вам нужно сделать, это:
1) убедитесь, что у вас есть отдельные указатели на чтение и запись (или отдельные объекты File для чтения и записи)
2) убедитесь, что вы забуферили непрочитанные части, которые вы собираетесь переписать
3) обрезать файл в конце, если ваша операция фильтрации должна заканчиваться коротким файлом
Я написал класс оболочки для https://github.org/pjump/i_rewriter.
С ним вы можете сделать
Rewriter.new("myfile") do |line, index|
index == 0 ? "prepended text\n" + line : line
end
или с исполняемым файлом:
$ i_rewriter 'index == 0 ? "prepended text\n" + line : line' myfile
Используйте осторожно (это может повредить ваш файл, если прервано).
Ответ 8
Чисто, но работает. Сведите к минимуму операции, связанные с файлами.
`#!/bin/ruby
inv_file = File.open("./1.txt","r+") { |f|
#Read file string by-string
until f.eof?
#Searching for some
if f.readline[/condition here/]
#remember position
offset = f.pos
#Find it? - stop reading
break
end
end
#Remember what contains rest of file
tail = f.readlines
#Again to offset
f.pos = offset
#insert what necessary
f.puts "INSERTED"
#reconstruct tail
f.write tail.join
}`