Как удалить строки данных в середине текстового файла с помощью Ruby
Я знаю, как писать в файл и читать из файла, но я не знаю, как модифицировать файл, кроме того, чтобы прочитать весь файл в памяти, манипулировать им и переписать весь файл. Для больших файлов это не очень эффективно.
Я не знаю разницы между append и write.
например.
Если у меня есть файл, содержащий:
Person1,will,23
Person2,Richard,32
Person3,Mike,44
Как мне удастся удалить строку, содержащую Person2?
Ответы
Ответ 1
Вы можете удалить строку несколькими способами:
-
Имитировать удаление. То есть просто переписывайте содержимое строки пробелами. Позже, когда вы читаете и обрабатываете файл, просто игнорируйте такие пустые строки.
Преимущества: это легко и быстро. Концы: это не реальное удаление данных (файл не сжимается), и вам нужно делать больше работы при чтении/обработке файла.
код:
f = File.new(filename, 'r+')
f.each do |line|
if should_be_deleted(line)
# seek back to the beginning of the line.
f.seek(-line.length, IO::SEEK_CUR)
# overwrite line with spaces and add a newline char
f.write(' ' * (line.length - 1))
f.write("\n")
end
end
f.close
File.new(filename).each {|line| p line }
# >> "Person1,will,23\n"
# >> " \n"
# >> "Person3,Mike,44\n"
-
Сделайте реальное удаление. Это означает, что строка больше не будет существовать. Поэтому вам нужно будет прочитать следующую строку и переписать текущую строку. Затем повторите это для всех следующих строк до тех пор, пока не будет достигнут конец файла. Это, похоже, задача с ошибкой (строки разной длины и т.д.), Поэтому здесь существует безошибочная альтернатива: открыть файл temp, записать в него строки до (но не включая) строки, которую вы хотите удалить, пропустить строку, которую вы хотите удалить, записать остальные в файл temp. Удалите исходный файл и переименуйте временный, чтобы использовать его имя. Готово.
Хотя это технически полная переписывание файла, он отличается от того, что вы просили. Файл не нужно полностью загружать в память. Вам нужна только одна строка за раз. Ruby предоставляет метод для этого: IO # each_line.
Профи: никаких предположений. Строки удаляются. Код чтения не должен изменяться. Минусы: при удалении строки (не только код, но и время ввода/вывода/ЦП) выполняется больше работы.
Существует фрагмент, который иллюстрирует этот подход в @azgult answer.
Ответ 2
Поскольку файлы сохраняются по существу как непрерывный блок данных на диск, удаление любой его части требует перезаписи, по крайней мере, того, что происходит после него. Это в сущности означает, что, как вы говорите, оно не особенно эффективно для больших файлов. Поэтому, как правило, рекомендуется ограничивать размеры файлов, чтобы такие проблемы не возникали.
Несколько "компромиссных" решений могут заключаться в том, чтобы скопировать файл по строкам во второй файл и затем перенести это, чтобы заменить первое. Это позволяет избежать загрузки файла в память, но не позволяет избежать доступа к жесткому диску:
require 'fileutils'
open('file.txt', 'r') do |f|
open('file.txt.tmp', 'w') do |f2|
f.each_line do |line|
f2.write(line) unless line.start_with? "Person2"
end
end
end
FileUtils.mv 'file.txt.tmp', 'file.txt'
Еще эффективнее было бы читать-писать, открывать файл и пропустить вперед до позиции, которую вы хотите удалить, а затем переместить оставшуюся часть данных обратно - но это создаст какой-то довольно уродливый код (и я не могу попросите сделать это сейчас).
Ответ 3
Вы можете открыть файл и прочитать его по очереди, добавляя строки, которые хотите сохранить в новом файле. Это позволяет вам контролировать все строки, не уничтожая исходный файл.
File.open('output_file_path', 'w') do |output| # 'w' for a new file, 'a' append to existing
File.open('input_file_path', 'r') do |input|
line = input.readline
if keep_line(line) # logic here to determine if the line should be kept
output.write(line)
end
end
end
Если вам известна позиция начала и конца фрагмента, которую вы хотите удалить, вы можете открыть файл, прочитать его в начале, затем продолжить поиск и продолжить чтение.
Просмотрите параметры метода чтения и прочитайте о поиске здесь:
http://ruby-doc.org/core-2.0/IO.html#method-i-read
Ответ 4
Прочитайте здесь:
File.open('output.txt', 'w') do |out_file|
File.open('input.txt', 'r').each do |line|
out_file.print line.sub('Person2', '')
end
end