Wczytywanie danych od użytkownika
Do tej pory wszystkie dane były wpisane na sztywno w kodzie. Teraz program zacznie pytać i słuchać. Poznasz metody konsoli do czytania z klawiatury i — co ważniejsze — nauczysz się pisać kod, który nie wywraca się, gdy ktoś wpisze coś innego, niż się spodziewałeś.
Czego się dziś nauczysz
- Wczytasz tekst z klawiatury metodą
Console.ReadLine - Napiszesz czytelną zachętę, która mówi użytkownikowi, czego oczekujesz
- Zamienisz wczytany tekst na liczbę bezpiecznym sposobem
- Sprawdzisz, czy wprowadzona wartość mieści się w dozwolonym zakresie
- Wyjaśnisz, dlaczego
Console.ReadLinepodkreśla się na żółto
Metoda Console.ReadLine
Ta jedna metoda robi wszystko, czego potrzeba do wczytania danych: zatrzymuje program, czeka na wpisanie tekstu i naciśnięcie klawisza Enter, po czym zwraca wpisany tekst.
Console.Write("Jak masz na imie? ");
string imie = Console.ReadLine();
Console.WriteLine($"Milo cie poznac, {imie}!");
Jak masz na imie? Anna
Milo cie poznac, Anna!
ReadLine zawsze zwraca tekst
Nawet jeśli użytkownik wpisze 42, dostaniesz napis "42", a nie liczbę. Żeby móc liczyć, trzeba wykonać konwersję z poprzedniej lekcji. To najczęstsze potknięcie przy pierwszych programach interaktywnych.
Żółte podkreślenie pod ReadLine
Visual Studio prawdopodobnie podkreśli ci tę linię i pokaże ostrzeżenie CS8600. Program działa, ale warto wiedzieć, o co chodzi.
string imie = Console.ReadLine();
// └── ostrzeżenie CS8600: konwersja literału o wartości null
Console.ReadLine może w rzadkich sytuacjach zwrócić brak wartości zamiast tekstu — na przykład gdy dane wejściowe pochodzą z pliku, który właśnie się skończył. Kompilator o tym wie i uprzedza, że zmienna string mogłaby zostać pusta w sensie „nie ma nic”.
| Rozwiązanie | Kiedy |
|---|---|
string imie = Console.ReadLine() ?? ""; |
Zalecane. Operator ?? mówi: „jeśli po lewej nic nie ma, użyj tego z prawej”. Zmienna zawsze dostanie jakiś napis, choćby pusty. |
string? imie = Console.ReadLine(); |
Znak zapytania po typie oznacza „ta zmienna może nie mieć wartości”. Poprawne, ale wtedy trzeba to sprawdzać przy każdym użyciu. |
| zignorować ostrzeżenie | W programie uruchamianym z klawiatury ReadLine praktycznie nigdy nie zwróci braku wartości. Na potrzeby nauki to dopuszczalne — ale lepiej wyrobić sobie nawyk pisania ?? "". |
W tym kursie będziemy pisać ?? "". To jeden znak więcej, a kod jest wtedy bez zarzutu.
Zachęta, czyli jak zapytać użytkownika
Program, który po prostu zatrzymuje się i miga kursorem, jest bezużyteczny. Użytkownik musi wiedzieć, czego od niego chcesz.
// ✗ Kursor w nowej linii — wygląda, jakby program się zawiesił
Console.WriteLine("Podaj wiek:");
string a = Console.ReadLine() ?? "";
// ✓ Kursor tuż za pytaniem — naturalnie zaprasza do pisania
Console.Write("Podaj wiek: ");
string b = Console.ReadLine() ?? "";
Trzy zasady dobrej zachęty
1. Używaj Console.Write, nie WriteLine — kursor zostaje w tej samej linii.
2. Zostaw spację na końcu — "Podaj wiek: ", żeby wpisany tekst nie kleił się do dwukropka.
3. Podaj oczekiwany format — "Podaj wiek (0-120): " albo "Podaj cene (np. 19,99): ". Użytkownik nie zgadnie, czy ma wpisać kropkę czy przecinek.
Wczytywanie liczb
Skoro ReadLine zwraca tekst, potrzebny jest drugi krok — konwersja. Z poprzedniej lekcji wiesz już, że dla danych od użytkownika używamy TryParse.
Console.Write("Podaj wiek: ");
string tekst = Console.ReadLine() ?? "";
bool sieUdalo = int.TryParse(tekst, out int wiek);
Console.WriteLine($"Wczytano poprawnie: {sieUdalo}");
Console.WriteLine($"Wartosc: {wiek}");
Można to skrócić i wczytać od razu w jednej linii:
Console.Write("Podaj wiek: ");
bool sieUdalo = int.TryParse(Console.ReadLine(), out int wiek);
Tutaj ?? "" nie jest potrzebne
TryParse przyjmuje brak wartości bez protestu — po prostu zwraca wtedy false. Ostrzeżenie CS8600 pojawia się tylko wtedy, gdy przypisujesz wynik ReadLine do zmiennej typu string.
Liczby z częścią ułamkową
Console.Write("Podaj wzrost w metrach (np. 1,75): ");
bool ok = double.TryParse(Console.ReadLine(), out double wzrost);
Console.WriteLine($"Wczytano: {wzrost}");
Przecinek, nie kropka
Na polskim systemie double.TryParse oczekuje przecinka. Użytkownik, który wpisze 1.75, dostanie false — i pomyśli, że program jest zepsuty.
Dlatego zawsze podawaj przykład w zachęcie: "(np. 1,75)". W następnej lekcji pokażemy, jak przyjmować oba zapisy naraz.
Sprawdzanie, czy dane mają sens
Udana konwersja to dopiero połowa sukcesu. Liczba -500 jest poprawną liczbą całkowitą, ale nie jest poprawnym wiekiem.
Przedsmak instrukcji warunkowej
Do sprawdzania danych potrzebujemy instrukcji if. Poznasz ją dokładnie w lekcji 12, ale zasada jest tak prosta, że możemy z niej skorzystać już teraz:
if (warunek) { ... } else { ... } — jeśli warunek jest prawdziwy, wykona się pierwszy blok; w przeciwnym razie drugi. Warunki budujesz z operatorów poznanych w lekcji 08.
Console.Write("Podaj wiek (0-120): ");
bool sieUdalo = int.TryParse(Console.ReadLine(), out int wiek);
if (!sieUdalo)
{
Console.WriteLine("To nie jest liczba calkowita.");
}
else if (wiek < 0 || wiek > 120)
{
Console.WriteLine("Wiek musi miescic sie w zakresie 0-120.");
}
else
{
Console.WriteLine($"Dziekuje. Za 10 lat bedziesz mial {wiek + 10} lat.");
}
| Wpisane | Który blok się wykona | Dlaczego |
|---|---|---|
abc | pierwszy | konwersja się nie udała |
-5 | drugi | konwersja OK, ale wartość poza zakresem |
200 | drugi | jak wyżej |
17 | trzeci | wszystko w porządku |
Dwa różne pytania, dwa różne komunikaty
Rozdzielenie „to nie jest liczba” od „liczba jest poza zakresem” to nie pedanteria. Użytkownik, który dostanie właściwy komunikat, poprawi się za pierwszym razem. Komunikat „błędne dane” nie mówi mu nic.
Czego tu jeszcze brakuje
Ten program mówi, co jest nie tak — ale kończy się i nie daje drugiej szansy. Żeby prosić o dane do skutku, potrzebna jest pętla. Wrócimy do tego w lekcji 14 i wtedy dopiszemy brakujący fragment.
Pozostałe metody klasy Console
| Metoda | Zwraca | Działanie |
|---|---|---|
Console.ReadLine() | string? |
Czeka na wpisanie tekstu i klawisz Enter. Zwraca wpisany tekst bez znaku końca linii. |
Console.Write(x) | nic | Wypisuje tekst, kursor zostaje w tej samej linii. Do zachęt. |
Console.WriteLine(x) | nic | Wypisuje tekst i przechodzi do nowej linii. Bez argumentu robi pustą linię. |
Console.ReadKey() | ConsoleKeyInfo |
Czeka na jeden klawisz, bez Entera. Przydatne do „naciśnij dowolny klawisz”. Wywołanie Console.ReadKey(true) nie wyświetla naciśniętego znaku. |
Console.Clear() | nic | Czyści okno konsoli. Uwaga: w niektórych terminalach nie działa albo tylko przewija ekran. |
Console.Title | string |
Właściwość — ustawia napis na pasku tytułu okna: Console.Title = "Kalkulator";. |
Console.ForegroundColor | kolor | Zmienia kolor tekstu: Console.ForegroundColor = ConsoleColor.Red;. Przywrócenie: Console.ResetColor(). |
Console.Title = "Moj program";
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Operacja zakonczona powodzeniem.");
Console.ResetColor();
Console.WriteLine();
Console.Write("Nacisnij dowolny klawisz...");
Console.ReadKey(true);
Kolorów używaj oszczędnie
Zielony przy powodzeniu i czerwony przy błędzie — tak. Tęcza w każdej linii sprawia, że program wygląda amatorsko i gorzej się czyta. Pamiętaj też o ResetColor(), inaczej kolor zostanie ustawiony aż do zamknięcia okna.
Kalkulator BMI
Program pyta o imię, wzrost i wagę, sprawdza poprawność danych i liczy wskaźnik masy ciała. Wszystko, czego nauczyłeś się w tym bloku, w jednym miejscu.
Console.Title = "Kalkulator BMI";
Console.WriteLine("=== KALKULATOR BMI ===");
Console.WriteLine();
// Imię — dowolny tekst, wystarczy sprawdzić, czy coś wpisano
Console.Write("Imie: ");
string imie = Console.ReadLine() ?? "";
if (imie.Length == 0)
{
imie = "Nieznajomy";
}
// Wzrost — liczba z ułamkiem, w sensownym zakresie
Console.Write("Wzrost w metrach (np. 1,75): ");
bool okWzrost = double.TryParse(Console.ReadLine(), out double wzrost);
// Waga — j.w.
Console.Write("Waga w kilogramach (np. 68,5): ");
bool okWaga = double.TryParse(Console.ReadLine(), out double waga);
Console.WriteLine();
// Sprawdzamy wszystko naraz
if (!okWzrost || !okWaga)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Blad: wzrost i waga musza byc liczbami (z przecinkiem).");
Console.ResetColor();
}
else if (wzrost <= 0.5 || wzrost >= 2.5 || waga <= 20 || waga >= 400)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Blad: podane wartosci sa poza realnym zakresem.");
Console.ResetColor();
}
else
{
double bmi = waga / (wzrost * wzrost);
Console.WriteLine($"{imie}, twoje BMI wynosi {bmi:F1}");
Console.WriteLine();
Console.WriteLine("Zakresy orientacyjne:");
Console.WriteLine(" ponizej 18,5 - niedowaga");
Console.WriteLine(" 18,5 - 24,9 - waga prawidlowa");
Console.WriteLine(" 25,0 - 29,9 - nadwaga");
Console.WriteLine(" 30,0 i wiecej - otylosc");
}
Console.WriteLine();
Console.Write("Nacisnij dowolny klawisz...");
Console.ReadKey(true);
=== KALKULATOR BMI ===
Imie: Anna
Wzrost w metrach (np. 1,75): 1,68
Waga w kilogramach (np. 68,5): 59
Anna, twoje BMI wynosi 20,9
Zakresy orientacyjne:
ponizej 18,5 - niedowaga
18,5 - 24,9 - waga prawidlowa
25,0 - 29,9 - nadwaga
30,0 i wiecej - otylosc
Nacisnij dowolny klawisz...
Omówienie
| Fragment | Dlaczego tak |
|---|---|
Console.ReadLine() ?? "" |
Usuwa ostrzeżenie CS8600. Gdyby ReadLine nie zwróciło nic, zmienna dostanie pusty napis zamiast braku wartości. |
if (imie.Length == 0) |
Puste imię nie jest błędem, więc nie przerywamy programu — po prostu podstawiamy wartość zastępczą. Nie każdy problem z danymi musi kończyć się komunikatem o błędzie. |
| Zachęty z przykładem | "(np. 1,75)" od razu pokazuje, że separatorem jest przecinek. Bez tego połowa użytkowników wpisze kropkę i zobaczy komunikat o błędzie. |
if (!okWzrost || !okWaga) |
Najpierw sprawdzamy, czy w ogóle udało się wczytać liczby. Dopiero potem ma sens pytanie o ich zakres. |
Zakres 0.5 – 2.5 metra |
Walidacja nie musi być idealna — ma odsiać wartości oczywiście bezsensowne, jak wzrost 175 (metrów, bo ktoś podał centymetry). |
waga / (wzrost * wzrost) |
Nawias jest konieczny. Bez niego dzielenie i mnożenie wykonałyby się od lewej: (waga / wzrost) * wzrost, co dałoby po prostu wagę. |
{bmi:F1} |
Jedno miejsce po przecinku wystarczy — BMI podaje się z taką dokładnością, a bez formatu zobaczylibyśmy 20,904761904761905. |
Console.ReadKey(true) |
Argument true sprawia, że naciśnięty klawisz nie pojawia się na ekranie. Bez niego w oknie zostałaby przypadkowa litera. |
Na co uważać
| Kod | Problem |
|---|---|
int wiek = Console.ReadLine(); |
CS0029 — ReadLine zwraca tekst, nie liczbę. Potrzebna konwersja. |
int wiek = int.Parse(Console.ReadLine()); |
Kompiluje się, ale przerwie program, gdy użytkownik wpisze cokolwiek innego niż liczbę. Użyj TryParse. |
Console.WriteLine("Podaj wiek: "); |
Nie błąd, ale kursor przeskoczy do nowej linii i program będzie wyglądał na zawieszony. Do zachęt używaj Write. |
Console.Write("Podaj wiek:"); |
Brak spacji na końcu — wpisany tekst sklei się z dwukropkiem. |
| zachęta bez formatu | "Podaj wzrost: " nie mówi, czy chodzi o metry czy centymetry ani czy pisać kropkę czy przecinek. Dodaj przykład. |
| sprawdzenie zakresu bez sprawdzenia konwersji | Gdy TryParse zawiedzie, zmienna dostaje zero — a zero często mieści się w dozwolonym zakresie. Najpierw pytaj, czy konwersja się udała. |
Console.ReadKey() bez argumentu |
Naciśnięty klawisz wypisze się na ekranie. Użyj ReadKey(true). |
brak ResetColor() |
Ustawiony kolor zostaje do końca działania programu — także dla komunikatów systemowych. |
Zadania
Program pyta o imię i rok urodzenia, po czym wypisuje powitanie z obliczonym wiekiem. Zadbaj o poprawne zachęty.
pokaż rozwiązanie
const int BiezacyRok = 2026;
Console.Write("Imie: ");
string imie = Console.ReadLine() ?? "";
Console.Write("Rok urodzenia (np. 2008): ");
bool ok = int.TryParse(Console.ReadLine(), out int rok);
if (!ok)
{
Console.WriteLine("Rok musi byc liczba.");
}
else
{
int wiek = BiezacyRok - rok;
Console.WriteLine($"Czesc {imie}! W tym roku konczysz {wiek} lat.");
}
Rok bieżący trzymamy w stałej, a nie wpisujemy wprost w obliczenie — dzięki temu w przyszłym roku poprawiasz jedną linię. Program mógłby pobrać go z systemu przez DateTime.Now.Year, ale do tego wrócimy przy datach.
Wczytaj długość w centymetrach i wypisz ją w metrach, calach i stopach. Przelicznik: 1 cal = 2,54 cm, 1 stopa = 30,48 cm.
pokaż rozwiązanie
const double CmNaCal = 2.54;
const double CmNaStope = 30.48;
Console.Write("Podaj dlugosc w centymetrach: ");
bool ok = double.TryParse(Console.ReadLine(), out double cm);
if (!ok || cm < 0)
{
Console.WriteLine("Podaj poprawna, nieujemna liczbe.");
}
else
{
Console.WriteLine($"{cm} cm to:");
Console.WriteLine($" {cm / 100,8:F2} m");
Console.WriteLine($" {cm / CmNaCal,8:F2} cala");
Console.WriteLine($" {cm / CmNaStope,8:F2} stopy");
}
Podaj dlugosc w centymetrach: 175
175 cm to:
1,75 m
68,90 cala
5,74 stopy
Warunek !ok || cm < 0 łączy dwa sprawdzenia: konwersję i sens wartości. Dzięki ocenie skróconej, gdy konwersja zawiedzie, drugi warunek nie jest nawet sprawdzany.
Liczba 8 w klamrach wyrównuje wyniki do prawej, żeby przecinki ustawiły się jeden pod drugim.
Wczytaj dwie liczby całkowite i wypisz ich sumę, różnicę, iloczyn oraz iloraz z dokładnością do dwóch miejsc. Zabezpiecz się przed dzieleniem przez zero.
pokaż rozwiązanie
Console.Write("Pierwsza liczba: ");
bool ok1 = int.TryParse(Console.ReadLine(), out int a);
Console.Write("Druga liczba: ");
bool ok2 = int.TryParse(Console.ReadLine(), out int b);
Console.WriteLine();
if (!ok1 || !ok2)
{
Console.WriteLine("Obie wartosci musza byc liczbami calkowitymi.");
}
else
{
Console.WriteLine($"{a} + {b} = {a + b}");
Console.WriteLine($"{a} - {b} = {a - b}");
Console.WriteLine($"{a} * {b} = {a * b}");
if (b == 0)
{
Console.WriteLine($"{a} / {b} = nie mozna dzielic przez zero");
}
else
{
Console.WriteLine($"{a} / {b} = {(double)a / b:F2}");
}
}
Rzutowanie (double)a jest konieczne — bez niego dostalibyśmy dzielenie całkowite i format F2 pokazałby na przykład 3,00 zamiast 3,50.
Sprawdzenie b == 0 musi być przed dzieleniem. Nie da się tego zrobić po fakcie — program przerwałby działanie wcześniej.
Wczytaj stan licznika z poprzedniego i bieżącego miesiąca oraz cenę za kilowatogodzinę. Oblicz zużycie i kwotę do zapłaty. Sprawdź, czy nowy stan nie jest mniejszy od poprzedniego.
pokaż rozwiązanie
Console.Write("Stan poprzedni (kWh): ");
bool ok1 = int.TryParse(Console.ReadLine(), out int poprzedni);
Console.Write("Stan biezacy (kWh): ");
bool ok2 = int.TryParse(Console.ReadLine(), out int biezacy);
Console.Write("Cena za kWh (np. 0,89): ");
bool ok3 = decimal.TryParse(Console.ReadLine(), out decimal cena);
Console.WriteLine();
if (!ok1 || !ok2 || !ok3)
{
Console.WriteLine("Wszystkie wartosci musza byc liczbami.");
}
else if (biezacy < poprzedni)
{
Console.WriteLine("Stan biezacy nie moze byc mniejszy od poprzedniego.");
}
else if (cena <= 0)
{
Console.WriteLine("Cena musi byc dodatnia.");
}
else
{
int zuzycie = biezacy - poprzedni;
decimal doZaplaty = zuzycie * cena;
Console.WriteLine($"Zuzycie: {zuzycie} kWh");
Console.WriteLine($"Cena: {cena:F2} zl/kWh");
Console.WriteLine($"Do zaplaty: {doZaplaty:F2} zl");
}
Stan poprzedni (kWh): 14520
Stan biezacy (kWh): 14783
Cena za kWh (np. 0,89): 0,89
Zuzycie: 263 kWh
Cena: 0,89 zl/kWh
Do zaplaty: 234,07 zl
Trzy osobne komunikaty dla trzech różnych problemów — użytkownik od razu wie, co poprawić.
Cenę trzymamy w decimal, bo to pieniądze. Zużycie w int, bo licznik pokazuje pełne kilowatogodziny. Mnożenie int * decimal działa bez rzutowania.
Napisz krótką ankietę: imię, wiek, ulubiony przedmiot oraz odpowiedź tak/nie na pytanie, czy uczeń lubi programować. Na końcu wypisz podsumowanie w ramce z gwiazdek. Odpowiedź „tak” ma być rozpoznawana niezależnie od wielkości liter.
pokaż rozwiązanie
Console.WriteLine("=== ANKIETA ===");
Console.WriteLine();
Console.Write("Imie: ");
string imie = Console.ReadLine() ?? "";
if (imie.Length == 0) imie = "Anonim";
Console.Write("Wiek: ");
int.TryParse(Console.ReadLine(), out int wiek);
Console.Write("Ulubiony przedmiot: ");
string przedmiot = Console.ReadLine() ?? "brak";
Console.Write("Czy lubisz programowac? (tak/nie): ");
string odpowiedz = Console.ReadLine() ?? "";
bool lubi = string.Equals(odpowiedz, "tak", StringComparison.OrdinalIgnoreCase);
Console.WriteLine();
Console.WriteLine(new string('*', 40));
Console.WriteLine($"* {"PODSUMOWANIE",-36} *");
Console.WriteLine(new string('*', 40));
Console.WriteLine($"* Imie: {imie,-25} *");
Console.WriteLine($"* Wiek: {wiek,-25} *");
Console.WriteLine($"* Przedmiot: {przedmiot,-25} *");
Console.WriteLine($"* Programuje z checia: {(lubi ? "TAK" : "NIE"),-15} *");
Console.WriteLine(new string('*', 40));
=== ANKIETA ===
Imie: Anna
Wiek: 17
Ulubiony przedmiot: informatyka
Czy lubisz programowac? (tak/nie): TAK
****************************************
* PODSUMOWANIE *
****************************************
* Imie: Anna *
* Wiek: 17 *
* Przedmiot: informatyka *
* Programuje z checia: TAK *
****************************************
string.Equals(..., OrdinalIgnoreCase) sprawia, że TAK, Tak i tak są traktowane tak samo.
new string('*', 40) tworzy linię z czterdziestu gwiazdek — krócej niż przepisywanie ich ręcznie.
Zapis {lubi ? "TAK" : "NIE"} to operator warunkowy: jeśli warunek przed znakiem zapytania jest prawdziwy, bierze pierwszą wartość, w przeciwnym razie drugą. To skrócona forma instrukcji if — poznasz ją dokładnie w lekcji 12. Zwróć uwagę na nawias wokół całości: bez niego dwukropek zostałby uznany za początek formatu.
Liczby ujemne po przecinku (-25, -14) wyrównują tekst do lewej, dzięki czemu prawa krawędź ramki układa się równo.
Co trzeba zapamiętać
Console.ReadLine()czeka na Enter i zwraca tekst — zawsze, nawet gdy użytkownik wpisze liczbę.- Do zachęt używaj
Console.Write, ze spacją na końcu i z przykładem oczekiwanego formatu. - Ostrzeżenie CS8600 usuwa zapis
Console.ReadLine() ?? "". - Liczby wczytujemy przez
TryParse— nigdy przezParseprzy danych od użytkownika. - Najpierw sprawdź, czy konwersja się udała, dopiero potem zakres — po nieudanej konwersji zmienna zawiera zero.
- Na polskim systemie liczby ułamkowe wczytuje się z przecinkiem.
- Każdy rodzaj błędu zasługuje na osobny, konkretny komunikat.
Console.ReadKey(true)czeka na klawisz, nie wyświetlając go.- Po zmianie koloru tekstu pamiętaj o
Console.ResetColor().