Wstęp
Mamy stworzony w poprzednich lekcjach świetny program.
Mamy obiekty, funkcje, menu.
Ale ma on jedną dyskwalifikującą wadę. Działa jak rybka w akwarium, czyli ma krótką pamięć. Wyłączasz komputer, idziesz spać, rano włączasz… i Twoja lista wydatków jest pusta. Wszystkie dane zniknęły.
Dlaczego? Bo trzymaliśmy je w RAM (pamięci operacyjnej). RAM jest szybka, ale ulotna (znika po odcięciu prądu). Aby dane przetrwały, musimy je zapisać na Dysku Twardym.
Dziś nauczymy nasz program pakować walizki (dane) i wysyłać je do magazynu (pliku), a potem je rozpakowywać.
Część 1: Mała Encyklopedia Typów (Teoria)
Zanim zaczniemy rzucać danymi po dysku, musimy uporządkować wiedzę. Komputer inaczej traktuje liczbę 5, a inaczej Listę Transakcji.
Typy Proste (Wartościowe)
To najprostsze klocki. Siedzą bezpośrednio w „pudełku” ze zmienną. Są lekkie.
int(Integer) – Liczby całkowite (np.1,-50,2025).float/double/decimal– Liczby z przecinkiem. W C# do pieniędzy używamydecimal(bo jest precyzyjny), w Pythoniefloat– który jest tożsamy z decimalem w C#..bool(Boolean) – Prawda lub Fałsz (True/False). Włącznik światła.char(Character) – Pojedynczy znak (np.'A','@').
Typy Złożone (Referencyjne)
To kontenery. Zmienna nie trzyma „mebli”, tylko „adres do magazynu”, gdzie te meble stoją.
string(Tekst) – Tak, tekst to typ złożony! To łańcuch znaków (char).List/Array(Lista/Tablica) – Kolekcja elementów.Class/Object(Klasa/Obiekt) – NaszaTransakcja. Złożona struktura.
Dlaczego to ważne przy zapisie?
Liczbę łatwo zapisać w pliku. Ale jak zapisać Obiekt, który ma w środku Opis, Kwotę i Datę? Nie możemy po prostu wrzucić „obiektu” do pliku tekstowego. Musimy go zserializować.
Część 2: JSON – Język, który łączy światy oraz dane na dysku
Serializacja to mądre słowo na „spakowanie”. Zamieniamy nasz skomplikowany Obiekt (meblościankę) na płaski tekst (instrukcję montażu IKEA), który można wysłać przez internet lub zapisać w pliku.
Najpopularniejszym formatem zapisu jest JSON (czyt. dżejson). Wygląda tak:
[
{
"opis": "Biedronka",
"kwota": -50.0,
"typ": "Wydatek"
},
{
"opis": "Wypłata",
"kwota": 5000.0,
"typ": "Przychód"
}
]
Widzisz? To zwykły tekst, ale sformatowany tak, że i człowiek, i komputer go zrozumieją. Używa go Python, C#, JavaScript, a nawet Twoja smart lodówka.
Zapisywanie na dysku pokażę na przykładzie słowników.
To najprostszy scenariusz. Masz listę słowników (np. proste wydatki bez użycia klas). Pythonowa biblioteka json łyka to bez popijania.
Używamy konstrukcji with open(...). Działa ona jak „samozamykacz” w drzwiach – gwarantuje, że plik zostanie zamknięty po zapisaniu, nawet jak program wyrzuci błąd.
import json
# Nasze dane w pamięci (RAM)
dane = [
{"towar": "Chleb", "cena": 5.50},
{"towar": "Masło", "cena": 8.00}
]
# ZAPIS (Serializacja)
# 'w' = write (zapisz/nadpisz)
# encoding='utf-8' = obsługa polskich znaków
with open("zakupy.json", "w", encoding='utf-8') as plik:
json.dump(dane, plik, indent=4)
# indent=4 sprawia, że plik ładnie wygląda (ma wcięcia)
print("Zapisano!")

Pliki, o ile nie podamy ścieżki zapisują się w tym samym katalogu, w którym mamy program główny.
Na zrzucie widzisz kod w VSCode i obok otwartą zawartość pliku json z danymi.

Odczytywanie danych
Teraz w drugą stronę. Program startuje i chce wiedzieć, co było w pliku.
import json
try:
# ODCZYT (Deserializacja)
# 'r' = read (czytaj)
with open("zakupy.json", "r", encoding='utf-8') as plik:
wczytane_dane = json.load(plik)
print("Co my tu mamy:", wczytane_dane)
# Wynik: [{'towar': 'Chleb', 'cena': 5.5}, {'towar': 'Masło', 'cena': 8.0}]
except FileNotFoundError:
print("Plik jeszcze nie istnieje!")
tym razem jak widzisz używamy wczytane_dane = json.load(plik) czyli metoda dump tworzy jsona, a load pobiera.
Część 3: Python kompletny program
W Pythonie obsługa plików jest bardzo elegancja dzięki instrukcji with.
Wyzwanie:
Python nie umie automatycznie zamienić Twojej własnej klasy Transakcja na JSON. Musimy mu pomóc. Każdy obiekt w Pythonie ma ukrytą kieszonkę __dict__, która przechowuje jego dane w formie słownika. Wykorzystamy to!
Kod w Pythonie
Najpierw na samej górze dodaj: import json
import json
import os
# --- 1. FOREMKA (KLASA) ---
# To jest ten element obiektowy.
# Dzięki temu mamy pewność, że każda transakcja ma opis, kwotę i typ.
class Transakcja:
def __init__(self, opis, kwota, typ):
self.opis = opis
self.kwota = kwota
self.typ = typ
# --- 2. ZMIENNE GLOBALNE ---
historia = [] # Tu będziemy trzymać OBIEKTY (instancje klasy Transakcja)
PLIK_DANYCH = "budzet.json"
# --- 3. FUNKCJE (LOGIKA) ---
def zapisz_dane():
# To był Twój błąd wcześniej. JSON nie umie zapisać Klasy.
# Musimy zamienić listę Obiektów na listę Słowników.
# Używamy t.__dict__, co jest możliwe TYLKO, gdy t jest Obiektem.
dane_do_zapisu = [t.__dict__ for t in historia]
with open(PLIK_DANYCH, 'w', encoding='utf-8') as plik:
json.dump(dane_do_zapisu, plik, indent=4)
print("💾 Dane zostały zapisane na dysku!")
def wczytaj_dane():
if not os.path.exists(PLIK_DANYCH):
return
with open(PLIK_DANYCH, 'r', encoding='utf-8') as plik:
dane_z_pliku = json.load(plik) # To jest lista słowników!
# Konwersja powrotna: Słowniki -> Obiekty
# Musimy "przelać" dane ze słownika do naszej foremki (Klasy)
for rekord in dane_z_pliku:
# Tworzymy NOWY obiekt Transakcja
t = Transakcja(rekord['opis'], rekord['kwota'], rekord['typ'])
historia.append(t)
print(f"📂 Wczytano {len(historia)} transakcji.")
def pobierz_kwote(typ_operacji):
while True:
try:
tekst = input(f"Podaj kwotę {typ_operacji}: ").replace(",", ".")
kwota = float(tekst)
if kwota <= 0:
print("Kwota musi być dodatnia!")
continue
return kwota
except ValueError:
print("Błąd! To nie jest liczba.")
def dodaj_transakcje(typ):
kwota = pobierz_kwote(typ)
opis = input(f"Podaj opis ({typ}): ")
# POPRAWKA: Tutaj tworzymy OBIEKT, a nie słownik!
finalna_kwota = kwota * (-1 if typ == "Wydatek" else 1)
# Wywołujemy konstruktor klasy (__init__)
nowa_transakcja = Transakcja(opis, finalna_kwota, typ)
historia.append(nowa_transakcja)
print(f"Zaksięgowano {typ}: {kwota:.2f} zł.")
def pokaz_historie():
if not historia:
print("Historia jest pusta.")
return
print("\n--- HISTORIA ---")
suma = 0
licznik = 1
for rekord in historia:
# Skoro rekord jest OBIEKTEM, używamy kropki, a nie ['...']
print(f"{licznik}. [{rekord.typ}] {rekord.opis}: {rekord.kwota:.2f} zł")
suma += rekord.kwota
licznik += 1
print(f"SALDO: {suma:.2f} zł")
# --- 4. GŁÓWNA PĘTLA ---
print("--- MONITOR BUDŻETU v5.0 (Python OOP + JSON) ---")
# Na starcie wczytujemy dane (deserializacja)
wczytaj_dane()
while True:
print("\n1. Przychód | 2. Wydatek | 3. Historia | 4. Koniec")
wybor = input("Wybierz: ")
match wybor:
case '1':
dodaj_transakcje("Przychód")
case '2':
dodaj_transakcje("Wydatek")
case '3':
pokaz_historie()
case '4':
zapisz_dane() # Zapis (serializacja)
print("Koniec programu.")
break
case _:
print("Nieznana opcja.")
Część 4: C# – System.Text.Json
W C# (nowoczesnym) mamy wbudowaną, potężną bibliotekę do JSON. Co ciekawe C# jest mądrzejszy niż Python w kwestii obiektów. Potrafi sam „zajrzeć” do Twojej klasy i spakować właściwości publiczne (public) bez żadnych sztuczek z __dict__.
W C# obsługa plików i JSON-a nie jest włączona domyślnie „prosto z pudełka” w głównym kodzie. Musimy na górze pliku „zamówić” odpowiednie narzędzia (przestrzenie nazw):
using System.IO; // "Input/Output" - Klucze do magazynu (Dysku)
using System.Text.Json; // Tłumacz, który zamienia Obiekty na Tekst
Serializacja (Pakowanie walizki)
Serializacja to zamiana żywego Obiektu (który ma swoje miejsce w pamięci RAM) na martwy ciąg znaków (napis), który można wysłać mailem albo zapisać w pliku.
W C# robi to metoda JsonSerializer.Serialize.
Ważne: C# zapisze tylko te pola, które są Publiczne (public). Jeśli masz jakieś prywatne sekrety w klasie, zostaną one pominięte.
// 1. Mamy listę obiektów w pamięci
List<Transakcja> lista = new List<Transakcja>();
lista.Add(new Transakcja("Chleb", 5.50m, "Wydatek"));
// 2. Konfiguracja (opcjonalne, ale warto)
// WriteIndented = true sprawia, że JSON nie jest zbitą linijką tekstu,
// tylko ładnie sformatowanym drzewkiem (z wcięciami).
var opcje = new JsonSerializerOptions { WriteIndented = true };
// 3. SERIALIZACJA (Obiekt -> Napis)
string gotowyTekstJson = JsonSerializer.Serialize(lista, opcje);
// Teraz 'gotowyTekstJson' to po prostu zmienna string!
Zapis na dysk (Klasa File)
Skoro mamy już tekst (string), musimy go zrzucić na dysk. W C# najprostszą metodą jest File.WriteAllText. Działa ona brutalnie, ale skutecznie: tworzy plik (lub kasuje stary, jeśli istniał) i wrzuca tam cały tekst.
string nazwaPliku = "budzet.json";
// To jedna linijka, która robi całą magię zapisu
File.WriteAllText(nazwaPliku, gotowyTekstJson);
Deserializacja (Rozpakowywanie)
To jest moment, w którym C# różni się od Pythona. W Pythonie json.load zwracał nam „jakiś słownik”. W C# musimy być precyzyjni. Musimy powiedzieć: „Hej, masz tu tekst, zamień go proszę w Listę Transakcji„.
Używamy do tego nawiasów ostrych <TypDanych>.
// 1. Najpierw czytamy tekst z dysku
string wczytanyTekst = File.ReadAllText("budzet.json");
// 2. DESERIALIZACJA (Napis -> Obiekt)
// <List<Transakcja>> - to instrukcja dla kompilatora:
// "Użyj foremki Transakcja i ułóż te obiekty w Liście".
List<Transakcja> mojaLista = JsonSerializer.Deserialize<List<Transakcja>>(wczytanyTekst);
// Teraz 'mojaLista' to znów żywe obiekty!
Console.WriteLine(mojaLista[0].Opis); // Działa!
Co jeśli plik nie istnieje? (File.Exists)
Gdybyśmy spróbowali odczytać plik, którego nie ma (np. przy pierwszym uruchomieniu programu), C# wyrzuciłby błąd (Exception) i zamknął program. Dlatego zawsze, zanim odczytamy, pukamy do drzwi:
if (File.Exists("budzet.json"))
{
// Czytaj śmiało
}
else
{
// Stwórz nową, pustą listę, żeby program miał na czym pracować
return new List<Transakcja>();
}Kod w C#
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO; // DO PLIKÓW
using System.Text.Json; // DO JSON
// --- 1. FOREMKA (KLASA) ---
public class Transakcja
{
public string Opis { get; set; }
public decimal Kwota { get; set; }
public string Typ { get; set; }
// Konstruktor (żeby łatwo tworzyć nowe obiekty)
public Transakcja(string opis, decimal kwota, string typ)
{
Opis = opis;
Kwota = kwota;
Typ = typ;
}
// Pusty konstruktor jest potrzebny dla Deserializatora (czasami C# go wymaga)
public Transakcja() { }
}
class Program
{
// Stała nazwa pliku
const string PLIK_DANYCH = "budzet.json";
// --- 2. FUNKCJE ZAPISU I ODCZYTU ---
static void ZapiszDane(List<Transakcja> baza)
{
// Opcje, żeby JSON był ładny i czytelny (z wcięciami)
var opcje = new JsonSerializerOptions { WriteIndented = true };
// Magia: Zamiana Listy Obiektów na Tekst
string jsonString = JsonSerializer.Serialize(baza, opcje);
// Zapis do pliku
File.WriteAllText(PLIK_DANYCH, jsonString);
Console.WriteLine("💾 Dane zostały zapisane!");
}
static List<Transakcja> WczytajDane()
{
// Sprawdzamy, czy plik istnieje. Jak nie - zwracamy pustą listę.
if (!File.Exists(PLIK_DANYCH))
{
return new List<Transakcja>();
}
// Odczytujemy tekst z pliku
string jsonString = File.ReadAllText(PLIK_DANYCH);
// Magia powrotna: Tekst -> Lista Obiektów
// Musimy podać w <...> na co chcemy zamienić ten tekst
List<Transakcja> wczytaneDane = JsonSerializer.Deserialize<List<Transakcja>>(jsonString);
Console.WriteLine($"📂 Wczytano {wczytaneDane.Count} transakcji.");
return wczytaneDane;
}
// --- 3. LOGIKA PROGRAMU ---
static void DodajTransakcje(List<Transakcja> baza, string typ)
{
Console.Write($"Podaj opis ({typ}): ");
string opis = Console.ReadLine();
Console.Write("Podaj kwotę: ");
string tekst = Console.ReadLine();
if (decimal.TryParse(tekst.Replace(",", "."), NumberStyles.Any, CultureInfo.InvariantCulture, out decimal kwota))
{
if (kwota <= 0)
{
Console.WriteLine("Kwota musi być dodatnia!");
return;
}
decimal finalnaKwota = (typ == "Wydatek") ? kwota * -1 : kwota;
// Tworzymy obiekt używając konstruktora
Transakcja t = new Transakcja(opis, finalnaKwota, typ);
baza.Add(t);
Console.WriteLine("Zaksięgowano!");
}
else
{
Console.WriteLine("To nie jest liczba.");
}
}
static void PokazHistorie(List<Transakcja> baza)
{
Console.WriteLine("\n--- HISTORIA ---");
decimal suma = 0;
int licznik = 1;
foreach (Transakcja t in baza)
{
Console.WriteLine($"{licznik}. [{t.Typ}] {t.Opis}: {t.Kwota} zł");
suma += t.Kwota;
licznik++;
}
Console.WriteLine($"SALDO: {suma} zł");
}
// --- 4. MAIN (GŁÓWNA PĘTLA) ---
static void Main()
{
Console.WriteLine("--- MONITOR BUDŻETU v5.0 (C# + JSON) ---");
// Na starcie wczytujemy dane z pliku!
List<Transakcja> historia = WczytajDane();
while (true)
{
Console.WriteLine("\n1. Przychód | 2. Wydatek | 3. Historia | 4. Koniec");
Console.Write("Wybierz: ");
string wybor = Console.ReadLine();
switch (wybor)
{
case "1":
DodajTransakcje(historia, "Przychód");
break;
case "2":
DodajTransakcje(historia, "Wydatek");
break;
case "3":
PokazHistorie(historia);
break;
case "4":
ZapiszDane(historia); // Zapisujemy przed wyjściem
return;
default:
Console.WriteLine("Nieznana opcja.");
break;
}
}
}
}skompiluj program i uruchom z exe, który znajdziesz w folderze bin projektu, w moim przypadku:

podaj dane, kliknij koniec, i ponownie uruchom program

Co się właśnie stało?
Stworzyliśmy system, który jest trwały.
- Uruchom program. Dodaj „Pensja: 5000”. Zamknij program.
- Zajrzyj do folderu z projektem. Powinien tam być plik
budzet.json. Otwórz go w notatniku. Zobaczysz swoje dane! - Uruchom program ponownie. Twoja pensja tam jest!
Przybornik Lekcji 5
| Komenda | Język | Co to robi? |
json.dump(dane, plik) | Python | Pakuje dane do formatu JSON i wrzuca do otwartego pliku. |
json.load(plik) | Python | Czyta plik JSON i zamienia go na listy/słowniki Pythona. |
with open(...) | Python | „Inteligentne” otwarcie pliku. Gwarantuje, że plik zostanie zamknięty po użyciu (nawet jak program wybuchnie). |
t.__dict__ | Python | Magiczna właściwość obiektu. Zwraca jego wnętrze w formie słownika. |
File.WriteAllText(...) | C# | Tworzy plik i wpisuje do niego podany tekst. Szybkie i proste. |
JsonSerializer.Serialize() | C# | Zamienia obiekt (lub listę obiektów) na napis w formacie JSON. |
JsonSerializer.Deserialize<Typ>() | C# | Zamienia napis JSON z powrotem na żywy obiekt danego typu. |
Podsumowanie różnic (Python vs C#)
| Cecha | Python (json) | C# (System.Text.Json) |
| Podejście | „Luźne”. Zamienia obiekt na słownik, a potem słownik na tekst. | „Sztywne”. Analizuje typ obiektu i mapuje go bezpośrednio na JSON. |
| Wymagania | Wymaga triku z __dict__ lub pisania własnego enkodera. | Działa automatycznie dla właściwości publicznych (public). |
| Odczyt | Zwraca słowniki (trzeba ręcznie zamienić je z powrotem na obiekty). | Zwraca gotowe Obiekty! (Pod warunkiem, że podasz typ w <...>). |
| Bezpieczeństwo | Jeśli JSON nie pasuje do obiektu, błąd wyskoczy dopiero przy użyciu. | Jeśli JSON nie pasuje, błąd wyskoczy już przy wczytywaniu (Deserializacji). |
Co dalej? (Lekcja 6)
Teraz zapisujemy wszystko w jednym pliku tekstowym. To działa super w domu, ale w banku by nie przeszło. W następnej, finałowej lekcji kursu podstawowego, zrobimy krok w stronę profesjonalizmu:
Zamienimy plik JSON na Prawdziwą Bazę Danych (SQLite). Uczymy się języka SQL mam nadzieję, z moim drugim kursem: SQL i zintegrujemy go z naszym kodem.
To będzie wielki finał pierwszej, coś sensownego robiącej aplikacji.
Tak, wiem, że strasznie dużo materiału, ale mam nadzieję, że realne wyniki bardziej do Ciebie przemawiają niż czysta teoria.
W lekcji 7 za to, zrobimy dokładnie to samo, ale w trzecim systemie, w TypeScript!

0 komentarzy