Подтвердить что ты не робот

Подготовьте одну строку к файлу с 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"
4b9b3361

Ответ 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
    }`