Hytale Modding
Основи Java

09 - Робота з рядками

Опануйте маніпулювання текстом та операції з рядками в Java.

Рядки - один з найпоширеніших типів у Java. Вони необхідні для обробки імен гравців, повідомлень у чаті, описів предметів та всього, що ви бачите.

Основи рядків

Рядки - це об'єкти, що зберігають текст:

String name = "Саймон";
String message = "Ласкаво просимо до Hytale!";
String empty = "";
Строки є незмінними

Після створення строку її неможливо змінити. Методи, які, здавалося б, модифікують строку, насправді створюють нову!

String text = "Привіт";
text.toUpperCase();           // Створює "ПРИВІТ", але не зберігає її
System.out.println(text);     // Все ще "Привіт"

String upper = text.toUpperCase();  // ✓ Зберегти результат
System.out.println(upper);          // "ПРИВІТ"

Методи рядків

Довжина

String name = "Анна";
int length = name.length();
System.out.println(length);  // 4

Зміна регістру

String text = "Привіт світ";
String upper = text.toUpperCase();  // "ПРИВІТ СВІТ"
String lower = text.toLowerCase();  // "привіт світ"

Перевірка вмісту

String message = "Ласкаво просимо до Hytale";

boolean hasHytale = message.contains("Hytale");  // true
boolean hasMinecraft = message.contains("Minecraft");  // false

boolean starts = message.startsWith("Ласкаво");  // true
boolean ends = message.endsWith("Hytale");  // true

Витягування частин

String text = "Привіт світ";

char first = text.charAt(0);      // 'П'
char last = text.charAt(10);      // 'т'

String sub1 = text.substring(0, 6);   // "Привіт"
String sub2 = text.substring(7);      // "світ"
Індекси підрядків

substring(start, end) включає start, але виключає end:

String text = "Hytale";
//             012345 (індекси)

text.substring(0, 2);  // "Hy" (індекси 0 і 1)
text.substring(2, 6);  // "tale" (індекси 2, 3, 4, 5)
text.substring(2);     // "tale" (від 2 до кінця)

Заміна тексту

String text = "Я люблю Minecraft";
String replaced = text.replace("Minecraft", "Hytale");
System.out.println(replaced);  // "Я люблю Hytale"

String noSpaces = "Привіт Світ".replace(" ", "");
System.out.println(noSpaces);  // "ПривітСвіт"

Обрізання пробілів

String messy = "  Привіт світ  ";
String clean = messy.trim();
System.out.println(clean);  // "Привіт світ" (без пробілів на початку та в кінці)

Розділення рядків

String command = "give player sword 5";
String[] parts = command.split(" ");

System.out.println(parts[0]);  // "give"
System.out.println(parts[1]);  // "player"
System.out.println(parts[2]);  // "sword"
System.out.println(parts[3]);  // "5"

Порівняння рядків

Ніколи не використовуйте == для рядків

Завжди використовуйте .equals() для порівняння вмісту рядків!

String name1 = "Стів";
String name2 = "Стів";

// Неправильно - порівнює посилання на об'єкти
if (name1 == name2) {
    System.out.println("Те саме");
}

// Правильно - порівнює фактичний текст
if (name1.equals(name2)) {
    System.out.println("Те саме");
}

// Ігнорувати регістр при порівнянні
if (name1.equalsIgnoreCase("стів")) {
    System.out.println("Те саме (без урахування регістру)");
}

Порядок порівняння

String a = "Apple";
String b = "Banana";

int result = a.compareTo(b);
// результат < 0, якщо a йде перед b
// результат == 0, якщо a дорівнює b
// результат > 0, якщо a йде після b

if (a.compareTo(b) < 0) {
    System.out.println(a + " йде перед " + b);
}

Об'єднання рядків

Використання оператора +

String first = "Привіт";
String second = "світ";
String combined = first + " " + second;  // "Привіт світ"

int score = 100;
String message = "Рахунок: " + score;  // "Рахунок: 100"

Використання concat()

String result = "Привіт".concat(" світ");  // "Привіт світ"

Створення довгих рядків

Для багатьох конкатенацій використовуйте StringBuilder:

StringBuilder builder = new StringBuilder();
builder.append("Гравець: ");
builder.append("Анна");
builder.append(", Рівень: ");
builder.append(10);
builder.append(", Здоров'я: ");
builder.append(100);

String result = builder.toString();
System.out.println(result);
// "Гравець: Анна, Рівень: 10, Здоров'я: 100"
Чому StringBuilder?

Звичайне об'єднання створює багато тимчасових рядків:

// Неефективно - створює багато тимчасових рядків
String result = "";
for (int i = 0; i < 1000; i++) {
    result = result + i;  // Створює 1000 тимчасових рядків!
}

// Ефективно - StringBuilder є змінюваним
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);  // Змінює один об'єкт
}
String result = sb.toString();

Використовуйте StringBuilder під час побудови рядків у циклах!

Практичні приклади

Аналіз команди гравця

public class CommandParser {
    public static void parseCommand(String command) {
        // "/give Steve diamond_sword 1"
        String[] parts = command.split(" ");
        
        String action = parts[0].replace("/", "");  // "give"
        String player = parts[1];                   // "Steve"
        String item = parts[2];                     // "diamond_sword"
        int amount = Integer.parseInt(parts[3]);    // 1
        
        System.out.println("Дія: " + action);
        System.out.println("Гравець: " + player);
        System.out.println("Предмет: " + item);
        System.out.println("Кількість: " + amount);
    }
    
    public static void main(String[] args) {
        parseCommand("/give Steve diamond_sword 1");
    }
}

Оформлення імені гравця

public class PlayerFormatter {
    public static String formatName(String name, int level, String rank) {
        StringBuilder formatted = new StringBuilder();
        
        if (rank != null && !rank.isEmpty()) {
            formatted.append("[").append(rank).append("] ");
        }
        
        formatted.append(name);
        formatted.append(" (Рів. ").append(level).append(")");
        
        return formatted.toString();
    }
    
    public static void main(String[] args) {
        String display1 = formatName("Анна", 10, "VIP");
        System.out.println(display1);  // "[VIP] Анна (Рів. 10)"
        
        String display2 = formatName("Богдан", 5, null);
        System.out.println(display2);  // "Богдан (Рів. 5)"
    }
}

Перевірка імені користувача

public class UsernameValidator {
    public static boolean isValid(String username) {
        // Правила: 3-16 символів, тільки літери та цифри
        
        if (username == null || username.isEmpty()) {
            return false;
        }
        
        username = username.trim();
        
        if (username.length() < 3 || username.length() > 16) {
            return false;
        }
        
        for (int i = 0; i < username.length(); i++) {
            char c = username.charAt(i);
            if (!Character.isLetterOrDigit(c)) {
                return false;
            }
        }
        
        return true;
    }
    
    public static void main(String[] args) {
        System.out.println(isValid("Steve"));      // true
        System.out.println(isValid("Player_123")); // false (містить підкреслення)
        System.out.println(isValid("ab"));         // false (занадто коротке)
        System.out.println(isValid(""));           // false (порожнє)
    }
}

Конструктор опису предмета

public class ItemDescription {
    public static String buildDescription(String name, String rarity, 
                                         int damage, int durability) {
        StringBuilder desc = new StringBuilder();
        
        // Заголовок з кодом кольору рідкісності
        desc.append(getRarityColor(rarity));
        desc.append(name);
        desc.append("\n");
        
        // Показники
        desc.append("Шкода: ").append(damage).append("\n");
        desc.append("Міцність: ").append(durability).append("/");
        desc.append(durability).append("\n");
        desc.append("Рідкісність: ").append(rarity);
        
        return desc.toString();
    }
    
    private static String getRarityColor(String rarity) {
        switch (rarity.toLowerCase()) {
            case "легендарна": return "§6";  // Золотий
            case "епічна": return "§5";       // Фіолетовий
            case "рідкісна": return "§9";       // Синій
            case "звичайна": return "§f";     // Білий
            default: return "§7";           // Сірий
        }
    }
    
    public static void main(String[] args) {
        String desc = buildDescription("Екскалібур", "Легендарна", 50, 1000);
        System.out.println(desc);
    }
}

Фільтр повідомлень чату

public class ChatFilter {
    private static String[] bannedWords = {"поганеслово1", "поганеслово2"};
    
    public static String filterMessage(String message) {
        String filtered = message.toLowerCase();
        
        for (String word : bannedWords) {
            if (filtered.contains(word)) {
                String replacement = "*".repeat(word.length());
                filtered = filtered.replace(word, replacement);
            }
        }
        
        return filtered;
    }
    
    public static void main(String[] args) {
        String message = "Це тест поганеслово1";
        String clean = filterMessage(message);
        System.out.println(clean);  // "Це тест ************"
    }
}

Форматування рядків

Використання String.format()

String name = "Анна";
int level = 10;
double health = 85.5;

String formatted = String.format("%s має рівень %d і %.1f%% здоров'я", 
                                 name, level, health);
System.out.println(formatted);
// "Анна має рівень 10 і 85.5% здоров'я"
Позначення формату

Загальні коди формату:

  • %s - рядок
  • %d - ціле число
  • %f - число з плаваючою комою
  • %.2f - число з плаваючою комою з 2 десятковими знаками
  • %n - новий рядок (незалежно від платформи)
String.format("Ім'я: %s", "Стів");            // "Ім'я: Стів"
String.format("Рівень: %d", 10);              // "Рівень: 10"
String.format("Здоров'я: %.1f", 85.5);        // "Здоров'я: 85.5"
String.format("Позиція: (%d, %d, %d)", 
              10, 64, -5);                    // "Позиція: (10, 64, -5)"

Загальні операції з рядками

Перевірка, чи рядок порожній

String text = "";

// Перевірка на порожній або нульовий
if (text == null || text.isEmpty()) {
    System.out.println("Порожній!");
}

// Перевірка на порожній, нульовий або тільки пробіл
if (text == null || text.trim().isEmpty()) {
    System.out.println("Порожній або пробіл!");
}

Підрахунок входжень

public static int countOccurrences(String text, String target) {
    int count = 0;
    int index = 0;
    
    while ((index = text.indexOf(target, index)) != -1) {
        count++;
        index += target.length();
    }
    
    return count;
}

// Використання
int count = countOccurrences("привіт привіт світ", "привіт");
System.out.println(count);  // 2

Зворотний рядок

public static String reverse(String text) {
    StringBuilder sb = new StringBuilder(text);
    return sb.reverse().toString();
}

// Використання
String reversed = reverse("Привіт");
System.out.println(reversed);  // "тівирП"

Перевірка на паліндром

public static boolean isPalindrome(String text) {
    String cleaned = text.toLowerCase().replaceAll("[^a-z0-9]", "");
    String reversed = new StringBuilder(cleaned).reverse().toString();
    return cleaned.equals(reversed);
}

// Використання
System.out.println(isPalindrome("racecar"));     // true
System.out.println(isPalindrome("hello"));       // false

Практичні вправи

  1. Перевірка імені користувача: Напишіть метод, який перевіряє, чи ім'я користувача:

    • Складається з 3-16 символів
    • Містить тільки літери, цифри та символи підкреслення
    • Не починається з цифри
    • Повертає true, якщо ім'я є допустимим, і false в іншому випадку
  2. Аналізатор команд: Проаналізуйте цей формат команди: /teleport x y z

    • Витягніть координати
    • Перетворіть їх на цілі числа
    • Поверніть масив із трьох значень
  3. Форматувальник чату: Створіть метод, який форматує повідомлення в чаті:

    • Вхідні дані: ім'я гравця, ранг, повідомлення
    • Вихідні дані: "[RANK] PlayerName: message"
    • Якщо рангу немає, просто покажіть "PlayerName: message"
  4. Лічильник слів: Порахуйте, скільки слів у реченні (слова розділені пробілами)

  5. Великі літери: Перетворіть рядок у форму заголовка:

    • Вхідні дані: "hello world"
    • Вихідні дані: "Hello World"