Fundamentals

Strings & Text Handling

Strings & Text Handling

Вступ та Контекст

Робота з текстовими даними — це один з найпоширеніших сценаріїв у розробці застосунків. Від обробки користувацького вводу до парсингу файлів конфігурації, від генерації звітів до валідації даних — рядки (strings) є фундаментальним типом даних у будь-якій мові програмування.

Цікавий факт: У C# рядки є незмінними (immutable). Це означає, що кожна операція, яка здається модифікацією рядка, насправді створює новий об'єкт у пам'яті. Розуміння цього принципу критично важливе для написання ефективного коду.

Що ви дізнаєтесь

У цьому розділі ви опануєте:

  • Концепцію незмінності рядків та її наслідки для продуктивності
  • Різні способи створення та форматування рядків
  • Ефективні техніки роботи з великими обсягами текстових даних
  • Базові прийоми валідації тексту за допомогою регулярних виразів

Передумови

Перед початком вивчення цієї теми рекомендується:

  • Розуміння базових типів даних C# (змінні, константи)
  • Знання про value types vs reference types
  • Базове розуміння роботи з пам'яттю (Stack vs Heap)

Фундаментальні Концепції

String Basics: Immutability (Незмінність)

Рядки в C# є референсними типами (reference types), але водночас незмінними (immutable). Це означає, що після створення об'єкта рядка його вміст не може бути змінений.

Loading diagram...
graph LR
    A["string text = 'Hello'"] -->|створення| B["Об'єкт 'Hello' в Heap"]
    C["text = 'World'"] -->|"НЕ змінює об'єкт"| B
    C -->|створює новий| D["Об'єкт 'World' в Heap"]
    B -.->|час життя закінчився| E[Garbage Collector]

    style A fill:#64748b,stroke:#334155,color:#ffffff
    style B fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style C fill:#64748b,stroke:#334155,color:#ffffff
    style D fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style E fill:#f59e0b,stroke:#b45309,color:#ffffff

Приклад незмінності:

string original = "Hello";
string modified = original.ToUpper(); // Створює НОВИЙ рядок

Console.WriteLine($"Original: {original}");   // Output: Hello
Console.WriteLine($"Modified: {modified}");   // Output: HELLO
Наслідки для продуктивності: Якщо ви виконуєте багато операцій конкатенації в циклі, кожна ітерація створюватиме новий об'єкт у пам'яті. Для таких сценаріїв використовуйте StringBuilder.

Чому рядки незмінні?

ПеревагиПояснення
Безпека потоків (Thread Safety)Незмінні об'єкти можна безпечно використовувати в багатопоточному середовищі без синхронізації
Оптимізація пам'яті (String Interning)CLR може зберігати лише одну копію ідентичних літералів
Безпека (Security)Рядки часто використовуються для зберігання паролів, шляхів файлів — незмінність захищає від модифікації
HashabilityНезмінні об'єкти можуть бути ефективно використані як ключі в Dictionary<TKey, TValue>

Архітектура та Механіка

String Literals (Літерали Рядків)

C# пропонує кілька способів оголошення рядкових літералів, кожен з яких призначений для специфічних сценаріїв.

Звичайні рядки з екрануванням (escape sequences):

string path = "C:\\Users\\Documents\\file.txt";
string multiLine = "Перший рядок\nДругий рядок";
string quote = "Він сказав: \"Привіт!\"";

Console.WriteLine(path);
Console.WriteLine(multiLine);
Console.WriteLine(quote);

:: ::

Порівняльна таблиця літералів:

Тип літералуСинтаксисЕкрануванняБагаторядковістьВипадок використання
Regular"text"Так (\\, \n, \")Ні (через \n)Прості коротки рядки
Verbatim@"text"Подвоєння ""Так (природне)Шляхи файлів, SQL-запити
Raw"""text"""НіТак (природне)JSON, XML, регулярні вирази

String Operations (Основні Операції)

C# надає багатий набір методів для маніпуляції рядками.

Concatenation (Конкатенація)

Об'єднання рядків можна виконати кількома способами:

string firstName = "John";
string lastName = "Doe";

// Спосіб 1: Оператор +
string fullName1 = firstName + " " + lastName;

// Спосіб 2: String.Concat
string fullName2 = String.Concat(firstName, " ", lastName);

// Спосіб 3: String.Join (зручно для масивів)
string[] parts = { firstName, lastName };
string fullName3 = String.Join(" ", parts);

Console.WriteLine(fullName1); // Output: John Doe
Увага: Конкатенація через + у циклі — антипаттерн! Використовуйте StringBuilder для таких сценаріїв.

Split (Розділення)

Розділення рядка на частини за роздільниками:

string csv = "John,Doe,30,Developer";

// Розділення за одним символом
string[] parts = csv.Split(',');

foreach (string part in parts)
{
    Console.WriteLine(part);
}
// Output:
// John
// Doe
// 30
// Developer

// Розділення за кількома роздільниками
string data = "apple;banana,orange|grape";
string[] fruits = data.Split(new char[] { ';', ',', '|' });

// Видалення порожніх елементів
string text = "word1  word2   word3"; // подвійні пробіли
string[] words = text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

Join (Об'єднання)

Зворотна операція до Split — об'єднання колекції в один рядок:

string[] words = { "C#", "is", "awesome" };
string sentence = String.Join(" ", words);
Console.WriteLine(sentence); // Output: C# is awesome

// Зручно для створення CSV
int[] numbers = { 1, 2, 3, 4, 5 };
string csv = String.Join(",", numbers);
Console.WriteLine(csv); // Output: 1,2,3,4,5

Replace (Заміна)

Заміна підрядків або символів:

string text = "Hello World";

// Заміна підрядка
string newText = text.Replace("World", "C#");
Console.WriteLine(newText); // Output: Hello C#

// Заміна символу
string cleaned = "a-b-c-d".Replace('-', '_');
Console.WriteLine(cleaned); // Output: a_b_c_d

// Видалення символів (заміна на порожній рядок)
string noSpaces = "Hello World".Replace(" ", "");
Console.WriteLine(noSpaces); // Output: HelloWorld

Substring (Вирізання Підрядка)

Витягування частини рядка:

string text = "Hello, World!";

// Substring(startIndex, length)
string sub1 = text.Substring(0, 5);  // "Hello"
string sub2 = text.Substring(7, 5);  // "World"

// З певного індексу до кінця
string sub3 = text.Substring(7);     // "World!"

Console.WriteLine(sub1);
Console.WriteLine(sub2);
Console.WriteLine(sub3);

// Сучасний спосіб (C# 8+): Range operator
string modern1 = text[0..5];    // "Hello"
string modern2 = text[7..12];   // "World"
string modern3 = text[7..];     // "World!"
Сучасний підхід: Оператор діапазону [start..end] набагато читабельніший та інтуїтивніший.

Additional String Methods

C# надає багато корисних методів для роботи з рядками. Розглянемо найпоширеніші.

string text = "Hello, World!";

// Contains - перевірка наявності підрядка
bool hasWorld = text.Contains("World");      // true
bool hasworld = text.Contains("world");      // false (case-sensitive)

// StartsWith / EndsWith
bool startsWithHello = text.StartsWith("Hello");  // true
bool endsWithMark = text.EndsWith("!");           // true
bool startsWithWorld = text.StartsWith("World");  // false

// Case-insensitive варіант (C# 5.0+)
bool containsWorld = text.Contains("world", StringComparison.OrdinalIgnoreCase);  // true

Console.WriteLine($"Contains 'World': {hasWorld}");
Console.WriteLine($"Starts with 'Hello': {startsWithHello}");
Console.WriteLine($"Ends with '!': {endsWithMark}");

Практична Реалізація

String Interpolation (Інтерполяція Рядків)

Інтерполяція — це сучасний та зручний спосіб вставки значень у рядки за допомогою префікса $.

string name = "Alice";
int age = 25;

// Старий спосіб (String.Format)
string oldWay = String.Format("Name: {0}, Age: {1}", name, age);

// Сучасний спосіб (String Interpolation)
string modernWay = $"Name: {name}, Age: {age}";

Console.WriteLine(modernWay); // Output: Name: Alice, Age: 25

Alignment та Width (Вирівнювання):

var products = new[]
{
    (Name: "Apple", Quantity: 5, Price: 0.50m),
    (Name: "Banana", Quantity: 12, Price: 0.30m),
    (Name: "Orange", Quantity: 8, Price: 0.75m)
};

// Syntax: {expression,width:format}
// width > 0: right-aligned, width < 0: left-aligned
Console.WriteLine($"|{"Product",-10}|{"Qty",5}|{"Price",8}|");
Console.WriteLine(new string('-', 27));

foreach (var product in products)
{
    Console.WriteLine($"|{product.Name,-10}|{product.Quantity,5}|{product.Price,8:C2}|");
}

// Output:
// |Product    |  Qty|   Price|
// |---------------------------|
// |Apple      |    5|   $0.50|
// |Banana     |   12|   $0.30|
// |Orange     |    8|   $0.75|

Interpolated Raw Strings (C# 11+):

Комбінація raw string literals з інтерполяцією — ідеально для JSON, XML:

string name = "John";
int age = 30;

// Один $ — звичайна інтерполяція
string json1 = $"""
    {{
        "name": "{name}",
        "age": {age}
    }}
    """;

// Багато $ дозволяє використовувати { та } як звичайні символи
string json2 = $$"""
    {
        "name": "{{name}}",
        "age": {{age}},
        "metadata": {
            "created": "2024-03-15"
        }
    }
    """;

Console.WriteLine(json2);
Кількість символів $ визначає, скільки фігурних дужок потрібно для інтерполяції. $$ означає {{variable}}, $$$ означає {{{variable}}}.

StringBuilder: Ефективна Побудова Рядків

Для сценаріїв з великою кількістю операцій конкатенації використовуйте StringBuilder.

Проблема з конкатенацією:

// ❌ ПОГАНО: кожна ітерація створює новий об'єкт string
string result = "";
for (int i = 0; i < 1000; i++)
{
    result += $"Number {i}, "; // 1000 нових об'єктів!
}
Loading diagram...
graph TD
    A[Ітерація 1] -->|створює| B["String: Number 0"]
    C[Ітерація 2] -->|створює| D["String: Number 0, Number 1"]
    B -.->|збирається GC| E[Garbage]
    E1[Ітерація 3] -->|створює| F["String: Number 0, Number 1, Number 2"]
    D -.->|збирається GC| E
    G[...] -->|1000 нових об'єктів| H["Велике навантаження на пам'ять"]

    style A fill:#64748b,stroke:#334155,color:#ffffff
    style B fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style C fill:#64748b,stroke:#334155,color:#ffffff
    style D fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style E fill:#f59e0b,stroke:#b45309,color:#ffffff
    style E1 fill:#64748b,stroke:#334155,color:#ffffff
    style F fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style G fill:#64748b,stroke:#334155,color:#ffffff
    style H fill:#f59e0b,stroke:#b45309,color:#ffffff

Рішення: StringBuilder:

using System.Text;

// ✅ ДОБРЕ: один об'єкт StringBuilder модифікується
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
    sb.Append($"Number {i}, ");
}
string result = sb.ToString();

Як StringBuilder працює під капотом

StringBuilder використовує змінюваний символьний масив (mutable character array) для зберігання даних. На відміну від незмінного string, StringBuilder модифікує існуючий буфер замість створення нових об'єктів.

Внутрішня структура:

ВластивістьОписТип
m_ChunkCharsВнутрішній масив символів (буфер)char[]
m_ChunkLengthПоточна кількість символів у буферіint
m_MaxCapacityМаксимально дозволений розмірint
CapacityПоточний розмір виділеної пам'ятіint
LengthРеальна кількість символів у рядкуint

Механізм роботи з пам'яттю:

Loading diagram...
graph TD
    Start["StringBuilder sb = new(16)"] -->|Initial State| State1["Capacity: 16<br/>Length: 0<br/>Buffer: [ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ]"]

    State1 -->|"Append('Hello')"| State2["Capacity: 16<br/>Length: 5<br/>Buffer: [ H e l l o _ _ _ _ _ _ _ _ _ _ _ ]"]

    State2 -->|"Append(' World!')"| State3["Capacity: 16<br/>Length: 12<br/>Buffer: [ H e l l o   W o r l d ! _ _ _ _ ]"]

    State3 -->|"Append(' More text...')"| Check{Length + New > Capacity?}

    Check -->|Так| Realloc["РЕАЛОКАЦІЯ<br/>New Capacity = Old × 2<br/>16 → 32"]
    Check -->|Ні| State4

    Realloc --> Copy["Копіювання в новий буфер<br/>[ H e l l o   W o r l d ! ... ]"]
    Copy --> State4["Capacity: 32<br/>Length: 27<br/>Buffer повністю заповнений"]

    State4 -->|"ToString()"| FinalString["Створює string<br/>з буферу"]

    style Realloc fill:#f59e0b,stroke:#b45309,color:#ffffff
    style Copy fill:#f59e0b,stroke:#b45309,color:#ffffff
    style Start fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style FinalString fill:#3b82f6,stroke:#1d4ed8,color:#ffffff
    style State1 fill:#64748b,stroke:#334155,color:#ffffff
    style State2 fill:#64748b,stroke:#334155,color:#ffffff
    style State3 fill:#64748b,stroke:#334155,color:#ffffff
    style Check fill:#f59e0b,stroke:#b45309,color:#ffffff
    style State4 fill:#64748b,stroke:#334155,color:#ffffff

Алгоритм збільшення ємності (Capacity):

// Псевдокод реального алгоритму StringBuilder
void EnsureCapacity(int requiredCapacity)
{
    if (requiredCapacity <= m_ChunkChars.Length)
        return; // Достатньо місця

    // Подвоєння ємності (або більше, якщо потрібно)
    int newCapacity = Math.Max(
        m_ChunkChars.Length * 2,  // Подвоєння
        requiredCapacity          // Або більше, якщо додається багато
    );

    // Перевірка максимуму
    if (newCapacity > m_MaxCapacity)
        throw new ArgumentOutOfRangeException();

    // Створення нового масиву та копіювання
    char[] newChars = new char[newCapacity];
    Array.Copy(m_ChunkChars, newChars, m_ChunkLength);
    m_ChunkChars = newChars;
}

Візуалізація росту:

ОпераціяLengthCapacityДія
new StringBuilder()016Початковий буфер 16 символів
Append("Hello")516Додавання без реалокації
Append(" World")1116Ще вміщається
Append(" and more text")2532⚠️ Реалокація: 16 → 32
Append(" even more...")3864⚠️ Реалокація: 32 → 64
Ключовий момент: Кожна реалокація коштує O(n) часу через копіювання. Якщо знаєте приблизний розмір фінального рядка, вкажіть capacity у конструкторі, щоб уникнути реалокацій.

Порівняння продуктивності:

using System.Diagnostics;
using System.Text;

void ComparePerformance(int iterations)
{
    // ❌ String concatenation
    var sw1 = Stopwatch.StartNew();
    string result1 = "";
    for (int i = 0; i < iterations; i++)
        result1 += "x";
    sw1.Stop();

    // ✅ StringBuilder без capacity
    var sw2 = Stopwatch.StartNew();
    var sb1 = new StringBuilder();
    for (int i = 0; i < iterations; i++)
        sb1.Append("x");
    string result2 = sb1.ToString();
    sw2.Stop();

    // ✅✅ StringBuilder з capacity
    var sw3 = Stopwatch.StartNew();
    var sb2 = new StringBuilder(iterations);
    for (int i = 0; i < iterations; i++)
        sb2.Append("x");
    string result3 = sb2.ToString();
    sw3.Stop();

    Console.WriteLine($"String concat:          {sw1.ElapsedMilliseconds}ms");
    Console.WriteLine($"StringBuilder:          {sw2.ElapsedMilliseconds}ms");
    Console.WriteLine($"StringBuilder+Capacity: {sw3.ElapsedMilliseconds}ms");
}

ComparePerformance(10000);
// Приблизні результати:
// String concat:          850ms  ← O(n²)
// StringBuilder:          2ms    ← O(n) з реалокаціями
// StringBuilder+Capacity: 1ms    ← O(n) без реалокацій
Оптимальна стратегія: Якщо ви знаєте, що фінальний рядок буде ~5000 символів, створіть new StringBuilder(5000). Це виключить усі реалокації та дасть максимальну продуктивність.

Основні методи StringBuilder:

var sb = new StringBuilder();

// Додавання різних типів
sb.Append("Hello ");
sb.Append(42);
sb.Append(' ');
sb.Append(true);

// AppendLine додає рядок з \n
sb.AppendLine("First line");
sb.AppendLine("Second line");

// AppendFormat (як String.Format)
sb.AppendFormat("Name: {0}, Age: {1}", "Alice", 25);

string result = sb.ToString();

Оптимізація: Initial Capacity:

// Без початкової ємності — множинні реалокації
var sb1 = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
    sb1.Append("x"); // реалокації: 16 -> 32 -> 64 -> 128...
}

// З початковою ємністю — одна алокація
var sb2 = new StringBuilder(10000);
for (int i = 0; i < 10000; i++)
{
    sb2.Append("x"); // без реалокацій!
}
Best Practice: Якщо ви знаєте приблизний розмір фінального рядка, завжди вказуйте capacity у конструкторі StringBuilder.

Коли використовувати StringBuilder?

СценарійРекомендація
2-3 конкатенаціїВикористовуйте + або інтерполяцію
Конкатенація в цикліЗавжди використовуйте StringBuilder
Динамічна побудова HTML/XML/JSONВикористовуйте StringBuilder
Складне форматування з умовамиStringBuilder

Regex Basics: Валідація та Паттерни

Регулярні вирази (Regular Expressions) — це потужний інструмент для пошуку, валідації та трансформації текстових даних.

Основи синтаксису:

\\d
Digit
Цифра [0-9]: \\d{3} = три цифри
\\w
Word Character
Буква, цифра або _: \\w+ = одне або більше слів
\\s
Whitespace
Пробіл, таб, новий рядок: \\s+ = один або більше пробілів
.
Any Character
Будь-який символ (крім \n): .+ = будь-яка послідовність
^
Start of String
Початок рядка: ^Hello = рядок починається з "Hello"
$
End of String
Кінець рядка: world$ = рядок закінчується на "world"
[ ]
Character Class
Набір символів: [abc] = a або b або c
_
0 or More
0 або більше повторень: a_ = 0 або більше 'a'
+
1 or More
1 або більше повторень: a+ = 1 або більше 'a'
?
0 or 1
0 або 1 повторення: colou?r = color або colour
{n,m}
Range
Від n до m повторень: \\d{3,5} = від 3 до 5 цифр

Приклад: Валідація Email:

using System.Text.RegularExpressions;

string[] emails = {
    "user@example.com",      // ✅ валідний
    "invalid.email",         // ❌ немає @
    "user@domain",           // ❌ немає домену верхнього рівня
    "user@domain.co.uk"      // ✅ валідний
};

// Простий паттерн для email
string pattern = @"^[\w\.-]+@[\w\.-]+\.\w{2,}$";

foreach (string email in emails)
{
    bool isValid = Regex.IsMatch(email, pattern);
    Console.WriteLine($"{email,-25} {(isValid ? "✅" : "❌")}");
}

Розшифровка паттерна:

^[\w\.-]+@[\w\.-]+\.\w{2,}$
│ │      │ │      │ │
│ │      │ │      │ └─ мінімум 2 літери (.com, .ua)
│ │      │ │      └─── крапка (екранована)
│ │      │ └────────── домен (літери, цифри, крапки, дефіси)
│ │      └─────────── символ @
│ └────────────────── ім'я користувача (літери, цифри, крапки, дефіси)
└───────────────────── початок рядка

Приклад: Валідація Телефону:

string[] phones = {
    "123-456-7890",       // ✅
    "(123) 456-7890",     // ✅
    "123.456.7890",       // ✅
    "1234567890",         // ✅
    "123-456-789",        // ❌ неповний номер
};

// Гнучкий паттерн для різних форматів
string pattern = @"^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$";

foreach (string phone in phones)
{
    bool isValid = Regex.IsMatch(phone, pattern);
    Console.WriteLine($"{phone,-20} {(isValid ? "✅" : "❌")}");
}

Опції Regex:

string text = "Hello WORLD";

// RegexOptions.IgnoreCase — ігнорує регістр
bool match1 = Regex.IsMatch(text, "world");                           // ❌ false
bool match2 = Regex.IsMatch(text, "world", RegexOptions.IgnoreCase);  // ✅ true

// RegexOptions.Multiline — ^ та $ працюють для кожного рядка
string multiline = "Line 1\nLine 2\nLine 3";
var matches = Regex.Matches(multiline, "^Line", RegexOptions.Multiline);
Console.WriteLine($"Знайдено: {matches.Count}"); // 3
Продуктивність: Регулярні вирази можуть бути повільними для великих текстів. Для критичних за продуктивністю сценаріїв розгляньте варіанти:
  • Використання compiled regex: new Regex(pattern, RegexOptions.Compiled)
  • Source Generators (C# 11+): [GeneratedRegex] attribute

Source Generator Regex (C# 11+):

using System.Text.RegularExpressions;

partial class EmailValidator
{
    [GeneratedRegex(@"^[\w\.-]+@[\w\.-]+\.\w{2,}$", RegexOptions.IgnoreCase)]
    private static partial Regex EmailPattern();

    public static bool IsValidEmail(string email)
    {
        return EmailPattern().IsMatch(email);
    }
}

// Використання
bool valid = EmailValidator.IsValidEmail("user@example.com");
Source-generated regex компілюється під час збірки, що дає максимальну продуктивність без runtime overhead.

Typical Use Cases

Сценарій 1: Парсинг CSV-файлу

string csvLine = "John,Doe,30,Developer,\"New York, NY\"";

// Проблема: простий Split не працює через кому всередині лапок
// Рішення: regex для парсингу
string pattern = ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)";
string[] fields = Regex.Split(csvLine, pattern);

foreach (string field in fields)
{
    Console.WriteLine(field.Trim('"'));
}

Сценарій 2: Генерація SQL-запиту

var users = new[] { "Alice", "Bob", "Charlie" };

var sb = new StringBuilder();
sb.AppendLine("SELECT * FROM Users");
sb.AppendLine("WHERE Name IN (");

for (int i = 0; i < users.Length; i++)
{
    sb.Append($"  '{users[i]}'");
    if (i < users.Length - 1)
        sb.Append(',');
    sb.AppendLine();
}

sb.AppendLine(");");

Console.WriteLine(sb.ToString());

Сценарій 3: Валідація українського телефону

string ukrainePhonePattern = @"^\+380\d{9}$";

string[] phones = {
    "+380501234567",    // ✅
    "+380671234567",    // ✅
    "0501234567",       // ❌ немає +380
    "+38050123456"      // ❌ недостатньо цифр
};

foreach (string phone in phones)
{
    bool isValid = Regex.IsMatch(phone, ukrainePhonePattern);
    Console.WriteLine($"{phone,-20} {(isValid ? "✅" : "❌")}");
}

Практика та Резюме

Завдання для Самостійної Роботи

Рівень 1: Базовий

Створіть програму, яка:

  1. Приймає ПІБ користувача (через Console.ReadLine)
  2. Розділяє його на ім'я, прізвище та по-батькові
  3. Виводить привітання у форматі: "Вітаємо, Ім'я Прізвище!"

Рівень 2: Середній

Напишіть функцію string CreateSlug(string title), яка:

  1. Перетворює назву статті на URL-slug
  2. Приклад: "Введення в C# програмування!" → "vvedennia-v-csharp-prohramuvannia"
  3. Видаляє спеціальні символи, замінює пробіли на дефіси, робить lowercase

Рівень 3: Просунутий

Створіть клас PasswordValidator, який перевіряє пароль на:

  1. Мінімум 8 символів
  2. Наявність великої літери
  3. Наявність цифри
  4. Наявність спеціального символу
  5. Використовуйте regex для валідації

Ключові Висновки

Незмінність рядків

Розуміння immutability критично для написання ефективного коду

StringBuilder для циклів

Використовуйте StringBuilder для багаторазових конкатенацій

Raw strings для складного тексту

Потрійні лапки """ спрощують роботу з JSON, XML, SQL

Regex для валідації

Регулярні вирази — потужний інструмент для перевірки форматів

Що далі?

Після опанування роботи з рядками ви готові до:

  • Dates & Time: Робота з датами, часовими зонами, форматування
  • Control Flow: Умовні конструкції та цикли для обробки текстових даних
  • Collections: Зберігання та маніпуляція масивами рядків
Додаткові ресурси: