问题介绍
在 Java 中进行字符串大小写转换的操作时,我们需要注意可能会遇到的一个小的低级错误:如果我们的项目有国际化的需要,大小写转换字符串时不要直接使用 toLowerCase()
和 toUpperCase()
,如下是这两个无参数的大小写转换方法的源码实现:
public String toLowerCase() {
return this.toLowerCase(Locale.getDefault());
}
public String toUpperCase() {
return this.toUpperCase(Locale.getDefault());
}
可以看到它们是使用默认的区域设置的规则将字符串中的所有字符转换为小写或大写。也就是这两个方法是使用系统当前默认的语言进行大小写转换,这在某些语言环境下大小写转换后会与我们的预期不符,例如:
在拉丁字母中大写字母“I”的小写格式是“i”,但是在某些语言(例如土耳其语和阿塞拜疆语)中,İ(\u0130,土耳其语大写的 i)对应的小写字符为 i(\u0069,英语小写 i),I(\u0049,英语大写 i)对应的小写字符为 ı(\u0131,土耳其语小写的无点 i)。
如图是土耳其语大小写字符的对照:
也就是在土耳其语的系统设置下,英文字符 i 和 I 的大小写转换并不会向我们预期的一样进行转换。
解决方法
Java 中大小写转换
使用 toLowerCase(Locale.ROOT)
方法转换则可以解决这个问题。Locale.ROOT
用于保证转换按照字符串的自然语言标准进行,而不受系统语言的特定区域设置的影响。
Kotlin 中大小写转换
Kotlin 中的大小写转换问题跟 Java 中一样,Kotlin 中的 toLowerCase()
和 toUpperCase()
的源码调用的也是 Java API 的 toLowerCase()
和 toUpperCase()
,所以也存在相同的问题。但是从 Kotlin 1.5 开始这两个方法被标记为 Deprecated
已弃用。
Kotlin 1.5 开始提供了新的方法 lowercase()
和 uppercase()
提供不受语言设置影响的大小写转换。
其他
如下是 Kotlin 源码中大小写转换相关的源码:
/**
* Returns a copy of this string converted to upper case using the rules of the default locale.
*/
@Deprecated("Use uppercase() instead.", ReplaceWith("uppercase(Locale.getDefault())", "java.util.Locale"))
@DeprecatedSinceKotlin(warningSince = "1.5")
@kotlin.internal.InlineOnly
public actual inline fun String.toUpperCase(): String = (this as java.lang.String).toUpperCase()
/**
* Returns a copy of this string converted to upper case using Unicode mapping rules of the invariant locale.
*
* This function supports one-to-many and many-to-one character mapping,
* thus the length of the returned string can be different from the length of the original string.
*
* @sample samples.text.Strings.uppercase
*/
@SinceKotlin("1.5")
@WasExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
public actual inline fun String.uppercase(): String = (this as java.lang.String).toUpperCase(Locale.ROOT)
/**
* Returns a copy of this string converted to lower case using the rules of the default locale.
*/
@Deprecated("Use lowercase() instead.", ReplaceWith("lowercase(Locale.getDefault())", "java.util.Locale"))
@DeprecatedSinceKotlin(warningSince = "1.5")
@kotlin.internal.InlineOnly
public actual inline fun String.toLowerCase(): String = (this as java.lang.String).toLowerCase()
/**
* Returns a copy of this string converted to lower case using Unicode mapping rules of the invariant locale.
*
* This function supports one-to-many and many-to-one character mapping,
* thus the length of the returned string can be different from the length of the original string.
*
* @sample samples.text.Strings.lowercase
*/
@SinceKotlin("1.5")
@WasExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
public actual inline fun String.lowercase(): String = (this as java.lang.String).toLowerCase(Locale.ROOT)