Łańcuchy znaków (string) w C#

Wprowadzenie – czym jest string?

Łańcuch znaków (string) to ciąg pojedynczych znaków reprezentujący tekst. W C# jest to typ referencyjny, ale zachowuje się często jak typ wartościowy.

string tekst = "Witaj świecie!";
Console.WriteLine(tekst);

Kluczowe informacje:

  • string to alias dla System.String
  • Jest niemutowalny – nie można zmieniać jego zawartości
  • Każda „zmiana” tworzy nowy obiekt string w pamięci
  • Można traktować jak tablicę znaków (dostęp przez indeks)

Dostęp do pojedynczych znaków

string nazwa = "C# jest super";
char pierwszyZnak = nazwa[0];    // 'C'
char ostatniZnak = nazwa[nazwa.Length - 1];  // 'r'

Console.WriteLine($"Pierwszy znak: {pierwszyZnak}");
Console.WriteLine($"Ostatni znak: {ostatniZnak}");
Console.WriteLine($"Długość tekstu: {nazwa.Length}");

// UWAGA: Nie można zmieniać znaków przez przypisanie!
// nazwa[0] = 'D';  // ❌ BŁĄD! String jest niemutowalny

Wyjaśnienie funkcji:

  • tekst[indeks] – dostęp do znaku na danej pozycji (indeks od 0)
  • tekst.Length – właściwość zwracająca liczbę znaków w stringu
  • Indeksowanie działa jak w tablicach (pierwszy element ma indeks 0)

2. Podstawowe właściwości i metody string

2.1 string.Empty – pusty string

// Różne sposoby utworzenia pustego stringu
string pusty1 = "";
string pusty2 = string.Empty;

// Sprawdzanie czy string jest pusty
if (tekst == string.Empty)
{
    Console.WriteLine("String jest pusty");
}

// Lepszy sposób sprawdzania
if (string.IsNullOrEmpty(tekst))
{
    Console.WriteLine("String jest pusty lub null");
}

Wyjaśnienie funkcji:

  • string.Empty – reprezentuje pusty string (preferowane nad „”)
  • string.IsNullOrEmpty(tekst) – sprawdza czy string jest null lub pusty
  • string.IsNullOrWhiteSpace(tekst) – dodatkowo sprawdza czy zawiera tylko białe znaki

2.2 Usuwanie białych znaków

string zSpacjami = "   Witaj świecie!   ";
Console.WriteLine($"Oryginalny: '[{zSpacjami}]'");
Console.WriteLine($"Trim(): '[{zSpacjami.Trim()}]'");
Console.WriteLine($"TrimStart(): '[{zSpacjami.TrimStart()}]'");
Console.WriteLine($"TrimEnd(): '[{zSpacjami.TrimEnd()}]'");

Wyjaśnienie funkcji:

  • Trim() – usuwa białe znaki (spacje, taby, nowe linie) z początku i końca
  • TrimStart() – usuwa białe znaki tylko z początku
  • TrimEnd() – usuwa białe znaki tylko z końca
  • Białe znaki to: spacja, tab (\t), nowa linia (\n, \r\n)

2.3 Zmiana wielkości liter

string tekst = "Programowanie w C#";
Console.WriteLine($"Oryginał: {tekst}");
Console.WriteLine($"Wielkie litery: {tekst.ToUpper()}");
Console.WriteLine($"Małe litery: {tekst.ToLower()}");

// Przydatne przy porównywaniu (ignorowanie wielkości liter)
string input = "ANNA";
if (input.ToLower() == "anna")
{
    Console.WriteLine("Imiona są takie same (ignorując wielkość liter)");
}

Wyjaśnienie funkcji:

  • ToUpper() – konwertuje wszystkie znaki na wielkie litery
  • ToLower() – konwertuje wszystkie znaki na małe litery
  • Przydatne przy porównywaniu tekstów bez uwzględniania wielkości liter
  • Nie zmienia oryginalnego stringu – tworzy nowy

3. Wyszukiwanie i sprawdzanie zawartości

3.1 Metody sprawdzające

string email = "student@technikum.edu.pl";

// Sprawdzanie zawartości
Console.WriteLine($"Zawiera '@': {email.Contains("@")}");        // true
Console.WriteLine($"Zaczyna się od 'student': {email.StartsWith("student")}");  // true
Console.WriteLine($"Kończy się na '.pl': {email.EndsWith(".pl")}");             // true

// Znajdowanie pozycji
int pozycjaMałpa = email.IndexOf('@');
Console.WriteLine($"Pozycja znaku '@': {pozycjaMałpa}");  // 7

// Sprawdzanie czy znak/tekst nie został znaleziony
if (email.IndexOf('$') == -1)
{
    Console.WriteLine("Email nie zawiera znaku '$'");
}

Wyjaśnienie funkcji:

  • Contains(tekst) – sprawdza czy string zawiera podany fragment (zwraca true/false)
  • StartsWith(tekst) – sprawdza czy string zaczyna się od podanego fragmentu
  • EndsWith(tekst) – sprawdza czy string kończy się podanym fragmentem
  • IndexOf(znak/tekst) – zwraca pozycję pierwszego wystąpienia (-1 jeśli nie znaleziono)
  • LastIndexOf(znak/tekst) – zwraca pozycję ostatniego wystąpienia

3.2 Wycinanie fragmentów

string kod = "PL-12345-ABC";
Console.WriteLine($"Oryginalny kod: {kod}");

// Substring - wycięcie fragmentu
string krajKod = kod.Substring(0, 2);        // "PL"
string numerKod = kod.Substring(3, 5);       // "12345" 
string literowKod = kod.Substring(9);        // "ABC" (od pozycji 9 do końca)

Console.WriteLine($"Kraj: {krajKod}");
Console.WriteLine($"Numer: {numerKod}");
Console.WriteLine($"Litery: {literowKod}");

Wyjaśnienie funkcji:

  • Substring(startIndex) – zwraca część stringu od pozycji startIndex do końca
  • Substring(startIndex, length) – zwraca część stringu o określonej długości
  • Indeksy liczą się od 0
  • Jeśli przekroczymy długość stringu, zostanie rzucony wyjątek

4. Formatowanie łańcuchów znaków

4.1 Interpolacja stringów (najwygodniejsza metoda)

string imie = "Anna";
int wiek = 20;
double srednia = 4.75;

// Podstawowa interpolacja
string info = $"Cześć {imie}! Masz {wiek} lat.";
Console.WriteLine(info);

// Formatowanie liczb
string oceny = $"Twoja średnia to {srednia:F2}";  // F2 = 2 miejsca po przecinku
Console.WriteLine(oceny);

// Formatowanie dat
DateTime teraz = DateTime.Now;
string data = $"Dzisiejsza data: {teraz:dd.MM.yyyy}";
Console.WriteLine(data);

Wyjaśnienie funkcji:

  • $"tekst {zmienna}" – interpolacja stringów ($ przed cudzysłowem)
  • {zmienna:format} – formatowanie wartości wewnątrz nawiasów
  • :F2 – formatuje liczbę z 2 miejscami po przecinku
  • :dd.MM.yyyy – formatuje datę (dzień.miesiąc.rok)
  • :C – formatuje jako walutę, :P – jako procent

4.2 string.Format() – klasyczna metoda

string szablon = "Imię: {0}, Wiek: {1}, Średnia: {2:F1}";
string wynik = string.Format(szablon, "Piotr", 19, 4.33);
Console.WriteLine(wynik);  // "Imię: Piotr, Wiek: 19, Średnia: 4.3"

Wyjaśnienie funkcji:

  • string.Format(szablon, arg0, arg1, ...) – formatuje string wg szablonu
  • {0}, {1}, {2} – miejsca na argumenty (numerowane od 0)
  • {2:F1} – argument 2 z formatowaniem do 1 miejsca po przecinku

5. Łączenie i dzielenie stringów

5.1 string.Join() – łączenie z separatorem

// Łączenie tablicy w jeden string
string[] przedmioty = {"Matematyka", "Informatyka", "Fizyka", "Chemia"};
string lista = string.Join(", ", przedmioty);
Console.WriteLine($"Przedmioty: {lista}");
// Wynik: "Przedmioty: Matematyka, Informatyka, Fizyka, Chemia"

// Łączenie liczb
int[] oceny = {5, 4, 4, 3, 5};
string listaOcen = string.Join(" - ", oceny);
Console.WriteLine($"Oceny: {listaOcen}");
// Wynik: "Oceny: 5 - 4 - 4 - 3 - 5"

Wyjaśnienie funkcji:

  • string.Join(separator, kolekcja) – łączy elementy kolekcji używając separatora
  • Separator to string, który zostanie wstawiony między elementy
  • Działa z tablicami, listami i innymi kolekcjami
  • Automatycznie konwertuje elementy na stringi

5.2 Operatory łączenia

// Operator + (unikaj dla wielu łączeń!)
string imie = "Anna";
string nazwisko = "Kowalska";
string pelne = imie + " " + nazwisko;  // "Anna Kowalska"

// StringBuilder dla wielu operacji łączenia (wydajniejszy)
using System.Text;
StringBuilder sb = new StringBuilder();
sb.Append("Imię: ");
sb.Append(imie);
sb.Append(", Nazwisko: ");
sb.Append(nazwisko);
string wynik = sb.ToString();

Wyjaśnienie funkcji:

  • Operator + – łączy stringi, ale tworzy nowy obiekt przy każdej operacji
  • StringBuilder – klasa do wydajnego łączenia wielu stringów
  • sb.Append(tekst) – dodaje tekst do StringBuilder
  • sb.ToString() – konwertuje StringBuilder na string

6. Zamiana i zastępowanie

6.1 Replace() – zastępowanie fragmentów

string tekst = "C++ jest trudny, ale C++ jest potężny";
string nowy = tekst.Replace("C++", "C#");
Console.WriteLine(nowy);  // "C# jest trudny, ale C# jest potężny"

// Usuwanie znaków (zastąpienie pustym stringiem)
string zSpacjami = "t e k s t";
string bezSpacji = zSpacjami.Replace(" ", "");
Console.WriteLine(bezSpacji);  // "tekst"

// Zamiana wielkich na małe (ręcznie)
string tekst2 = "WITAJ";
string maly = tekst2.Replace("W", "w").Replace("I", "i").Replace("T", "t")
                   .Replace("A", "a").Replace("J", "j");
// Lepiej użyć ToLower()!

Wyjaśnienie funkcji:

  • Replace(staryTekst, nowyTekst) – zamienia wszystkie wystąpienia staryTekst na nowyTekst
  • Zamienia WSZYSTKIE wystąpienia, nie tylko pierwsze
  • Zwraca nowy string (oryginalny pozostaje niezmieniony)
  • Można używać do usuwania (zastąpienie pustym stringiem)

7. Najczęstsze błędy i pułapki

7.1 Niemutowalność stringów

// ❌ BŁĄD - próba zmiany znaku
string tekst = "Hello";
// tekst[0] = 'h';  // Błąd kompilacji!

// ✅ PRAWIDŁOWO - tworzenie nowego stringu
string tekst2 = 'h' + tekst.Substring(1);  // "hello"

7.2 Porównywanie stringów

// ❌ Niebezpieczne - wielkość liter ma znaczenie
if (input == "Anna") { }

// ✅ Bezpieczne porównanie
if (input.ToLower() == "anna") { }
// lub
if (string.Equals(input, "Anna", StringComparison.OrdinalIgnoreCase)) { }

7.3 Wydajność przy łączeniu

// ❌ Nieefektywne dla wielu łączeń
string wynik = "";
for (int i = 0; i < 1000; i++)
{
    wynik += i.ToString();  // Tworzy 1000 nowych obiektów!
}

// ✅ Efektywne
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
    sb.Append(i);
}
string wynik = sb.ToString();

8.Przydatne wzorce formatowania

double liczba = 1234.5678;
DateTime data = DateTime.Now;

Console.WriteLine($"{liczba:F2}");      // 1234.57 (2 miejsca po przecinku)
Console.WriteLine($"{liczba:C}");       // 1 234,57 zł (waluta)
Console.WriteLine($"{liczba:P}");       // 123 456,78% (procent)
Console.WriteLine($"{liczba:N0}");      // 1 235 (liczba całkowita z separatorami)

Console.WriteLine($"{data:dd.MM.yyyy}");           // 24.09.2025
Console.WriteLine($"{data:HH:mm:ss}");             // 14:30:25
Console.WriteLine($"{data:dddd, dd MMMM yyyy}");   // środa, 24 września 2025

9. Praktyczny przykład – analiza pliku tekstowego

using System;
using System.IO;
using System.Linq;

class AnalizatorTekstu
{
    static void Main()
    {
        string nazwaPliku = "tekst.txt";
        
        // Tworzymy przykładowy plik
        if (!File.Exists(nazwaPliku))
        {
            string przykład = @"Programowanie w C# jest fascynujące!
C# to nowoczesny język programowania.
Dzięki C# możemy tworzyć różne aplikacje.";
            File.WriteAllText(nazwaPliku, przykład);
        }
        
        // Wczytujemy plik
        string zawartość = File.ReadAllText(nazwaPliku);
        Console.WriteLine("=== ANALIZA PLIKU ===");
        Console.WriteLine($"Zawartość pliku:\n{zawartość}\n");
        
        // Podstawowe statystyki
        Console.WriteLine("=== STATYSTYKI ===");
        Console.WriteLine($"Liczba znaków: {zawartość.Length}");
        Console.WriteLine($"Liczba linii: {zawartość.Split('\n').Length}");
        
        // Analiza słów
        string[] słowa = zawartość.Split(new char[] {' ', '\n', '\r', '.', '!', ','}, 
                                       StringSplitOptions.RemoveEmptyEntries);
        Console.WriteLine($"Liczba słów: {słowa.Length}");
        
        // Najczęściej występujące słowo
        var grupySlów = słowa.GroupBy(s => s.ToLower())
                            .OrderByDescending(g => g.Count())
                            .First();
        Console.WriteLine($"Najczęstsze słowo: '{grupySlów.Key}' ({grupySlów.Count()} razy)");
        
        // Statystyki znaków specjalnych
        int spacje = zawartość.Count(c => c == ' ');
        int literC = zawartość.ToUpper().Count(c => c == 'C');
        
        Console.WriteLine($"Liczba spacji: {spacje}");
        Console.WriteLine($"Liczba liter 'C' (bez względu na wielkość): {literC}");
        
        // Sprawdzanie zawartości
        Console.WriteLine("\n=== WYSZUKIWANIE ===");
        Console.WriteLine($"Zawiera słowo 'programowanie': {zawartość.ToLower().Contains("programowanie")}");
        Console.WriteLine($"Zaczyna się od 'Program': {zawartość.StartsWith("Program")}");
        
        // Formatowanie wyniku
        string raport = $@"
=== RAPORT ANALIZY ===
Plik: {nazwaPliku}
Data analizy: {DateTime.Now:dd.MM.yyyy HH:mm}
Znaków: {zawartość.Length}
Słów: {słowa.Length}
Linii: {zawartość.Split('\n').Length}
Najczęstsze słowo: {grupySlów.Key}
        ";
        
        // Zapisanie raportu
        File.WriteAllText("raport.txt", raport);
        Console.WriteLine("\nRaport zapisano do pliku 'raport.txt'");
    }
}

Wyjaśnienie nowych funkcji:

  • Count(warunek) – liczy elementy spełniające warunek (wymaga using System.Linq)
  • GroupBy(funkcja) – grupuje elementy według klucza
  • OrderByDescending() – sortuje malejąco
  • First() – pobiera pierwszy element
  • @"tekst" – verbatim string (wieloliniowy, bez interpretacji znaków specjalnych)

10. Podsumowanie

Najważniejsze metody string:

  • Sprawdzanie: Contains(), StartsWith(), EndsWith(), IsNullOrEmpty()
  • Manipulacja: Trim(), ToUpper(), ToLower(), Replace(), Substring()
  • Wyszukiwanie: IndexOf(), LastIndexOf()
  • Formatowanie: interpolacja $"", string.Format()
  • Łączenie: string.Join(), StringBuilder dla wielu operacji
  • Dzielenie: Split() (poznane w poprzedniej lekcji)

Kluczowe zasady:

  • String jest niemutowalny – każda „zmiana” tworzy nowy obiekt
  • Używaj interpolacji $"" do formatowania
  • StringBuilder do wielu operacji łączenia
  • Zawsze sprawdzaj wielkość liter przy porównaniach
  • Łącz wiedzę o stringach z operacjami na plikach

11. Praktyczne zadania

Zadanie 1 – Walidator hasła

Napisz program, który:

  1. Pobiera hasło od użytkownika
  2. Sprawdza czy hasło ma co najmniej 8 znaków
  3. Sprawdza czy zawiera wielką literę, małą literę i cyfrę
  4. Wyświetla wynik walidacji

Zadanie 2 – Generator inicjałów

Napisz program, który:

  1. Pobiera imię i nazwisko
  2. Tworzy inicjały (pierwsze litery)
  3. Formatuje je jako „A.K.” (wielkie litery z kropkami)

Zadanie 3 – Cenzura tekstu

Napisz program, który:

  1. Wczytuje plik tekstowy
  2. Zastępuje wybrane słowa gwiazdkami (np. „brzydkie” → „********”)
  3. Zapisuje ocenzurowany tekst do nowego pliku
  4. Wyświetla statystykę zmian

Zadanie 4 – Analizator emaili

Napisz program, który:

  1. Wczytuje plik z adresami email (jeden w linii)
  2. Sprawdza poprawność każdego adresu (zawiera @ i domenę)
  3. Grupuje według domen (np. gmail.com, outlook.com)
  4. Zapisuje raport do pliku

Zadanie 5 – Konwerter formatów

Napisz program, który:

  1. Wczytuje plik CSV z danymi: „Imię,Nazwisko,Wiek”
  2. Konwertuje na format: „Imię Nazwisko (wiek lat)”
  3. Zapisuje do pliku tekstowego
  4. Dodaje statystyki (średni wiek, najstarszy/najmłodszy)