1. 概述

本文将介绍在 Kotlin 中如何将字符串(String)按指定规则拆分为多个部分。我们会覆盖常见的分隔符拆分、正则表达式拆分、懒加载拆分以及结果转数组等实用场景。这些技巧在日常开发中非常常见,尤其处理 CSV、日志解析或协议文本时极易踩坑,掌握它们能大幅提升编码效率和健壮性。

2. 按分隔符拆分

Kotlin 提供了简洁而强大的 split() 扩展函数,用于以一个或多个字符作为分隔符进行字符串拆分。

✅ 使用单个分隔符:

val info = "Name,Year,Location"
assertThat(info.split(",")).containsExactly("Name", "Year", "Location")

这会按照逗号 , 将字符串切分成列表,行为符合预期。

✅ 支持多个分隔符同时使用:

val info = "Name,Year,Location/Time"
assertThat(info.split(",", "/")).containsExactly("Name", "Year", "Location", "Time")

上面的例子中,,/ 都被视为有效分隔符,实现多符号混合切割。

✅ 可限制返回元素数量(limit 参数):

val info = "Name,Year,Location/Time/Date"
assertThat(info.split(",", limit = 2)).containsExactly("Name", "Year,Location/Time/Date")
assertThat(info.split(",", "/", limit = 4)).containsExactly("Name", "Year", "Location", "Time/Date")

当设置 limit = 2 时,最多只产生两个元素 —— 第二个元素包含剩余所有未再拆分的内容。这个特性在需要保留部分原始结构时特别有用。

✅ 支持忽略大小写拆分:

val info = "127.0.0.1aaFirefoxAA58"
assertThat(info.split("aa", ignoreCase = true)).containsExactly("127.0.0.1", "Firefox", "58")

通过 ignoreCase = true 实现不区分大小写的匹配,避免因格式不统一导致解析失败。

✅ 使用换行符拆分:lines() 函数

除了自定义分隔符,Kotlin 还提供了专门处理文本行的扩展函数 lines()

val info = "First line\nsecond line\rthird"
assertThat(info.lines()).containsExactly("First line", "second line", "third")

该方法自动识别 \n\r\r\n 三种常见换行符,适合读取文件或多行输入的场景。

2.1 懒加载拆分(Lazy Split)

⚠️ 注意:所有 split() 的变体返回的都是 List<String> —— 即 立即执行并生成完整集合。如果源字符串非常大,可能会造成内存浪费。

为此,Kotlin 提供了 splitToSequence(),它返回一个 Sequence<String>,支持惰性求值:

val info = "random_text,".repeat(1000)
assertThat(info.splitToSequence(",").first()).isEqualTo("random_text")

在这个例子中,尽管原字符串包含上千个片段,但 splitToSequence() 并不会立刻创建 1000 个元素的列表,而是像迭代器一样按需计算。只有当你调用 .first() 时才会触发第一个元素的拆分。

✅ 优势总结:

  • 内存友好,适用于超长字符串或流式处理
  • 在链式操作中避免中间集合的创建,提升性能
  • 与 Kotlin 的 Sequence API 完美集成

例如:

info.splitToSequence(",")
    .map { it.uppercase() }
    .filter { it.startsWith("R") }
    .take(5)
    .toList()

上述操作在整个序列上是“流水线式”执行的,不会生成多余中间列表。

3. 使用正则表达式拆分

除了字面量分隔符,我们还可以使用正则表达式来定义更复杂的拆分逻辑。

✅ 示例:提取连续数字

val info = "28 + 32 * 2 / 64 = 29"
val regex = "\\D+".toRegex() // 匹配非数字序列
assertThat(info.split(regex)).containsExactly("28", "32", "2", "64", "29")

这里用 \\D+ 表示任意长度的非数字字符作为分隔符,从而提取出所有独立的数值。

✅ 也可以直接传入 Java 的 Pattern 对象:

val pattern = Pattern.compile("\\D+")
assertThat(info.split(pattern)).containsExactly("28", "32", "2", "64", "29")

✅ 同样支持 limit 参数:

assertThat(info.split(regex, 3)).containsExactly("28", "32", "2 / 64 = 29")
assertThat(info.split(pattern, 3)).containsExactly("28", "32", "2 / 64 = 29")

⚠️ 常见踩坑点:从 Java 转向 Kotlin 的误区

很多熟悉 Java 的开发者容易犯一个错误:把正则表达式字符串直接传给 split()

比如我们要按连续空白字符拆分:

val info = "a b    c      d"

❌ 错误写法(Java 风格照搬):

assertThat(info.split("\\s+")).containsExactly(info) // ❌ 实际没拆开!

原因:这种方式会被当作字面量字符串 "\\s+" 来匹配,而原字符串中并不存在这样的子串,因此整个字符串被原样保留。

✅ 正确做法:必须显式构造 Regex 对象:

assertThat(info.split(Regex("\\s+"))).containsExactly("a", "b", "c", "d")
// 或更简洁:
assertThat(info.split("\\s+".toRegex())).containsExactly("a", "b", "c", "d")

📌 记住口诀:Kotlin 中用正则拆分,一定要转成 Regex 类型,不能直接传字符串。

4. 拆分结果转数组

默认情况下,split() 返回的是 ArrayList<String>

val fruits = "apple,banana,grapes,orange"
val fruitsArrayList = fruits.split(",")
assertEquals("ArrayList", fruitsArrayList::class.simpleName)

但某些场景下我们需要的是 Array<String>,比如调用 Java 接口、反射或 vararg 参数传递。

✅ 解决方案一:使用 toTypedArray()

val fruitsArray = fruitsArrayList.toTypedArray()
assertEquals("Array", fruitsArray::class.simpleName)
assertEquals(4, fruitsArray.size)
assertEquals("apple", fruitsArray[0])
assertEquals("banana", fruitsArray[1])
assertEquals("grapes", fruitsArray[2])
assertEquals("orange", fruitsArray[3])

这是最常用也最推荐的方式,代码清晰且性能良好。

✅ 解决方案二:使用 Array() 构造器手动构建

val fruitsArray = Array(fruitsArrayList.size) { i -> fruitsArrayList[i] }
assertEquals(4, fruitsArray.size)
assertEquals("apple", fruitsArray[0])
// ...其余断言同上

虽然可行,但语法略显冗余,除非有特殊索引处理需求,否则建议优先使用 toTypedArray()

5. 总结

本文系统梳理了 Kotlin 中字符串拆分的核心方法:

  • split(delimiter):基础拆分,支持多分隔符、limit 和 ignoreCase
  • lines():专为换行设计,兼容多种换行符
  • splitToSequence():懒加载拆分,节省内存,适合大数据量场景
  • split(regex):支持正则表达式,注意必须传 Regex 对象而非字符串
  • toTypedArray():轻松将 List 转为 Array,满足 Java 互操作需求

📌 最佳实践建议:

  • 日常简单拆分用 split()
  • 大文本或链式操作优先考虑 splitToSequence()
  • 正则拆分务必使用 .toRegex()Regex(...)
  • 输出为数组时记得调用 toTypedArray()

所有示例代码均已上传至 GitHub:
👉 https://github.com/baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-strings


原始标题:Splitting a String in Kotlin