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: Зберігання та маніпуляція масивами рядків
Додаткові ресурси: