Jak wczytać tylko konkretną ilość linii z pliku?

0

Mam zadanie:
Napisz funkcję, która z pliku wypisuje n ostatnich linii uwzględniając to, że plik może być bardzo duży (teoretycznie może nie mieścić się cały w pamięci).

W tym celu oblicz rozmiar pliku używając funkcji seek(0, 2) oraz funkcji tell()

Następnie wczytaj ostatnie N znaków z pliku używając funkcji read() i policz ile wśród nich jest końców linii (\n)

Jeżeli nie będzie ich więcej niż założone n wczytaj N przedostatnich znaków i poszukaj kolejnych końców linii. W razie potrzeby powtarzaj wczytywanie i szukanie \n aż ich liczba przekroczy n. Wczytywane znaki należy łączyć w jeden napis. Po znalezieniu odpowiedniej liczby końców linii należy pozbyć się z początku tego napisu pierwszej (niepełnej) linii oraz kolejnych tak, aby w napisie pozostało ich tylko n. Napis należy wypisać.

Dla uproszczenia możemy założyć, że parametr n jest mniejszy od liczby wszystkich linii w pliku (nie trzeba tego sprawdzać).

to mój kod:

def ostatnie(nazwa, n):
    
    with open(nazwa, 'rb') as input_file:
        input_file.seek(0, 2)
        length = input_file.tell()
        
        napis = b''
        input_file.seek(-n,2)
        
        while True:
            ostatnieznaki = input_file.read(n)
            input_file.seek(-n,1)

            if not ostatnieznaki:
                break
            
            ilosc = ostatnieznaki.count(b'\n')

            if ilosc >= n:
                break
            
            input_file.seek(-n,1)
            napis = ostatnieznaki + napis
            
        lines = napis.splitlines()[-n:]
        result = b'\n'.join(lines).decode('utf-8')

    return result
    
    
ostatnie('input.txt', 3)

daje błąd: line 22

[Errno 22] Invalid argument

Nie rozumiem czemu nie działa, a nie mam pojęcia jak zrobić to zadanie inaczej. Ktoś ma pomysł?

4

W tym programie popełniłeś kilka błędów:

  • W pętli dwa razy przesuwasz wstecz pozycję w pliku.
  • W seek przesuwasz wstecz poza początek pliku, jeśli długość pliku nie jest wielokrotnością liczby poszukiwanych znaków końca linii.
  • Szukasz liczbę znaków końca wiersza w danych tylko ostatnio wczytanych, a nie w całym napisie.

Błąd występuje prawdopodobnie dlatego, że seek nie obsługuje przesunięcia od bieżącej pozycji. Można to rozwiązać przesuwając względem początku pliku na uaktualnianą pozycję:

def ostatnie( nazwa, n ):
    with open(nazwa, 'rb' ) as input_file:
        input_file.seek( 0, 2 )
        length = input_file.tell()
        napis = b''
        ilosc = 0
        while ilosc <= n:
            if length < n:
                n = length
            length -= n
            input_file.seek( length, 0 )
            ostatnieznaki = input_file.read(n)
            napis = ostatnieznaki + napis
            ilosc += ostatnieznaki.count( b'\n' )
        lines = napis.splitlines()[ -n: ]
        result = b'\n'.join(lines).decode( 'utf-8' )
    return result
print( ostatnie( 'input.txt', 3 ))

1 użytkowników online, w tym zalogowanych: 0, gości: 1