String.split: примеры (JAVA)
String.split(String regex): String[]Описание метода String.split
Метод String.split применяется для разделения строки на фрагменты по заданному разделителю, заданному в виде регулярного выражения. В стандартной библиотеке определены две сигнатуры:
public String[] split(String regex)- разделение по регулярному выражению без явного ограничения количества частей (эквивалентно вызову с limit = 0).public String[] split(String regex, int limit)- разделение по регулярному выражению с указанием предельного числа элементов в возвращаемом массиве.
Параметры и поведение:
- regex - регулярное выражение, описывающее разделитель. При необходимости разделитель как литерал нужно экранировать или использовать
Pattern.quote. - limit > 0 - в результате не более limit элементов. При достижении предела оставшаяся часть строки помещается в последний элемент (она может содержать разделители).
- limit == 0 - поведение по умолчанию: результат содержит все элементы, но ведущие и внутренние пустые строки сохраняются, а конечные пустые строки удаляются.
- limit < 0 - нет ограничения по количеству элементов, все возможные пустые строки, в том числе конечные, сохраняются.
Возвращаемое значение: массив String[] с фрагментами. В редких случаях массив может быть пустым (например, при разбиении пустой строки с регулярным выражением, совпадающим со всем содержимым, или при определенных комбинациях regex и limit).
Исключения и замечания:
- Если строка, на которой вызывается метод, равна
null, возникаетNullPointerException. - Если передан некорректный шаблон регулярного выражения, возникает
PatternSyntaxException. - Каждый вызов компилирует регулярное выражение заново. При частом использовании с одним и тем же шаблоном рекомендуется заранее компилировать
Patternи использоватьpattern.split()для повышения производительности. - Если регулярное выражение содержит захватывающие группы (parentheses), то текст, соответствующий этим группам, включается в результирующий массив между частями (см. примеры далее).
Короткие примеры использования
Примеры демонстрируют обычные варианты работы с различными значениями regex и limit.
Разделение по пробелу:
String s = "one two three";
String[] parts = s.split("\\s+");
System.out.println(Arrays.toString(parts));
[one, two, three]
Разделение по точке (экранирование):
String s = "a.b.c";
String[] p = s.split("\\.");
System.out.println(Arrays.toString(p));
[a, b, c]
limit > 0: объединение остатка в последний элемент:
String s = "a,b,c,d";
String[] p = s.split(",", 3);
System.out.println(Arrays.toString(p));
[a, b, c,d]
limit == 0: удаление конечных пустых строк:
String s = "a,,";
String[] p = s.split(",", 0);
System.out.println(Arrays.toString(p));
[a]
limit < 0: сохранение конечных пустых строк:
String s = "a,,";
String[] p = s.split(",", -1);
System.out.println(Arrays.toString(p));
[a, , ]
Регулярное выражение с захватывающей группой (группы появляются в результате):
String s = "a-1-b";
String[] p = s.split("(-)" );
System.out.println(Arrays.toString(p));
[a, -, 1, -, b]
Альтернативы в Java
- Pattern.split(CharSequence) - предпочтителен при многократном использовании одного шаблона, так как регулярное выражение компилируется один раз.
- Scanner.useDelimiter - удобен при парсинге потоков и чтении токенов с различными типами, не возвращает массив сразу, а дает итеративный доступ.
- StringTokenizer - устаревший класс для простых разделителей без регулярных выражений; быстрее для простых задач, но не поддерживает regex и рекомендован к ограниченному использованию.
- Guava Splitter - удобен для цепочек фильтрации, пропуска пустых строк, тримминга и настройки поведения; не использует regex по умолчанию, но предоставляет гибкие варианты.
- Apache Commons StringUtils.split - выполняет простое разделение без regex, полезен для простых разделителей и безопасного поведения при null.
Выбор зависит от задачи: для многократного применения одного regex - Pattern; для потоковой обработки - Scanner; для удобного API с фильтрацией - Guava; для простых литералов - StringUtils или String.split с Pattern.quote.
Аналоги в других языках
Краткие сравнения с примерами работы и отличиями от Java.
PHP:
// explode (литеральный разделитель)
$parts = explode(",", "a,b,c");
print_r($parts);
// preg_split (регекс)
$parts = preg_split('/\\s+/', "one two three");
print_r($parts);
Array ( [0] => a [1] => b [2] => c ) Array ( [0] => one [1] => two [2] => three )
Особенность: explode работает с литеральным разделителем и быстрее для простых случаев, preg_split использует PCRE.
JavaScript:
const s = "a.b.c";
console.log(s.split('.'));
console.log("one two".split(/\s+/));
[ 'a', 'b', 'c' ] [ 'one', 'two' ]
Отличие: JS split принимает строку или RegExp; нет варианта limit == 0 с удалением конечных пустых элементов - есть параметр limit как положительное число.
Python:
s = "one two three"
print(s.split()) # split по пробелам, сжатие пробелов
import re
print(re.split(r"\s+", s))
['one', 'two', 'three'] ['one', 'two', 'three']
Особенность: str.split() при вызове без аргумента сжимает пробелы; для regex используется re.split.
SQL:
-- SQL Server
SELECT value FROM STRING_SPLIT('a,b,c', ',');
-- PostgreSQL
SELECT regexp_split_to_array('a,b,c', ',');
a
b
c
{a,b,c}
Отличия: реализация и порядок строк зависят от СУБД; многие реализации не гарантируют порядка (SQL Server до определённых версий).
C#:
var s = "a,b,c";
var p = s.Split(',');
// Regex.Split
var r = System.Text.RegularExpressions.Regex.Split("one two", "\\s+");
Console.WriteLine(string.Join("|", p));
a|b|c
Отличие: string.Split имеет перегрузки с удалением пустых элементов и с массивом разделителей; Regex.Split использует регулярные выражения.
Lua:
local s = "a,b,c"
local t = {}
for part in string.gmatch(s, "[^,]+") do table.insert(t, part) end
print(table.concat(t, '|'))
a|b|c
Отличие: в стандартной библиотеке нет готового split - используется pattern matching и gmatch.
Go:
import "strings"
fmt.Println(strings.Split("a,b,c", ","))
import "regexp"
r := regexp.MustCompile(`\s+`)
fmt.Println(r.Split("one two", -1))
[a b c] [one two]
Отличие: strings.Split использует литерал, regexp.Split использует компилируемый regex; поведение limit аналогично Java (в Go вторым аргументом -1 сохраняет все элементы).
Kotlin:
val s = "a,b,c"
println(s.split(","))
println(s.split(Regex(","), limit = 2))
[a, b, c] [a, b,c]
Отличие: Kotlin предоставляет перегрузки с Regex и удобные коллекции; семантика limit близка к Java.
Типичные ошибки и их проявления
- Неэкранированный специальный символ regex - ожидание разделения по точке, но используется ситуация с любым символом:
String s = "a.b.c"; String[] p = s.split("."); System.out.println(Arrays.toString(p));[] // неожиданное поведение: регулярное выражение "." соответствует любому символу, результат пустой строкой-частями
Рекомендация: использовать "\\." илиPattern.quote("."). - Непонимание поведения limit - удаление конечных пустых строк при limit == 0 или объединение остатка при limit > 0:
System.out.println(Arrays.toString("a,,".split(",", 0))); System.out.println(Arrays.toString("a,,".split(",", -1)));[a] [a, , ]
- NullPointerException при вызове на null:
String s = null; s.split(",");Exception in thread "main" java.lang.NullPointerException
- Игнорирование затрат на компиляцию regex при частых вызовах - заметное снижение производительности. Пример замены на
Pattern.compile(...)и повторное использование: - Неожиданное включение текстов захваченных групп в результат при использовании округляющих скобок:
String s = "a-1-b"; System.out.println(Arrays.toString(s.split("(-)")));[a, -, 1, -, b]
Это иногда вызывает лишние элементы в массиве.
Изменения и история
Метод String.split присутствует в Java с версии 1.4 и опирается на API регулярных выражений (класс Pattern). С течением времени сам метод существенно не менялся. Основные улучшения косвенно связаны с оптимизациями в движке регулярных выражений и с добавлением удобных альтернатив в более поздних версиях Java (например, String.lines() в Java 11 для разделения по строкам). В целом семантика split и значение параметра limit остались стабильными.
Расширенные и редкие сценарии использования
Ниже примеры, показывающие более тонкие моменты и советы по применению.
1) Повторное использование компилированного шаблона для производительности:
Pattern p = Pattern.compile("\\s+");
for (String line : lines) {
String[] parts = p.split(line); // быстрее, чем line.split("\\s+") в цикле
}
(нет текстового вывода, экономия на компиляции регулярных выражений)
2) Включение разделителя в результат через захватывающие группы:
String s = "key1=1;key2=2";
String[] p = s.split("([=;])");
System.out.println(Arrays.toString(p));
[key1, =, 1, ;, key2, =, 2]
Полезно при необходимости сохранить разделитель или его тип между токенами.
3) Ограниченное разбиение при необходимости получить заголовок и тело:
String s = "Subject: Title\r\n\r\nBody with \r\nlines";
String[] parts = s.split("\r\n\r\n", 2);
System.out.println("Header: " + parts[0]);
System.out.println("Body: " + parts[1]);
Header: Subject: Title Body: Body with lines
4) Парсинг чисел и преобразование в поток:
String s = "1, 2, 3,4";
int[] nums = Arrays.stream(s.split(",\\s*"))
.mapToInt(Integer::parseInt).toArray();
System.out.println(Arrays.toString(nums));
[1, 2, 3, 4]
5) Разбиение с использованием lookaround для сохранения разделителя в виде метки, но без его включения в результаты:
String s = "a; b, c|d";
// разделить, сохранив разделители как границы, но не включая их
String[] parts = s.split("(?<=[;,|])\\s*|\\s*(?=[;,|])");
System.out.println(Arrays.toString(parts));
[a;, b,, c|, d]
Композиция lookbehind / lookahead позволяет контролировать, какие символы остаются рядом с фрагментами.
6) Разделение по Unicode-классам, например по всем пунктуационным символам:
String s = "word1,word2;word3.word4";
String[] parts = s.split("\\p{Punct}+");
System.out.println(Arrays.toString(parts));
[word1, word2, word3, word4]
7) Осторожность при попытке распарсить CSV с помощью простого split - пример ошибки и рекомендация:
String csv = "a,\"b,b\",c";
System.out.println(Arrays.toString(csv.split(",")));
// Неправильно для полей в кавычках, лучше использовать CSV-парсер из библиотеки
[a, "b, b", c] // на самом деле split даст [a, "b, b", c] только случайно; в сложных случаях будут ошибки
Замечание: для корректного разбора CSV с кавычками и экранированием рекомендуется использовать специализированные парсеры (OpenCSV, Apache Commons CSV).
8) Комбинация split и Stream API для сложных преобразований (фильтрация, приведение типов, агрегация):
String s = " 1 , , 2 , 3 , " ;
List nums = Arrays.stream(s.split(","))
.map(String::trim)
.filter(t -> !t.isEmpty())
.map(Integer::valueOf)
.collect(Collectors.toList());
System.out.println(nums);
[1, 2, 3]
9) Разбиение с сохранением пустых токенов и последующая обработка:
String s = ",a,,b,"
String[] p = s.split(",", -1);
System.out.println(Arrays.toString(p));
// Обработка: замена пустых значений на null
for (int i=0;i<p.length;i++) if (p[i].isEmpty()) p[i] = null;
System.out.println(Arrays.toString(p));
[, a, , b, ] [null, a, null, b, null]
10) Использование split в рекурсивных или потоковых алгоритмах: при глубоких разбиениях лучше учитывать затраты на создание массивов и память; при необходимости использовать итеративные подходы или потоковую обработку с Matcher.