乱码出现的原因
我们在写程序的时候经常会遇到乱码的情况,有时乱码是一些不认识的汉字,有些乱码是一连串的问号,这让我们非常难受。乱码问题我想一定困扰过许多刚刚开始学习web的朋友,综上所述我写了一些东东,希望对各位有所帮助。
乱码出现的原因其实本质上就是对汉字使用A方式编码,然后使用B方式解码就会出现乱码的问题。
而出现两者不同会存在两个地方需要注意:
- 前端:第一个前端向后端传输时会将文本编码,文本到了后端会对编码后的内容进行解码,当两者不一样时就会出现乱码。
- 数据库:第二个我们利用dao向数据库存贮时,也会将文本编码,然后到了数据库时也会再次解码,这时也有可能会出现乱码的情况
- tomcat
那么又有小伙伴要问你凭什么说编码和解码不一样就会出现问题呢?
回答:我直接上代码。
package test;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* @author : 范东升 @date : 2023/4/24
* CONTENT:
*/
public class BMTest {
private static final String STR = "我爱包头";
private static final List<Charset> MyStandardCharsets = new ArrayList<>();
static {
MyStandardCharsets.add(StandardCharsets.US_ASCII);
MyStandardCharsets.add(StandardCharsets.ISO_8859_1);
MyStandardCharsets.add(StandardCharsets.UTF_8);
MyStandardCharsets.add(StandardCharsets.UTF_16BE);
MyStandardCharsets.add(StandardCharsets.UTF_16LE);
MyStandardCharsets.add(StandardCharsets.UTF_16);
MyStandardCharsets.add(Charset.forName("GBK"));
}
public static void main(String[] args) {
for (int i=0;i<MyStandardCharsets.size();i++)
{
for (int j=0;j<MyStandardCharsets.size();j++)
{
if (i==j)
continue;
byte[] bytes = codeString(STR,MyStandardCharsets.get(i));
String s = decodeBytes(bytes, MyStandardCharsets.get(j));
System.out.println("结果 : "+s + "\n");
}
System.out.println("********************************");
}
}
/***
* @param str 字符串
* @param encode 解码方式
* @return 解码后字节流
*/
private static byte[] codeString(String str , Charset encode ) {
System.out.print("编码 : "+ encode.displayName() +" ");
return str.getBytes(encode);
}
/**
* @param bytes 字节流
* @param encode 解码方式
* @return 返回解码后的字符串
*/
private static String decodeBytes(byte[] bytes,Charset encode){
System.out.println("解码 : "+ encode.displayName());
return new String(bytes,encode);
}
private static void printBytes(byte[] bytes) {
for (int i=0;i<bytes.length;i++)
{
System.out.print(bytes[i]+" | ");
}
System.out.println();
}
}
结果
"我爱包头"
行表示编码方式,列表示解码方式
US_ASCII | ISO_8859_1 | UTF_8 | UTF_16BE | UTF_16LE | UTF_16 | GBK | |
US_ASCII | ???? | ������������ | br1SY4 | b1rS4Y | ��br1SY4 | �������� | |
ISO_8859_1 | ???? | æç±å头 | br1SY4 | b1rS4Y | þÿbr1SY4 | ÎÒ°®°üÍ· | |
UTF_8 | ???? | ???? | br1SY4 | b1rS4Y | ��br1SY4 | �Ұ���ͷ | |
UTF_16BE | 㼿㼿 | 㼿㼿 | 釧袱藥꒴ | ᅢㅲՓ㑙 | 我爱包头 | 컒낮냼춷 | |
UTF_16LE | 㼿㼿 | 㼿㼿 | 裦놈賥뒤 | ᅢㅲՓ㑙 | �ᅢㅲՓ㑙 | 틎꺰ﲰ럍 | |
UTF_16 | 㼿㼿 | 㼿㼿 | 釧袱藥꒴ | 我爱包头 | ᅢㅲՓ㑙 | 컒낮냼춷 | |
GBK | ???? | ???? | 鎴戠埍鍖呭ご | br1SY4 | b1rS4Y | �br1SY4 |
那么又有小伙伴要问了,为什么编码和解码方式不一样就会出现这种问题呢?
如utf-8中,一个字母用一个字节表示,一个汉字用三个字节表示,特殊的汉字用四个字节表示,而gbk中,一个字母用一个字节表示,一个汉字用两个字节表示。相信说这里聪明的小伙伴就已经恍然大悟了,后面可以自己去实践一下。当然你可以在上面的代码中观察到。
解决办法其实也很简单,当我们明白到这里的时候想要解决这个问题,那么规定好4个口的编码就解码就可以了。
JPS+SERVLET解决方案:
前端传输编码:
我们在jsp文件中加入下面这句话,这一步其实是解决了前端传输的编码问题。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
当该JSP页面发送请求时,contentType中的内容也会被包含在请求头中,一起发送过去。
(我们还有可能会使用ajax去发请求,记得也带上编码方式)
后端解析解码:
我们需要在servlet中加入。
request.setCharacterEncoding("utf-8");
当然给每一个servlet去配置utf-8明显太麻烦。这时我们可以在web.xml文件中去配置一个过滤器是常用的方法。
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
数据库字段设置为UTF-8
这个也有多种方案:第一个直接给mysql下配置
查看mysql字符集
show variables like 'character%';
修改配置文件里内容
[client]
default-character-set=utf8
[mysqld]
character-set-server=utf8
collation_server=utf8_general_ci
去编辑某个库的字符集,建议navcat直接看和改
这里需要注意一点:改库之后表里面已经存在字段的字符集不会改变记得要改。
后端传输数据库编码
useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
springboot解决方案
解决方法是换汤不换药啊,毕竟springBoot中的springMVC就是servlet的自动挡。我们可以springboot配置的理念解决问题也可以使用springMVC的理念去解决问题。
1:配置文件
#编码格式
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
2:写一个过滤器放到容器里。
@Configuration
public class MyConfiguration extends WebMvcConfigurationSupport {
@Bean
public CharacterEncodingFilter characterEncodingFilter(){
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return filter;
}
}
3:这种其实和上面的request.setCharacterEncoding("utf-8");差不多一个意思,springMVC又自动化了一层。
@RestController
public class TestController {
@Value("${book.name}")
private String name;
@Value("${book.number}")
private String number;
//防止中文乱码
@RequestMapping(value = "/getBook",produces = "application/json;charset=utf-8")
public String getBook(){
return name + number;
}
}
结论:解决办法不少,一定知道本质问题。