2026-02-05 11:42:52 +08:00

303 lines
9.3 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright 2026 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile StringUtils.kt
* LastUpdate 2026-02-05 11:05:33
* UpdateUser MingLiPro
*/
@file:JvmName("StringUtils")
package com.mingliqiye.utils.string
import com.mingliqiye.utils.base.BASE16
import com.mingliqiye.utils.logger.MingLiLoggerFactory
import com.mingliqiye.utils.objects.isNull
import java.net.URLDecoder
import java.net.URLEncoder
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
private val log = MingLiLoggerFactory.getLogger(Class.forName("com.mingliqiye.utils.string.StringUtils"))
val NULLISH_STRINGS = setOf("null", "NaN", "undefined", "None", "none")
/**
* 判断`字符串`是否为空
*
* @param str 待判断的字符串
* @return `true`: 空 `false`: 非空
*/
@OptIn(ExperimentalContracts::class)
@JvmName("isEmpty")
fun String?.isNullish(): Boolean {
contract {
returns(false) implies (this@isNullish != null)
}
return this.isNullOrBlank() || this in NULLISH_STRINGS
}
@JvmName("__Formatde")
fun String.formatd(vararg args: Any) = com.mingliqiye.utils.string.format(this, *args)
/**
* 格式化字符串,将字符串中的占位符{}替换为对应的参数值
*
* `Kotlin`语言给我老老实实用`$`啊
*
* @param str 需要格式化的字符串,包含{}占位符 \\{} 代表一个{}
* @param args 要替换占位符的参数列表
* @return 格式化后的字符串
*/
fun format(str: String, vararg args: Any?): String {
var argIndex = 0
val result = StringBuilder()
var lastIndex = 0
// 匹配所有非转义的 {}
val pattern = Regex("(?<!\\\\)\\{}")
val matches = pattern.findAll(str)
for (match in matches) {
// 添加匹配前的文本
result.append(str, lastIndex, match.range.first)
// 替换占位符
if (argIndex < args.size) {
result.append(args[argIndex].toString())
argIndex++
}
lastIndex = match.range.last + 1
}
// 添加剩余文本
if (lastIndex < str.length) {
result.append(str, lastIndex, str.length)
}
// 处理转义的 \\{}(替换为 {}
val finalResult = result.toString().replace("\\{}", "{}")
// 检查参数数量
val placeholderCount = matches.count()
if (placeholderCount != args.size) {
log.warn("Placeholder count: $placeholderCount, Argument count: ${args.size}")
log.warn("template : $str")
log.warn(
"Arguments : [${
", ".join(args) {
if (it.isNull()) return@join "null:null"
"${it.javaClass.simpleName}:$it"
}
}]")
}
return finalResult
}
/**
* 将字符串转换为Unicode编码格式
*
* @param str 待转换的字符串
* @return 转换后的Unicode编码字符串每个字符都以\\u开头的十六进制形式表示
*/
fun stringToUnicode(str: String): String {
val sb = java.lang.StringBuilder()
val c = str.toCharArray()
for (value in c) {
sb.append(stringToUnicode(value))
}
return sb.toString()
}
/**
* 将字符转换为Unicode转义字符串
*
* @param c 需要转换的字符
* @return 返回格式为"\\uXXXX"的Unicode转义字符串其中XXXX为字符的十六进制Unicode码点
*/
fun stringToUnicode(c: Char): String {
return "\\u" + String.format("%04x", c.code)
}
/**
* 将整数转换为Unicode字符串表示形式
*
* @param c 要转换的整数表示Unicode码点
* @return 返回格式为"\\uXXXX"的Unicode字符串其中XXXX是参数c的十六进制表示
*/
fun stringToUnicode(c: Int): String {
return "\\u" + Integer.toHexString(c)
}
/**
* 将Unicode编码字符串转换为普通字符串
*
* 该函数接收一个包含Unicode转义序列的字符串将其解析并转换为对应的字符序列
*
* @param unicode 包含Unicode转义序列的字符串格式如"\\uXXXX"其中XXXX为十六进制数
* @return 转换后的普通字符串包含对应的Unicode字符
*/
fun unicodeToString(unicode: String): String {
val sb = java.lang.StringBuilder()
// 按照Unicode转义符分割字符串得到包含十六进制编码的数组
val hex: Array<String?> = unicode.split("\\\\u".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
// 从索引1开始遍历因为分割后的第一个元素是转义符前面的内容可能为空
for (i in 1..<hex.size) {
// 将十六进制字符串转换为整数作为字符的Unicode码点
val index = hex[i]!!.toInt(16)
// 将Unicode码点转换为对应字符并添加到结果中
sb.append(index.toChar())
}
return sb.toString()
}
/**
* 创建一个指定初始容量的StringBuilder实例
*
* @param i StringBuilder的初始容量
* @return 指定初始容量的StringBuilder实例
*/
fun stringBuilder(i: Int): java.lang.StringBuilder {
return StringBuilder(i)
}
/**
* 根据当前字符串创建一个StringBuilder实例
*
* @return 包含当前字符串内容的StringBuilder实例
*/
fun String.stringBuilder(): java.lang.StringBuilder {
return StringBuilder(this)
}
/**
* 将字符串按照指定分隔符进行分割
* @param str 需要分割的字符串
* @param separator 分割符
* @return 分割后的字符串列表
*/
fun split(str: String, separator: String): List<String> {
return str.split(separator)
}
/**
* 将列表中的元素使用指定分隔符连接成字符串
* @param separator 连接分隔符
* @param getstring 转换函数将列表元素转换为字符串默认使用toString()方法
* @return 连接后的字符串
*/
fun <T> List<T>.join(separator: String, getstring: (T) -> String = { it.toString() }): String {
// 使用StringBuilder构建结果字符串
val sb = StringBuilder()
for (i in this.indices) {
sb.append(this[i])
// 除了最后一个元素外,都在后面添加分隔符
if (i != this.size - 1) {
sb.append(separator)
}
}
return sb.toString()
}
/**
* 使用当前字符串作为分隔符,将列表中的元素连接成字符串
* @param list 需要连接的元素列表
* @param getstring 转换函数将列表元素转换为字符串默认使用toString()方法
* @return 连接后的字符串
*/
fun <T> String.join(list: List<T>, getstring: (T) -> String = { it.toString() }): String {
// 使用StringBuilder构建结果字符串
val sb = StringBuilder()
for (i in list.indices) {
sb.append(getstring(list[i]))
// 除了最后一个元素外,都在后面添加当前字符串作为分隔符
if (i != list.size - 1) {
sb.append(this)
}
}
return sb.toString()
}
fun <T> String.join(array: Array<T>, getstring: (T) -> String = { it.toString() }): String {
// 使用StringBuilder构建结果字符串
val sb = StringBuilder()
for (i in array.indices) {
sb.append(getstring(array[i]))
// 除了最后一个元素外,都在后面添加当前字符串作为分隔符
if (i != array.size - 1) {
sb.append(this)
}
}
return sb.toString()
}
fun String?.parserTemplate(template: String): List<String>? {
if (this == null) return null
val regex: Regex = Regex(
"^" +
template
.replace("\\", "\\\\")
.replace("(", "\\(")
.replace(")", "\\)")
.replace("[", "\\[")
.replace("]", "\\]")
.replace("+", "\\+")
.replace("=", "\\=")
.replace("{}", "((?s).*)")
.toRegex() +
"$"
)
val datas = regex.find(this)?.groupValues ?: return null
return List(datas.size - 1) {
datas[it + 1]
}
}
fun String.urlEncode() = URLEncoder.encode(this, Charsets.UTF_8.name())
fun String.urlDecode() = URLDecoder.decode(this, Charsets.UTF_8.name())
fun String.hmacSHA256String(keyS: String): String {
val instance = Mac.getInstance("HmacSHA256")
val key = keyS.toByteArray()
instance.init(SecretKeySpec(key, 0, key.size, "HmacSHA256"))
val bytes = instance.doFinal(this.toByteArray())
return BASE16.encode(bytes)
}
fun String.md5String(): String {
val instance = MessageDigest.getInstance("MD5")
instance.update(this.toByteArray())
val bytes = instance.digest()
return BASE16.encode(bytes)
}
fun String.hmacMd5String(keyS: String): String {
val instance = Mac.getInstance("HmacMD5")
val key = keyS.toByteArray()
instance.init(SecretKeySpec(key, 0, key.size, "HmacSHA256"))
val bytes = instance.doFinal(this.toByteArray())
return BASE16.encode(bytes)
}