Добавить одну строку в файл с Ruby - PullRequest
15 голосов
/ 24 декабря 2011

Я бы хотел добавить одну строку в начало файла с помощью 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"

Ответы [ 8 ]

22 голосов
/ 24 декабря 2011

Это довольно распространенная задача:

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

Вы не хотите пытаться загрузитьфайл полностью в память.Это будет работать до тех пор, пока вы не получите файл, который больше, чем объем выделенной вам ОЗУ, и компьютер перейдет в режим сканирования или, что еще хуже, выйдет из строя.

Вместо этого читайте его построчно.Чтение отдельных строк по-прежнему чрезвычайно быстро и масштабируемо.На вашем диске должно быть достаточно места для хранения исходного и временного файлов.

7 голосов
/ 24 декабря 2011

Между прочим, это работает:

#!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
4 голосов
/ 25 апреля 2014

Как уже говорили, вероятно, не используйте это для больших файлов, но это простое начало.

rd = IO.read 'myfile'
IO.write 'myfile', "hello\n" + rd
4 голосов
/ 21 марта 2014

Я придумал что-то вроде этого, оно немного более наглядно и менее загадочно, чем другие решения, которые я видел:

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")
2 голосов
/ 24 декабря 2011

Вы можете попробовать это:

File.copy_stream(myfile,tempfile)
f = File.open(myfile,'w')
f.write("Hello\n#{File.open(tempfile,'r').read}")
f.close
File.delete(tempfile)
2 голосов
/ 24 декабря 2011

Не существует механизма, позволяющего легко делать то, что вы хотите.

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

$ 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
$ 

Если вы хотите обрабатывать файлы, которые могут не умещаться полностью в памяти, вы должны закодировать цикл вроде этого (не проверено - лучше подумайте, псевдокод):

while (data=orig.read(4096)) {
    new.write(data)
}

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

0 голосов
/ 01 марта 2016

Чисто, но работает.Минимизируйте операции с файлами.

`#!/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
    }`
0 голосов
/ 07 июля 2015

Из командной строки вы можете сделать:

ruby -i -pe '$_= "prepended text\n"+$_ if $. == 1' myfile

или более эффективно

ruby -i -pe 'BEGIN { gets; print "prepended text\n" + $_ }; ' myfile

К сожалению, оказывается, опции -i (на месте) неХотя на самом деле это не на месте (и не является опцией sed s на месте) - мой файл будет иметь другой индекс после операции.

Это огорчило меня, потому что эффективноЯ не могу отфильтровать (предварять) огромный файл, если у меня недостаточно места на диске для двух его копий.

Хотя на самом деле это не сложно сделать на месте (фильтрация в целомне только для первой строки).Все, что вам нужно сделать:

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

Использовать осторожно (это может повредить ваш файл при прерывании).

...