字符串操作:字符串模板、多行字符串、字符串常用函数
字符串操作,说白了就是 Kotlin 里最基础也最常用的活儿。我做了这么多年项目,几乎每个类都离不开字符串处理。今天咱们就把字符串模板、多行字符串,还有 split、replace、正则这几个高频函数彻底聊透。
1. 字符串模板:不只是拼接
Java 时代拼字符串有多痛苦?我记得刚转 Kotlin 那会儿,看到字符串模板简直想哭——终于不用写一堆加号和 String.format 了。
val name = "Kotlin"
val version = 1.9
// 简单模板
println("Hello, $name!") // Hello, Kotlin!
// 表达式模板
println("${name.uppercase()} version ${version + 0.1}") // KOTLIN version 2.0
我的习惯:能用模板就别用拼接。模板里写复杂表达式时,记得用花括号包起来,可读性会好很多。
你想想看,如果模板里要调用函数或者做运算,不加花括号会怎样?编译器会直接报错。嗯,这里要注意:$name.length 是合法的,但 $name.toUpperCase() 就不行,必须写成 ${name.uppercase()}。
2. 多行字符串:告别转义噩梦
我在项目中遇到过最头疼的事,就是写 SQL 或者 JSON 时满屏的转义符。Kotlin 的三引号字符串完美解决了这个问题。
val sql = """
SELECT id, name, age
FROM users
WHERE age > 18
ORDER BY id DESC
""".trimIndent()
trimIndent() 会去掉公共前导空白,保持缩进整洁。如果你需要保留某些缩进,可以用 trimMargin() 自定义边界符。
我曾经踩过的坑:三引号字符串里不能直接写 $ 符号做模板插值?其实可以,但如果你要输出字面量的 $,就得用 ${'$'} 这种写法。说白了就是转义方式变了,但本质没变。
3. 字符串常用函数:split、replace、正则
这三个函数我几乎天天用。咱们一个一个说。
3.1 split:分割的艺术
Kotlin 的 split 比 Java 灵活得多。它可以接受多个分隔符,甚至正则表达式。
val str = "apple,banana;orange|grape"
// 多个分隔符
val fruits = str.split(",", ";", "|")
println(fruits) // [apple, banana, orange, grape]
// 限制分割次数
val parts = str.split(",", limit = 2)
println(parts) // [apple, "banana;orange|grape"]
我个人习惯用 splitToSequence 处理大字符串,它返回惰性序列,不会一次性创建所有子串,内存友好。
3.2 replace:替换的多种玩法
replace 函数有好几个重载版本。我项目中用得最多的是带 lambda 的版本,可以动态决定替换内容。
val text = "我的手机号是13812345678,他的手机号是13987654321"
// 用正则替换,隐藏中间四位
val masked = text.replace(Regex("(\\d{3})\\d{4}(\\d{4})")) {
"${it.groupValues[1]}****${it.groupValues[3]}"
}
println(masked) // 我的手机号是138****5678,他的手机号是139****4321
核心要点:replace 配合 lambda 可以做非常灵活的替换逻辑。你想想看,如果只是简单替换,用 replace(old, new) 就够了;但要做条件替换、动态替换,lambda 版本才是王道。
3.3 正则表达式:Regex 类
Kotlin 的 Regex 类比 Java 的 Pattern 好用太多。它直接提供了 matches、containsMatchIn、findAll 等方法,不用再手动创建 Matcher 对象。
val emailRegex = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
val email = "test@example.com"
// 完全匹配
println(emailRegex.matches(email)) // true
// 查找所有匹配
val log = "Error at line 42: NullPointerException\nError at line 87: IndexOutOfBoundsException"
val errorRegex = Regex("Error at line (\\d+): (\\w+)")
val results = errorRegex.findAll(log)
results.forEach { match ->
println("Line ${match.groupValues[1]}: ${match.groupValues[2]}")
}
我的建议:正则表达式尽量用原始字符串(三引号)写,少一层转义,可读性高很多。比如 Regex("""\d+""") 比 Regex("\\d+") 清爽多了。
4. 知识体系总览
下面这张图把字符串操作的核心知识点串起来了。我画图时习惯把「模板」和「多行字符串」归为「创建与格式化」,把 split、replace、正则归为「处理与转换」。
5. 实战组合拳
最后分享一个我项目里真实用过的场景:解析日志文件,提取错误信息并脱敏。
val log = """
[INFO] 2024-01-15 10:30:00 User login: user001
[ERROR] 2024-01-15 10:30:05 NullPointerException at line 42
[INFO] 2024-01-15 10:31:00 User logout: user001
[ERROR] 2024-01-15 10:32:00 IndexOutOfBoundsException at line 87
""".trimIndent()
// 提取所有 ERROR 行
val errorLines = log.split("\n")
.filter { it.startsWith("[ERROR]") }
.map { line ->
// 用正则提取异常类型和行号
val regex = Regex("""\[ERROR\] .+? (\w+Exception) at line (\d+)""")
val match = regex.find(line)
if (match != null) {
"异常: ${match.groupValues[1]}, 行号: ${match.groupValues[2]}"
} else {
"未知错误"
}
}
errorLines.forEach { println(it) }
// 输出:
// 异常: NullPointerException, 行号: 42
// 异常: IndexOutOfBoundsException, 行号: 87
你看,split 分割行,正则提取关键信息,filter 过滤,map 转换——这几个函数组合起来,处理文本数据非常顺手。
总结一下:字符串模板让拼接更优雅,多行字符串让格式化文本更干净,split、replace、正则这三个函数覆盖了 90% 的字符串处理需求。我建议你平时多练练这些组合用法,遇到实际需求时才能信手拈来。