Android开发常见问题:字符串乱码问题

news/2024/7/3 13:21:33 标签: android, 乱码, java, String, trim

前言

最近的项目里遇到了字符串乱码的问题,记录一下研究心得。

正文

一、byte数组如何保存字符串

通常情况下,数据的传输和读取都是通过socket,读取socket需要使用byte数组,例如要写入一个字符串到socket中,我们需要先把String转成byte数组:

java">val s = "123"
// kotlin默认的编码格式是Charsets.UTF_8
val byteArray = s.toByteArray()

对方收到了byte数组后,会再调用

java">val s1 = byteArray.toString(Charsets.UTF_8)

首先确认一下byteArray里面到底是怎么保存字符串的:

java">val byteArray = s.toByteArray()
println("byteArray size:${byteArray.size}")
byteArray.forEach {
    println(it)
}

// 输出结果
49
50
51

如果你熟悉Unicode编码,你能很快看出来,其实byte数组里就是分别存了1,2,3三个字符的Unicode编码,转成字符串的时候就根据编码查找对应的字符拼接起来。

二、为什么会有乱码

乱码指的是字符解码错误导致显示的字符混乱的现象。我们已经知道byte数组里是按照指定的编码格式保存字符对应的unicode码,显而易见会有两种可能导致乱码

1、编码格式与解码格式不匹配

这是一种非常常见的错误,比如服务端数据没有指定utf-8格式,客户端根据utf-8去解码得到的数据就是一团乱码,或者本地保存的文件是gbk,用utf-8打开看到的内容就是一坨乱码。解决的方法也很简单,修改编码和解码格式匹配就可以了。

2、byte数组里的unicode码,系统不支持

如果unicode码系统不支持,有两种可能:

  1. 系统字符集有问题,不支持此unicode。这种情况一般是系统字符集不全导致的部分字符显示不出来或乱码,只需要更新最新的字符集即可;
  2. 系统字符集是完整的,确实不存在对应的字符,显示的时候系统可能会显示成其他字符;

Unicode码不能是负数,目前Android系统支持0~65535之间的unicode码,如果是范围以外的值,那么需要确认数据的来源是否正确,例如unicode码传错了,或者中间有数据转换异常。

三、如何去掉乱码

  1. 如果是编码格式和解码异常不匹配导致的乱码,需要修改两者一致;
  2. 如果是系统字符集问题,那么需要更新系统字符集;
  3. 确认乱码的unicode,小于等于32考虑使用trim或者根据unicode自行删除;

前两种问题都比较好解决,第三种情况比较特殊,我遇到的问题是数据源的unicode码是0:

java">var s = "123"
s = s.plus(0.toChar())
println(s)

在这里插入图片描述
可以在网上搜索一下unicode字符表:
在这里插入图片描述
从表格查到unicode = 0表示空字符,但是在不同系统上空字符显示的效果不一样,有的是不可见,有的像我截图的这样是一个其他的字符。接下来我们看一下String源码中的trim方法:

java">public String trim() {
    int len = length();
    int st = 0;

    while ((st < len) && (charAt(st) <= ' ')) {
        st++;
    }
    while ((st < len) && (charAt(len - 1) <= ' ')) {
        len--;
    }
    return ((st > 0) || (len < length())) ? substring(st, len) : this;
}

trim一般用来去掉字符串头部和尾部的空白字符,按照源码的逻辑,只要是unicode码小于等于空格都会被删掉,从表中查到空格的unicode码是32,所以unicode小于等于32的字符都会被删掉。像我这种情况使用trim就可以有效的去掉乱码

unicode可以超过65535吗,按照我的实测是不会,目前我们的系统都是16位,那么最多就是65535。比较常见的问题还有:

为什么Java最多只能标识65535个字符
Java代码中一个方法代码不能超出65535字节

目前看String.trim方法已经能覆盖到几乎所有的情况了,所以第三种情况优先考虑使用trim

StringtrimJavaStringtrim_88">4、Kotlin的String.trim和Java的String.trim

刚才Java的String.trim已经看过了,但是Kotlin对String.trim进行了覆盖:

java">/**
 * Returns a string having leading and trailing whitespace removed.
 */
@kotlin.internal.InlineOnly
public inline fun String.trim(): String = (this as CharSequence).trim().toString()

/**
 * Returns a sub sequence of this char sequence having leading and trailing whitespace removed.
 */
public fun CharSequence.trim(): CharSequence = trim(Char::isWhitespace)

/**
 * Returns a sub sequence of this char sequence having leading and trailing characters matching the [predicate] removed.
 */
public inline fun CharSequence.trim(predicate: (Char) -> Boolean): CharSequence {
    var startIndex = 0
    var endIndex = length - 1
    var startFound = false

    while (startIndex <= endIndex) {
        val index = if (!startFound) startIndex else endIndex
        val match = predicate(this[index])

        if (!startFound) {
            if (!match)
                startFound = true
            else
                startIndex += 1
        } else {
            if (!match)
                break
            else
                endIndex -= 1
        }
    }

    return subSequence(startIndex, endIndex + 1)
}

Kotlin的String.trim被覆盖为:只删掉头部和尾部的空白字符:

java">public static boolean isWhitespace(int codePoint) {
   // We don't just call into icu4c because of the JNI overhead. Ideally we'd fix that.
    // Any ASCII whitespace character?
    if ((codePoint >= 0x1c && codePoint <= 0x20) || (codePoint >= 0x09 && codePoint <= 0x0d)) {
        return true;
    }
    if (codePoint < 0x1000) {
        return false;
    }
    // OGHAM SPACE MARK or MONGOLIAN VOWEL SEPARATOR?
    if (codePoint == 0x1680 || codePoint == 0x180e) {
        return true;
    }
    if (codePoint < 0x2000) {
        return false;
    }
    // Exclude General Punctuation's non-breaking spaces (which includes FIGURE SPACE).
    if (codePoint == 0x2007 || codePoint == 0x202f) {
        return false;
    }
    if (codePoint <= 0xffff) {
        // Other whitespace from General Punctuation...
        return codePoint <= 0x200a || codePoint == 0x2028 || codePoint == 0x2029 || codePoint == 0x205f ||
            codePoint == 0x3000; // ...or CJK Symbols and Punctuation?
    }
    // Let icu4c worry about non-BMP code points.
    return isWhitespaceImpl(codePoint);
}

就以我遇到的unicode是0,这里返回的是false,所以没办法去掉乱码。如果你用的Kotlin又想要使用Java的String.trim方法,只能自己实现:

java">String.trim { it <= ' ' })

http://www.niftyadmin.cn/n/5016723.html

相关文章

五种定时任务方案(Timer+ScheduleExecutorService+spring task+多线程执行+quartz)

方案一&#xff1a;Timer (1)Timer.schedule(TimerTask task,Date time)安排在制定的时间执行指定的任务。 (2)Timer.schedule(TimerTask task,Date firstTime ,long period)安排指定的任务在指定的时间开始进行重复的固定延迟执行&#xff0e; (3)Timer.schedule(TimerTask…

线性规划对偶问题:理论推导和实际应用

文章目录 对偶问题实例对偶问题定义和性质定义性质 对偶问题应用影子价格理论应用 参考文献 对偶问题实例 之前在很多地方&#xff0c;都看到过“对偶”这两个字眼&#xff0c;总觉得这个词很高大上。对偶理论的百度百科中甚至写到&#xff1a;“在线性规划早期发展中最重要的…

学成在线-网站搭建

文章目录 代码素材来自b站pink老师 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>学成在线首…

lv4 嵌入式开发-4 标准IO的读写(二进制方式)

目录 1 标准I/O – 按对象读写 2 标准I/O – 小结 3 标准I/O – 思考和练习 文本文件和二进制的区别&#xff1a; 存储的格式不同&#xff1a;文本文件只能存储文本。除了文本都是二进制文件。 补充计算机内码概念&#xff1a;文本符号在计算机内部的编码&#xff08;计算…

一篇博客教会您SpringMVC文件上传、下载,多文件上传及工具jrebel的使用

目录 一.文件上传 二.文件下载 三.多文件上传 四&#xff0c;jrebel的介绍 前言&#xff1a; 我们之前已经实现了SpringMVC的增删改查&#xff0c;今天这一篇博客教会您SpringMVC文件上传、下载&#xff0c;多文件上传及工具jrebel的使用&#xff0c;希望这篇博客能够给正在…

学习Bootstrap 5的第十一天

折叠 基础的折叠 在 Bootstrap 5 中&#xff0c;折叠效果可以通过添加特定的属性和类来轻松实现内容的显示和隐藏。具体步骤如下&#xff1a; 1、创建一个可折叠的元素&#xff0c;通常使用 <div> 标签&#xff0c;并为其添加 .collapse 类&#xff0c;以指示它是可折…

Stable Diffusion 免费升级 SDXL 1.0,哪些新特性值得关注?体验如何?5 分钟带你体验!

一、引言 7 月 26 日&#xff0c;Stability AI 发布了 SDXL 1.0&#xff0c;号称目前为止&#xff0c;最厉害的开放式图像生成大模型。 它到底有没有网上说的那么炸裂&#xff1f;真的已经实现了像 midjourney 一样 靠嘴出图 的功能吗&#xff1f;相对于之前的版本&#xff0c;…

python28种极坐标绘图函数总结

文章目录 基础图误差线等高线polar场图polar统计图非结构坐标图 &#x1f4ca;python35种绘图函数总结&#xff0c;3D、统计、流场&#xff0c;实用性拉满 matplotlib中的画图函数&#xff0c;大部分情况下只要声明坐标映射是polar&#xff0c;就都可以画出对应的极坐标图。但…