Java中出现中文乱码浅析与问题解决

news/2024/7/7 20:03:00

一、编码介绍

字符编码是一种将字符映射到数字代码的规则或方式。在计算机中,所有的数据最终都以二进制形式存储,包括文本数据。因此,要在计算机中存储和处理文本,就需要将字符转换为对应的数字编码。

字符编码可以分为两种基本类型:固定长度编码和可变长度编码。

  1. 固定长度编码:每个字符都被映射到固定长度的二进制序列。ASCII(美国信息交换标准码)就是最著名的固定长度编码,它使用7位二进制表示128个字符,包括26个拉丁字母、阿拉伯数字、标点符号等。

  2. 可变长度编码:不同的字符可能被映射到不同长度的二进制序列。Unicode是一种常用的可变长度编码,它支持几乎所有世界上使用的字符,包括各种语言的字母、符号、表情符号等。而UTF-8、UTF-16和UTF-32则是Unicode的实现方式之一。

下面是一些常见的字符编码:

  • ASCII(American Standard Code for Information Interchange):一种7位固定长度编码,用于表示英文字母、数字和一些常用符号,共128个字符。

    以下是ASCII码表的部分内容:

    ASCII码     字符    描述
    ---------------------------------
    0           NUL     空字符
    1           SOH     标题开始
    2           STX     文本开始
    3           ETX     文本结束
    4           EOT     传输结束
    5           ENQ     请求
    6           ACK     确认
    7           BEL     响铃
    8           BS      退格
    9           HT      水平制表符
    10          LF      换行
    11          VT      垂直制表符
    12          FF      换页
    13          CR      回车
    14          SO      选择
    15          SI      取消选择
    16          DLE     数据链路转义
    17          DC1     设备控制1
    18          DC2     设备控制2
    19          DC3     设备控制3
    20          DC4     设备控制4
    21          NAK     拒绝接收
    22          SYN     同步
    23          ETB     块结束
    24          CAN     取消
    25          EM      结束媒体
    26          SUB     替换
    27          ESC     转义
    28          FS      文件分隔符
    29          GS      组分隔符
    30          RS      记录分隔符
    31          US      单元分隔符
    32          (space) 空格
    33          !       感叹号
    34          "       双引号
    35          #       井号
    36          $       美元符号
    37          %       百分号
    38          &       和号
    39          '       单引号
    40          (       左括号
    41          )       右括号
    42          *       星号
    43          +       加号
    44          ,       逗号
    45          -       减号/破折号
    46          .       句号
    47          /       斜杠
    48-57       0-9     数字
    58          :       冒号
    59          ;       分号
    60          <       小于号
    61          =       等号
    62          >       大于号
    63          ?       问号
    64          @       at符号
    65-90       A-Z     大写字母
    91          [       左方括号92          \       反斜杠93          ]       右方括号
    94          ^       上箭头
    95          _       下划线
    96          `       反引号
    97-122      a-z     小写字母
    123         {       左花括号
    124         |       竖线
    125         }       右花括号
    126         ~       波浪符
    127         DEL     删除
    
  • ISO 8859-1:也称为Latin-1,是一种8位固定长度编码,扩展了ASCII,包含了西欧常用的字符。

  • UTF-8(Unicode Transformation Format - 8-bit):一种可变长度编码,使用1至4个字节来表示Unicode字符,是互联网上使用最广泛的字符编码方式,支持全球范围内的所有字符。

  • UTF-16:一种可变长度编码,使用2或4个字节来表示Unicode字符,常用于Java和Windows平台。

  • UTF-32:一种固定长度编码,使用4个字节来表示Unicode字符,适合在内存中处理Unicode文本。

选择合适的字符编码取决于应用的需求和环境,但通常推荐使用Unicode的变种,如UTF-8,因为它能够支持全球范围内的所有字符,并且兼容ASCII。在Java中,String对象使用的是UTF-16编码,但在文件读写、网络通信等场景中,需要注意字符编码的设置以避免出现乱码问题。

二、中文乱码浅析与问题解决

  1. 字符编码不匹配:Java中的String对象是以UTF-16编码的,而很多时候中文字符在输入输出时可能会涉及到其他编码格式,比如UTF-8、GBK等。当编码格式不一致时就可能导致中文乱码。

    • 解决方法:确保在输入、输出以及处理中使用一致的字符编码。一般情况下,推荐使用UTF-8编码,因为它支持最广泛的字符范围。
  2. 文件读写编码设置错误:在读取或写入文件时,如果没有指定正确的字符编码,就会导致中文乱码。

    • 解决方法:在使用InputStreamReader或OutputStreamWriter时,明确指定编码格式,例如:

      InputStreamReader isr = new InputStreamReader(new FileInputStream("file.txt"), "UTF-8");
      OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("file.txt"), "UTF-8");
      
  3. 网络传输编码设置错误:在进行网络通信时,如果没有指定正确的字符编码,同样会导致中文乱码。

    • 解决方法:在使用Socket通信或者HttpURLConnection时,设置字符编码,例如:

      Socket socket = new Socket("hostname", port);
      BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
      
  4. IDE或控制台环境中的编码设置错误:有时候中文乱码是由于IDE或控制台的编码设置不正确造成的

  5. 字符编码转换不正确:在进行字符编码转换时,如果使用了不正确的方式,也可能导致中文乱码

三、解决中文乱码的通解

通解很简单,打印!打印!!打印!!把所以出现乱码位置的地方,用十六进制的形式的形式打印出来,然后根据码表进行对照,看看问题到底出在哪里。笔者用以下UDP服务器的例子具体说明:

笔者这里写了一个回响服务器:

public class UdpEchoServer {
    DatagramSocket socketServe = null;

    public UdpEchoServer(int port) throws SocketException {
        socketServe = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            //接受客户端request
            DatagramPacket request = new DatagramPacket(new byte[4096], 4096);
            socketServe.receive(request);
            //根据输入包,处理
            String strRequest = new String(request.getData(), 0, request.getLength());//有效长度
            String strResponse = process(strRequest);
            //构造response数据包
            //注意这里的长度,尤其是中文的字节长度与String中的长度不一样
            DatagramPacket response = new DatagramPacket(strResponse.getBytes(),0,strResponse.getBytes().length);
            //state test
            for (byte cur :
                    response.getData()) {
                System.out.printf("%x",cur);
            }

            //end
            response.setAddress(request.getAddress());
            response.setPort(request.getPort());
            socketServe.send(response);
            //打印日志
            System.out.printf("[%s:%d]request:%s,response:%s",request.getAddress(),request.getPort(),strRequest,strResponse);
        }
    }

    public String process(String request) {
        //回响服务器
        return  request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}

这时,出现了英文可以正确显示,但中文不行

但这时服务器的日志却正确构造出了响应

于是,我就把response中的数据全部打印出来,与真正的编码进行对比(这里你首先要知道Java String 默认格式是utf8) 

 for (byte cur :
                    response.getData()) {
                System.out.printf("%x",cur);
            }

 

发现结果不一样,原因就是,构造包时长度不够,经过排查发现报的长度应该是

DatagramPacket response = new DatagramPacket(strResponse.getBytes(),0,strResponse.getBytes().length);

而不是

DatagramPacket response = new DatagramPacket(strResponse.getBytes(),0,strResponse.length());

 经过打印,问题解决,所以解决中文乱码的最佳方式就是把文字以十六进制的形式打印出来,手动比较,发现问题。


http://lihuaxi.xjx100.cn/news/2090679.html

相关文章

专升本 C语言笔记-05 常量定义 #define宏定义 const关键字 enum关键字

1.宏定义 #define的使用 1.1.宏定义的概念 宏定义就是用一个标识符来表示一个字符串,如果后面代码出现了该标识符,那么就全部替换成指定的字符串 1.2.宏定义实现案例 #include <stdio.h> #define N 123 int main() {printf("N %d\n",N);printf("N N…

resetFields()失效

问题描述 先点击新增&#xff0c;再取消弹窗。resetFields()正常运行&#xff0c;清空表单内容。先点击编辑&#xff0c;再新增&#xff0c;表单内容不能清空。 解决方法 在nextTick里赋值表单内容&#xff0c;因为弹窗会把第一次赋值的内容当成初始值&#xff0c;所以要在弹…

组态软件与西门子S7系列PLC及三菱PLC间的无线通信测试

组态软件与西门子S7系列PLC及三菱PLC间的无线通信测试需要用到以下设备&#xff1a; l 西门子PLC型号&#xff1a;S7-200Smart 2台 l 三菱PLC型号&#xff1a;FX5U 2台 l 上位机&#xff1a;力控V7.1 1台 l 无线通讯终端&#xff08;网口版&#xff09;——DTD418MB 3块…

二分搜索树和AVL树

问题 跑道预订系统。一个飞机场只有一个飞机跑道&#xff0c;需要为未来的飞机着陆预留空闲的跑道。飞机预订的着陆时间为t&#xff0c;假如没有其他的计划在(t-k,tk)的时间内着陆的飞机&#xff0c;则将t加入集合R。其中k是变量。请问有没有一种时间复杂度为O(logn)的算法解决…

九州金榜|小学生产生厌学应该怎么办?

随着寒假结束&#xff0c;孩子们也步入的正常校园生活&#xff0c;现在孩子们开学已经三周了&#xff0c;可是孩子很多孩子进入不了学习状态&#xff0c;尤其是小学生&#xff0c;还沉寂在假期中&#xff0c;孩子不想学习&#xff0c;其实这是孩子厌学的一中表现&#xff0c;很…

淘宝商品详情数据采集(商品属性,规格,价格,详情图等)

淘宝商品详情数据采集是一个涉及多个步骤的过程&#xff0c;主要目的是获取商品的各种详细信息&#xff0c;如商品属性、规格、价格、详情图等。以下是一个基本的采集流程&#xff1a; 确定采集目标&#xff1a;首先&#xff0c;需要明确要采集的淘宝商品范围&#xff0c;例如…

揭秘FastStone Capture:一款强大且高效的截图工具

目录 【引子】【FastStone Capture概述】【安装步骤】【使用攻略】【核心功能解析】【总结】 【引子】 在数字化信息时代&#xff0c;无论是工作汇报、在线教学&#xff0c;还是日常交流中&#xff0c;屏幕截图已经成为我们必不可少的辅助工具。今天&#xff0c;我要为大家详细…

中国电子学会2022年9月份青少年软件编程Sc ratch图形化等级考试试卷四级真题

1.运行下列程序&#xff0c;说法正确的是&#xff1f; A&#xff1a;列表中的数字全部小于11 B&#xff1a;列表的长度为10 C&#xff1a;变量i最终值为20 D&#xff1a;列表中有大于10的数字 2.按钮Button3的当前造型为第2个造型&#xff0c;运行下列程序&#xff0c;正确…