String.charAt: примеры (JAVA)

charAt: поведение и примеры использования
Раздел: Строки (String) - базовые операции
String.charAt(int index): char

Описание метода

Метод String.charAt в Java имеет сигнатуру

public char charAt(int index)
и возвращает символ типа char из строки по указанной позиции. Индексация начинается с нуля. Возвращаемое значение представляет собой единицу UTF-16 (code unit), а не универсальный Unicode code point для символов за пределами базовой многобайтовой плоскости.

Аргументы и поведение:

  • index: целое число (int). Допустимый диапазон: 0 ≤ index < length(), где length() - длина строки в кодовых единицах UTF-16.

Возвращаемое значение:

    char. Это UTF-16 code unit (16-битный символ). Для символов вне базовой многобайтовой плоскости (например, эмодзи) метод возвращает либо верхний (high) суррогат, либо нижний (low) суррогат в зависимости от индекса.

Исключения:

  • Если index < 0 или index >= length(), бросается IndexOutOfBoundsException (в реализации строк обычно это подкласс StringIndexOutOfBoundsException) с соответствующим сообщением.

Особенности и замечания:

  • Время выполнения - константное O(1), так как внутри строка хранится как массив кодовых единиц и доступ осуществляется по индексу.
  • Строки в Java неизменяемы, поэтому доступ к символам безопасен для читателей из разных потоков без внешней синхронизации.
  • Для работы с реальными Unicode code point рекомендуется использовать методы codePointAt, codePointCount и итерацию по code points, если требуется корректная обработка суррогатных пар.

Короткие примеры

Базовый пример извлечения символа по индексу.

public class Example1 {
    public static void main(String[] args) {
        String s = "Hello";
        char c = s.charAt(1);
        System.out.println(c);
    }
}
e

Пример с выходом за пределы индекса и результатом исключения.

public class Example2 {
    public static void main(String[] args) {
        String s = "Hi";
        System.out.println(s.charAt(5));
    }
}
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 5
    at java.base/java.lang.String.checkIndex(String.java:...)
    at java.base/java.lang.String.charAt(String.java:...)
    at Example2.main(Example2.java:...)

Поведение с суррогатной парой: символ вне BMP (например, эмодзи) занимает две кодовые единицы.

public class Example3 {
    public static void main(String[] args) {
        String s = "A????B"; // эмулирует символ за пределами BMP
        System.out.println((int) s.charAt(1)); // кодовая единица (high или low суррогат)
        System.out.println((int) s.charAt(2));
    }
}
55357
56842

Альтернатива получения полного code point для позиции с учётом суррогатных пар.

public class Example4 {
    public static void main(String[] args) {
        String s = "A????B";
        int cp = s.codePointAt(1);
        System.out.println(Integer.toHexString(cp));
    }
}
1f60a

Похожие методы в Java

Короткий список методов в Java, которые связаны с получением символов и их особенностями:

  • codePointAt(int index): возвращает полный Unicode code point, корректно обрабатывает суррогатные пары. Предпочтительнее при работе с реальными Unicode символами.
  • codePoints() (Stream<Integer>): поток кодовых точек для безопасной итерации по Unicode символам.
  • toCharArray(): возвращает массив char для массовой работы с символами; полезно при необходимости модификации копии.
  • substring(int, int): получение подстроки, в том числе длиной 1 для получения строки из одного char.
  • StringBuilder.charAt(int) и StringBuffer.charAt(int): аналогичные методы для изменяемых буферов строк.

Аналоги в других языках

Краткие примечания и примеры по разным языкам с указанием отличий от Java.

PHP (байтовая индексация, для Unicode - mb_* функции):

$s = "Привет";
echo $s[2];
и (в зависимост от кодировки может быть байт, не символ)
// Для настоящих символов
echo mb_substr($s, 2, 1, 'UTF-8');
и

JavaScript (строки как последовательность UTF-16 code units; есть charAt и codePointAt):

let s = 'A????B';
console.log(s.charAt(1));
console.log(s.codePointAt(1).toString(16));
\uD83D
1f60a

Python (строки - последовательность Unicode code points):

s = 'A????B'
print(s[1])
print(ord(s[1]))
????
128522

C# (поведение похоже на Java: индекс возвращает char - UTF-16 code unit):

string s = "A????B";
Console.WriteLine((int)s[1]);
55357

Go (строка - последовательность байтов; для rune/Unicode нужно преобразование):

s := "A????B"
fmt.Println(s[1]) // байт
r := []rune(s)
fmt.Printf("%U\n", r[1])
(значение байта)
U+1F60A

Kotlin (аналогично Java, Char - UTF-16):

val s = "A????B"
println(s[1].code)
55357

Lua (интерфейс по умолчанию - байтовые подстроки; для Unicode - библиотека utf8):

s = "A????B"
print(string.sub(s, 2, 2)) -- индекс 1-based (байты)
print(utf8.codepoint(s, 2)) -- требует utf8
(часть байта)
128522

SQL (варианты зависят от СУБД):

SELECT SUBSTRING('Hello', 2, 1); -- позиция 1-based
e

Ключевые отличия: в Java и C# индекс возвращает UTF-16 единицу; в Python и некоторых языках индекс оперирует полноразмерными Unicode символами; в Go и Lua по умолчанию индекс работает с байтами, для Unicode требуется явное преобразование.

Типичные ошибки

Наиболее распространённые ошибки и примеры их проявления.

1. Выход за границы строки (IndexOutOfBounds).

public class Err1 {
    public static void main(String[] args) {
        String s = "X";
        System.out.println(s.charAt(1));
    }
}
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 1
    at java.base/java.lang.String.checkIndex(String.java:...)
    at java.base/java.lang.String.charAt(String.java:...)
    at Err1.main(Err1.java:...)

2. Ошибочная обработка символов за пределами BMP - предположение, что char содержит полный Unicode символ.

String s = "\uD83D\uDE0A"; // эмоция как суррогатная пара
char c = s.charAt(0);
System.out.println(c);
System.out.println(Character.isHighSurrogate(c));
(нечитаемый символ)
true

3. Приведение char к int без учета того, что это только кодовая единица UTF-16 и не совпадает с полным code point для суррогатных пар.

String s = "????";
System.out.println((int) s.charAt(0));
55357 // часть суррогатной пары, не 128522

4. Использование charAt при работе с байтовыми потоками или с разными кодировками без явного приведения кодировок.

Изменения и история

Метод charAt как таковой не претерпевал существенных изменений в современных версиях Java: сигнатура и поведение остаются прежними с ранних выпусков платформы. Развитие поддержки Unicode в API языка произошло через добавление методов для работы с кодовыми точками (codePointAt, codePoints и т. п.), которые появились для корректной обработки суррогатных пар и мультиязычных текстов. Депрекаций для charAt не было.

Расширенные и нестандартные примеры

1. Правильная итерация по Unicode code points с использованием offsetByCodePoints.

Пример java
public class Adv1 {
    public static void main(String[] args) {
        String s = "A????Б";
        for (int i = 0; i < s.length(); ) {
            int cp = s.codePointAt(i);
            System.out.printf("U+%X ", cp);
            i += Character.charCount(cp);
        }
    }
}
U+41 U+1F60A U+41

2. Извлечение символа как строкового значения длиной 1 с учётом surrogate pair (без утраты информации при корректной позиции).

Пример java
public class Adv2 {
    public static void main(String[] args) {
        String s = "A????B";
        int idx = s.offsetByCodePoints(0, 1); // индекс первого code point после A
        int cp = s.codePointAt(idx);
        String ch = new String(Character.toChars(cp));
        System.out.println(ch);
    }
}
????

3. Высокопроизводительная обработка символов в цикле при знании, что вход ASCII-only.

Пример java
public class Adv3 {
    public static void main(String[] args) {
        String s = "Ascii text repeated...";
        int sum = 0;
        for (int i = 0, n = s.length(); i < n; i++) {
            sum += s.charAt(i); // быстрый доступ к UTF-16 единице
        }
        System.out.println(sum);
    }
}
(целочисленная сумма кодов символов)

4. Комбинация с потоками: преобразование строки в поток кодовых точек для функциональной обработки.

Пример java
public class Adv4 {
    public static void main(String[] args) {
        String s = "A????B";
        s.codePoints().forEach(cp -> System.out.printf("U+%X ", cp));
    }
}
U+41 U+1F60A U+42

5. Реконструкция строки из массива char, когда требуется модификация отдельных кодовых единиц.

Пример java
public class Adv5 {
    public static void main(String[] args) {
        String s = "hello";
        char[] a = s.toCharArray();
        a[0] = 'H';
        String s2 = new String(a);
        System.out.println(s2);
    }
}
Hello

6. Пример поиска символов по условию с использованием charAt и индексной арифметики.

Пример java
public class Adv6 {
    public static void main(String[] args) {
        String s = "a1b2c3";
        StringBuilder digits = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (Character.isDigit(ch)) digits.append(ch);
        }
        System.out.println(digits.toString());
    }
}
123

джава String.charAt function comments

En
String.charAt Returns the char value at the specified index