2013-05-19 8 views
11

Wiem, jak pisać do pliku i czytać z pliku, ale nie wiem, jak zmodyfikować plik, oprócz czytania całego pliku do pamięci, manipulując nim i przepisując cały plik. W przypadku dużych plików nie jest to zbyt produktywne.Jak usunąć wiersze danych w środku pliku tekstowego z Ruby

Tak naprawdę nie znam różnicy między dopisem a zapisem.

E.g.

Jeśli mam plik zawierający:

Person1,will,23 
Person2,Richard,32 
Person3,Mike,44 

Jak będę mógł po prostu usunąć wiersz zawierający PERSON2?

+0

Sawa, zawsze mi pomagasz. Więc za każdym razem, gdy program zapisuje plik, nadpisuje cały plik? – Senjai

+0

Jak zamierzasz znaleźć linie do usunięcia bez czytania pliku? Czy to zawsze jakiś numer linii? –

+0

@Senjai Sergio sugeruje coś, co może pomóc, a jeśli to prawda, to moje poprzednie komentarze są błędne. Przepraszam za to. – sawa

Odpowiedz

13

można usunąć wiersza w kilka sposobów:

  • usunięcie Symulacja. Oznacza to, że wystarczy nadpisać zawartość linii spacjami. Później, po przeczytaniu i przetworzeniu pliku, po prostu zignoruj ​​takie puste linie.

    Zalety: to jest łatwe i szybkie. Przeciw: to nie jest prawdziwe usunięcie danych (plik się nie zmniejsza) i musisz wykonać więcej pracy podczas odczytu/przetwarzania pliku.

    Kod:

    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" 
    
  • Czy prawdziwe usunięcie. Oznacza to, że linia nie będzie już istnieć. Będziesz musiał przeczytać następny wiersz i nadpisać bieżącą linię. Następnie powtórz to dla wszystkich następnych linii, aż do osiągnięcia końca pliku. Wydaje się, że jest to zadanie podatne na błędy (linie o różnej długości itd.), Więc oto jest wolna od błędów alternatywa: otwórz plik tymczasowy, napisz do niego linie do (ale nie włączając) linii, którą chcesz usunąć, pomiń linię chcesz usunąć, napisz resztę do pliku tymczasowego. Usuń oryginalny plik i zmień nazwę tymczasowego, aby użyć jego nazwy. Gotowe.

    Choć technicznie jest to całkowita przeróbka pliku, różni się on od zadawanego. Plik nie musi być w pełni załadowany do pamięci. Potrzebujesz tylko jednej linii naraz. Ruby zapewnia metodę: IO#each_line.

    Zalety: Brak założeń. Linie zostaną usunięte. Kod czytania nie musi być zmieniany. Przeciw: dużo więcej pracy przy kasowaniu linii (nie tylko kod, ale także czas IO/CPU).

    Istnieje urywek, który ilustruje to podejście w @ azgult's answer.

+0

Czy można nadpisać tylko część pliku (ze spacjami) bez nadpisywania całego pliku? – sawa

+1

Oczywiście, to możliwe. Otwórz plik w trybie zapisu, wyszukaj potrzebne przesunięcie i rozpocznij zapis. –

+0

Tryb zapisu, tryb odczytu/zapisu (flaga "r +") jest potrzebny do nadpisania części. – azgult

0

Czytaj here:

File.open('output.txt', 'w') do |out_file| 
    File.open('input.txt', 'r').each do |line| 
    out_file.print line.sub('Person2', '') 
    end 
end 
+0

Powoduje to zastąpienie całego pliku. Sądzę, że OP wie, jak to zrobić, i nie o to pyta. – sawa

+1

Wygląda na to, że jest to jedyny sposób zgodny z innymi odpowiedziami. – juanpastas

3

Możesz otworzyć plik i przeczytać go wiersz po wierszu, dołączając wiersze, które chcesz zachować do nowego pliku. Pozwala to na największą kontrolę nad liniami, bez niszczenia oryginalnego pliku.

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 

Jeśli znasz położenie początku i na końcu fragmentu, który chcesz usunąć, można otworzyć plik, przeczytaj na początku, a następnie dążyć do końca i kontynuować czytanie.

Spójrz parametry metody odczytu i czytać o poszukiwaniu tutaj:

http://ruby-doc.org/core-2.0/IO.html#method-i-read

4

Ponieważ pliki są zapisywane w zasadzie jako ciągły blok danych na dysku, usuwając jakakolwiek jego część powoduje konieczność przepisywania na przynajmniej to, co przychodzi po tym. Zasadniczo oznacza to, że - jak mówisz - nie jest to szczególnie skuteczne w przypadku dużych plików. Dlatego dobrze jest ograniczyć rozmiary plików, aby takie problemy nie występowały.

Kilka "kompromisowych" rozwiązań może polegać na skopiowaniu pliku poprzez linię do drugiego pliku, a następnie przeniesienie go w miejsce pierwszego. W ten sposób unika załadowaniu pliku do pamięci, ale nie unika żadnego dostępu do dysku twardego:

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' 

Jeszcze bardziej efektywne byłoby do odczytu i zapisu otworzyć plik i przejść od razu do pozycji, którą chcesz usunąć, a następnie przesunąć resztę danych - ale to by oznaczało jakiś dość brzydki kod (i nie można mnie o to teraz prosić).

Powiązane problemy