← Strona główna

09 — Obsługa plików

Odczyt i zapis plików txt i csv, kontekst with open(), kodowanie UTF-8.

Przetwarzanie i algorytmy

1. Otwieranie pliku — open() i tryby

Plik otwieramy funkcją open(nazwa, tryb, encoding). Zawsze powinniśmy go zamknąć po skończeniu — najlepiej używając menedżera kontekstu with.

TrybZnaczeniePlik nie istnieje
'r'odczyt (domyślny)FileNotFoundError
'w'zapis — nadpisujetworzy nowy
'a'dopisywanie na koniectworzy nowy
'x'tworzenie — błąd gdy istniejetworzy nowy
'r+'odczyt i zapisFileNotFoundError
'rb'odczyt binarnyFileNotFoundError
'wb'zapis binarnytworzy nowy
Zawsze używaj with open() Blok with automatycznie zamyka plik po wyjściu z bloku — nawet gdy wystąpi błąd. Bez with musisz pamiętać o plik.close().
Przykład — otwieranie pliku
# Bez with — ryzykowne
plik = open("dane.txt", "r", encoding="utf-8")
zawartosc = plik.read()
plik.close()   # trzeba pamiętać!

# Z with — zalecane
with open("dane.txt", "r", encoding="utf-8") as plik:
    zawartosc = plik.read()
# plik zamknięty automatycznie po wyjściu z bloku

# Tryb zapisu — tworzy lub nadpisuje
with open("wynik.txt", "w", encoding="utf-8") as plik:
    plik.write("Hello, World!\n")

# Tryb dopisywania
with open("log.txt", "a", encoding="utf-8") as plik:
    plik.write("Nowa linia\n")

# Plik w podkatalogu
with open("dane/uczniowie.txt", "r", encoding="utf-8") as f:
    dane = f.read()

2. Odczyt pliku — read(), readline(), readlines()

MetodaZwracaKiedy używać
read()cały plik jako strmałe pliki, cały tekst naraz
read(n)n znakówczytanie fragmentów
readline()jedna linia jako strlinia po linii ręcznie
readlines()lista wszystkich liniigdy potrzebna lista
for linia in plikiterator liniiduże pliki, oszczędność pamięci
Znaki nowej linii Każda linia w pliku kończy się \n. Przy odczycie readline() i iteracji ten znak zostaje. Usuń go przez linia.strip() lub linia.rstrip('\n').
Przykład — odczyt pliku
# read() — cały plik naraz
with open("tekst.txt", "r", encoding="utf-8") as f:
    zawartosc = f.read()
    print(zawartosc)

# readlines() — lista linii (z \n)
with open("tekst.txt", "r", encoding="utf-8") as f:
    linie = f.readlines()
    print(linie)  # ['linia1\n', 'linia2\n', ...]

# Iteracja — linia po linii (najczęściej używane)
with open("tekst.txt", "r", encoding="utf-8") as f:
    for linia in f:
        linia = linia.strip()   # usuwa \n i spacje
        if linia:               # pomija puste linie
            print(linia)

# readline() — ręcznie
with open("tekst.txt", "r", encoding="utf-8") as f:
    pierwsza = f.readline().strip()
    druga    = f.readline().strip()
    print(pierwsza, druga)

3. Zapis pliku — write() i writelines()

write(tekst) — zapisuje dokładnie podany tekst. Nie dodaje automatycznie nowej linii — musisz dopisać \n samodzielnie.

writelines(lista) — zapisuje listę napisów. Też nie dodaje \n między elementami.

  • write() zwraca liczbę zapisanych znaków
  • Tryb 'w' kasuje poprzednią zawartość pliku
  • Tryb 'a' dopisuje na koniec — nie kasuje
  • Zawsze podawaj encoding="utf-8" dla polskich znaków
Buforowanie Python buforuje zapis — dane trafiają do pliku dopiero przy zamknięciu lub po flush(). Blok with automatycznie wywołuje flush() i close() przy wyjściu.
Przykład — zapis pliku
# Zapis pojedynczych linii
with open("wyniki.txt", "w", encoding="utf-8") as f:
    f.write("Wyniki egzaminu\n")
    f.write("=" * 20 + "\n")
    f.write(f"Kasia: 95\n")
    f.write(f"Marek: 87\n")

# Zapis listy — writelines
uczniowie = ["Kasia", "Marek", "Zofia", "Piotr"]
with open("lista.txt", "w", encoding="utf-8") as f:
    for i, u in enumerate(uczniowie, 1):
        f.write(f"{i}. {u}\n")

# Dopisywanie — tryb 'a'
with open("log.txt", "a", encoding="utf-8") as f:
    import datetime
    znacznik = datetime.datetime.now()
    f.write(f"{znacznik}: Program uruchomiony\n")

# Zapis z użyciem print() do pliku
with open("raport.txt", "w", encoding="utf-8") as f:
    print("Raport", file=f)
    print("-" * 20, file=f)
    print(f"Suma: {100}", file=f)

4. Pliki CSV — moduł csv

CSV (Comma-Separated Values) to format pliku tekstowego gdzie dane rozdzielone są przecinkami (lub średnikami). Pierwsza linia to zazwyczaj nagłówek.

  • Moduł csv jest wbudowany — nie trzeba instalować
  • csv.reader — odczyt jako listy wierszy
  • csv.writer — zapis list jako wierszy CSV
  • csv.DictReader — odczyt jako słowniki (klucz = nagłówek)
  • csv.DictWriter — zapis słowników
  • Parametr delimiter — separator (domyślnie ',')
newline='' przy CSV Otwierając plik CSV zawsze podawaj newline='' — bez tego moduł csv może nieprawidłowo obsługiwać znaki nowej linii, szczególnie na Windows.
Przykład — odczyt i zapis CSV
import csv

# Zapis CSV
dane = [
    ["Imię",   "Klasa", "Punkty"],
    ["Kasia",  "3A",    95],
    ["Marek",  "3B",    87],
    ["Zofia",  "3A",    91],
]

with open("uczniowie.csv", "w", newline="",
          encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(dane)

# Odczyt CSV — reader
with open("uczniowie.csv", "r", newline="",
          encoding="utf-8") as f:
    reader = csv.reader(f)
    naglowek = next(reader)    # pierwsza linia = nagłówek
    for wiersz in reader:
        print(wiersz)

# Odczyt CSV — DictReader (wygodniejszy)
with open("uczniowie.csv", "r", newline="",
          encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for wiersz in reader:
        print(f"{wiersz['Imię']}: {wiersz['Punkty']}")

5. Obsługa błędów — try/except przy plikach

Operacje na plikach mogą się nie powieść z wielu powodów. Dobrze napisany program obsługuje te sytuacje zamiast się zawieszać.

WyjątekPrzyczyna
FileNotFoundErrorplik nie istnieje (tryb 'r')
PermissionErrorbrak uprawnień do pliku
IsADirectoryErrorpodano katalog zamiast pliku
UnicodeDecodeErrorzłe kodowanie znaków
IOErrorogólny błąd wejścia/wyjścia
os.path — sprawdzanie pliku import osos.path.exists(nazwa) sprawdza czy plik istnieje. os.path.isfile() sprawdza czy to plik (nie katalog). Pozwala uniknąć wyjątku zamiast go łapać.
Przykład — obsługa błędów
import os

nazwa = input("Nazwa pliku: ")

# Sprawdzenie przed otwarciem
if not os.path.exists(nazwa):
    print(f"Plik '{nazwa}' nie istnieje.")
else:
    with open(nazwa, "r", encoding="utf-8") as f:
        print(f.read())

# Obsługa wyjątkami
try:
    with open(nazwa, "r", encoding="utf-8") as f:
        zawartosc = f.read()
        print(zawartosc)
except FileNotFoundError:
    print(f"Błąd: plik '{nazwa}' nie istnieje.")
except PermissionError:
    print("Błąd: brak uprawnień do odczytu.")
except UnicodeDecodeError:
    print("Błąd: sprawdź kodowanie pliku.")
except Exception as e:
    print(f"Nieoczekiwany błąd: {e}")
finally:
    print("Operacja zakończona.")

6. Praktyczne wzorce pracy z plikami

Poniżej najczęstsze scenariusze z egzaminów INF.04 — zapamiętaj te wzorce.

  • Wczytaj liczby z pliku — każda w osobnej linii, konwertuj przez int() lub float()
  • Wczytaj dane tabelaryczne — CSV lub linie z separatorem
  • Zapisz wyniki do pliku — po przetworzeniu danych
  • Kopiowanie pliku — odczyt i zapis do nowego
  • Zliczanie linii/słów/znaków — klasyczne zadanie
Encoding na egzaminie Zawsze używaj encoding="utf-8" — polskie znaki (ą, ę, ó...) wymagają UTF-8. Bez tego dostaniesz UnicodeDecodeError lub przekłamania w tekście.
Przykład — wzorce egzaminacyjne
# Wzorzec 1: wczytaj liczby z pliku, oblicz sumę
with open("liczby.txt", "r", encoding="utf-8") as f:
    liczby = [int(linia.strip()) for linia in f
              if linia.strip()]
print(f"Suma: {sum(liczby)}, Avg: {sum(liczby)/len(liczby):.2f}")

# Wzorzec 2: wczytaj CSV bez modułu csv
wyniki = []
with open("dane.csv", "r", encoding="utf-8") as f:
    naglowek = f.readline()   # pomiń nagłówek
    for linia in f:
        czesci = linia.strip().split(",")
        imie   = czesci[0]
        wynik  = int(czesci[1])
        wyniki.append((imie, wynik))

# Wzorzec 3: zlicz linie, słowa, znaki
with open("tekst.txt", "r", encoding="utf-8") as f:
    tekst  = f.read()
linie  = tekst.splitlines()
slowa  = tekst.split()
print(f"Linii: {len(linie)}, Słów: {len(slowa)}, Znaków: {len(tekst)}")

# Wzorzec 4: zapisz posortowane wyniki
wyniki.sort(key=lambda x: x[1], reverse=True)
with open("ranking.txt", "w", encoding="utf-8") as f:
    for i, (imie, w) in enumerate(wyniki, 1):
        f.write(f"{i}. {imie}: {w}\n")

Zadania przykładowe z omówieniem

Zadanie 1 Dziennik ocen — zapis i odczyt łatwe

Wczytuj od użytkownika pary: imię ucznia i ocena. Zapisz je do pliku oceny.txt (każda para w osobnej linii, oddzielona przecinkiem). Następnie odczytaj plik i wypisz wszystkich uczniów z ich średnią.

Rozwiązanie
# CZĘŚĆ 1: Zbieranie i zapis
wpisy = []
while True:
    imie = input("Imię (lub 'koniec'): ")
    if imie.lower() == "koniec":
        break
    ocena = int(input("Ocena: "))
    wpisy.append((imie, ocena))

with open("oceny.txt", "w", encoding="utf-8") as f:
    for imie, ocena in wpisy:
        f.write(f"{imie},{ocena}\n")

print("Zapisano do pliku oceny.txt")

# CZĘŚĆ 2: Odczyt i statystyki
uczniowie = {}
with open("oceny.txt", "r", encoding="utf-8") as f:
    for linia in f:
        czesci = linia.strip().split(",")
        imie   = czesci[0]
        ocena  = int(czesci[1])
        if imie not in uczniowie:
            uczniowie[imie] = []
        uczniowie[imie].append(ocena)

print("\n--- Statystyki ---")
for imie, oceny in uczniowie.items():
    sr = sum(oceny) / len(oceny)
    print(f"{imie:<12} oceny: {oceny}  śr: {sr:.2f}")
Omówienie krok po kroku
  1. Zapis par z separatorem
    f.write(f"{imie},{ocena}\n") — przecinek jako separator, \n na końcu każdej linii. To prosty format CSV bez modułu csv.
  2. Odczyt i parsowanie
    linia.strip().split(",") — najpierw usuwamy \n przez strip(), potem dzielimy po przecinku. Indeks [0] to imię, [1] to ocena.
  3. Grupowanie w słowniku
    Ten sam uczeń może mieć wiele wpisów. Słownik uczniowie grupuje oceny — dla każdego imienia lista ocen. Wzorzec identyczny jak w kafelku 07.
  4. Dwie fazy — zapis i odczyt
    Celowo rozdzielamy zapis i odczyt — dwa osobne bloki with. To ważne: między nimi plik jest poprawnie zamknięty i gotowy do odczytu.
Zadanie 2 Analiza pliku CSV z wynikami średnie

Masz plik CSV z kolumnami: Imię, Matematyka, Polski, Angielski. Wczytaj go, oblicz średnią dla każdego ucznia i dla każdego przedmiotu. Wypisz ranking uczniów (od najlepszego) i zapisz go do nowego pliku.

Rozwiązanie
import csv

# Plik wejściowy (utwórz go ręcznie lub kodem poniżej)
# Imię,Matematyka,Polski,Angielski
# Kasia,90,85,92
# Marek,78,88,75
# Zofia,95,91,89

uczniowie = []
with open("wyniki.csv", "r", newline="",
          encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for wiersz in reader:
        imie  = wiersz["Imię"]
        mat   = int(wiersz["Matematyka"])
        pol   = int(wiersz["Polski"])
        ang   = int(wiersz["Angielski"])
        sr    = (mat + pol + ang) / 3
        uczniowie.append({
            "imie": imie, "mat": mat,
            "pol": pol, "ang": ang, "sr": sr
        })

# Ranking uczniów
ranking = sorted(uczniowie, key=lambda x: x["sr"],
                 reverse=True)

print(f"{'Miejsce':<8} {'Imię':<10} {'Mat':>5} "
      f"{'Pol':>5} {'Ang':>5} {'Śr':>7}")
print("-" * 45)
for i, u in enumerate(ranking, 1):
    print(f"{i:<8} {u['imie']:<10} {u['mat']:>5} "
          f"{u['pol']:>5} {u['ang']:>5} {u['sr']:>7.2f}")

# Zapis rankingu
with open("ranking.txt", "w", encoding="utf-8") as f:
    f.write("RANKING UCZNIÓW\n")
    f.write("=" * 30 + "\n")
    for i, u in enumerate(ranking, 1):
        f.write(f"{i}. {u['imie']}: {u['sr']:.2f}\n")
Omówienie krok po kroku
  1. DictReader — słowniki zamiast list
    Każdy wiersz CSV staje się słownikiem gdzie klucze to nagłówki. wiersz["Matematyka"] zamiast wiersz[1] — czytelniejsze i mniej podatne na błędy pozycyjne.
  2. Budowanie listy słowników
    Każdy uczeń to słownik z polami. Lista słowników to wygodna struktura do sortowania, filtrowania i wypisywania.
  3. Sortowanie po średniej
    sorted(..., key=lambda x: x["sr"], reverse=True) — sortujemy malejąco po wartości klucza "sr" w każdym słowniku.
  4. Wyrównanie tabeli
    Formatowanie f-stringa z szerokościami kolumn — wynik wygląda jak prawdziwa tabela zarówno na ekranie jak i w pliku.
Zadanie 3 Filtrowanie i kopiowanie pliku średnie

Wczytaj plik tekstowy. Utwórz nowy plik zawierający tylko linie które zawierają podane słowo kluczowe (wyszukiwanie bez rozróżniania wielkości liter). Wypisz ile linii skopiowano i ile pominięto.

Rozwiązanie
import os

plik_we  = input("Plik wejściowy: ")
plik_wy  = input("Plik wyjściowy: ")
szukane  = input("Słowo kluczowe: ").lower()

if not os.path.exists(plik_we):
    print("Błąd: plik wejściowy nie istnieje.")
else:
    skopiowano = 0
    pominieto  = 0

    with open(plik_we,  "r", encoding="utf-8") as f_we, \
         open(plik_wy, "w", encoding="utf-8") as f_wy:
        for linia in f_we:
            if szukane in linia.lower():
                f_wy.write(linia)
                skopiowano += 1
            else:
                pominieto += 1

    print(f"Skopiowano: {skopiowano} linii")
    print(f"Pominięto:  {pominieto} linii")
    print(f"Zapisano do: {plik_wy}")
Omówienie krok po kroku
  1. Dwa pliki w jednym with
    with open(...) as f_we, open(...) as f_wy: — można otworzyć wiele plików w jednym bloku with. Oba zostaną poprawnie zamknięte.
  2. Szukanie bez case-sensitivity
    szukane in linia.lower() — zamieniamy linię na małe litery przed porównaniem. Szukane słowo też jest już małymi literami (zamieniamy je przy wczytaniu).
  3. Zapis linii bez modyfikacji
    f_wy.write(linia) — piszemy oryginalną linię z \n na końcu. Nie robimy strip() bo chcemy zachować oryginalny format.
  4. Walidacja przed operacją
    os.path.exists() sprawdza istnienie pliku przed próbą otwarcia. Lepiej sprawdzić wcześniej niż łapać wyjątek.

Zadania do samodzielnego rozwiązania

Utwórz najpierw przykładowe pliki tekstowe lub CSV — możesz je wygenerować kodem albo wpisać ręcznie w edytorze.

1★☆☆

Notatnik

Napisz program który wczytuje linie od użytkownika i zapisuje je do pliku. Wpisanie pustej linii kończy wpisywanie. Wypisz ile linii zapisano.

Wskazówka: while True, if not linia: break, tryb 'w'
2★☆☆

Statystyki pliku

Wczytaj dowolny plik tekstowy. Wypisz: liczbę linii, liczbę słów, liczbę znaków (ze spacjami i bez), najdłuższą linię.

Wskazówka: splitlines(), split(), max(..., key=len)
3★★☆

Generator CSV

Napisz program generujący plik CSV z losowymi danymi 20 uczniów: imię, klasa (3A/3B/3C), wynik (0–100). Następnie odczytaj go i wypisz statystyki dla każdej klasy.

Wskazówka: random.choice(), csv.writer, grupowanie po klasie
4★★☆

Numerowanie linii

Wczytaj plik tekstowy i zapisz jego kopię gdzie każda linia ma numer na początku (jak w edytorze kodu): 1 | treść linii.

Wskazówka: enumerate(f, 1), f"{i:3} | {linia}"
5★★☆

Scalanie plików

Masz kilka plików tekstowych (np. czesc1.txt, czesc2.txt, czesc3.txt). Scal je w jeden plik wynikowy — każdy poprzedzony nagłówkiem z nazwą pliku źródłowego.

Wskazówka: lista nazw plików, pętla, tryb 'a' lub jeden 'w'
6★★★

Prosta baza danych w CSV

Zaimplementuj CRUD dla pliku CSV z uczniami: dodaj rekord, usuń po imieniu, edytuj wynik, wylistuj wszystkich. Każda operacja zapisuje zmiany do pliku.

Wskazówka: wczytaj cały plik do listy, zmodyfikuj listę w pamięci, zapisz z powrotem