前后端编码的开发与编写
java编码字符编码
ASCII 码
因为计算机只认识数字,所以我们在计算机里面的一切数据都是以数字来表示,因为英文字符有限,所以规定使用的字节的最高位是 0,每一个字节都是以 0-127 之间的数字来表示。比如 A 对应 65,a 对应 97。这便是 美国标准信息交换码,ASCII
String str = new String("Aa");
byte[] strASCII = str.getBytes("ASCII");
System.out.println(Arrays.toString(strASCII));//[65, 97]
为了解决各个国家因为本地化字符编码带来的影响,就把全世界所有的字符统一进行编码---Unicode 编码
此时某一个字符在全世界任何地方显示都是固定的,比如汉字 哥,在任何地方都是以十六进制 54E5 来表示。
Unicode 的字符编码都占有两个字节
Unicode只有一个字符集,中、日、韩的三种文字占用了Unicode中0x3000到0x9FFF的部分 Unicode目前普遍采用的是UCS-2,它用两个字节来编码一个字符, 比如汉字"经"的编码是0x7ECF,注意字符编码一般用十六进制来 表示,为了与十进制区分,十六进制以0x开头,0x7ECF转换成十进制 就是32463,UCS-2用两个字节来编码字符,两个字节就是16位二进制, 2的16次方等于65536,所以UCS-2最多能编码65536个字符。 编码从0到127的字符与ASCII编码的字符一样,比如字母"a"的Unicode 编码是0x0061,十进制是97,而"a"的ASCII编码是0x61,十进制也是97, 对于汉字的编码,事实上Unicode对汉字支持不怎么好,这也是没办法的, 简体和繁体总共有六七万个汉字,而UCS-2最多能表示65536个,才六万 多个,所以Unicode只能排除一些几乎不用的汉字,好在常用的简体汉字 也不过七千多个,为了能表示所有汉字,Unicode也有UCS-4规范,就是用 4个字节来编码字符,不过现在普遍采用的还是UCS-2,只用两个字节来 编码,看一下Unicode对汉字的编码:
这里我们找到了“王”对应的 纵坐标 是 7380
这里我们再寻找“王”对应的 横坐标 我们从左往右数 是B
所以 “王” 对应的Unicode编码是 \u738b
char c6='\u66f9';
char c7='\u950b';
UTF-8
是一种针对 Unicode 的可变长度字符编码,又称为 万国码,是 Unicode 的实现方式之一。编码中的第一个字节仍与 ASCII 兼容,这使得原来处理 ASCII 字符的软件无须或只需做少部分修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持 UTF-8 编码
String str = new String("Aa帅锅");
byte[] strASCII = str.getBytes("UTF-8");
System.out.println(Arrays.toString(strASCII));//[65, 97, -27, -72, -123, -23, -108, -123]
存储字母、数字:无论什么字符集都占有 1 个字节
存储汉字:GBK 家族占有 2 个字节。UTF-8 占有 3 个字节
不能使用单字节的字符集(ASCII/ISO-8859-1)来存储中文
字符的编码和解码
信息在计算机网络中传输是以字节的形式。那么如何变为字节?这就是编码的过程。那么计算机接收了这个编码,如何让使用者认识呢?那必须要将字节转换为人所识别的字符串形式,这就是解码的过程。
编码:将字符串转换为 byte 数组
解码:把 byte 数组转换为 字符串
java中使用String类型也是可以指定字节到字符转换的编码字符集的。
一、中文编码避免乱码
new String(getBytes(ISO-8859-1),UTF-8)来避免乱码,当然UTF-8可以换成GBK,unicode。
tomcat默认全部都是用ISO-8859-1编码,不管你页面用什么显示,Tomcat最终还是会替你将所有字符转做ISO-8859-1.
在Java中,String的getBytes()方法是得到一个操作系统默认的编码格式的字节数组。
String.getBytes(String decode)方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示
有时候,为了让中文字符适应某些特殊要求(如http header头要求其内容必须为iso8859-1编码)和tomcat中全部用ISO-8859-1编码,可能会通过将中文字符按照字节方式来编码的情况,如
String s_iso88591 = new String("深".getBytes("UTF-8"),"ISO8859-1"),
这样得到的s_iso8859-1字符串实际是三个在 ISO8859-1中的字符,在将这些字符传递到目的地后,目的地程序再通过相反的方式String s_utf8 = new String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")来得到正确的中文汉字“深”。这样就既保证了遵守协议规定、也支持中文。
二、new String(str.getBytes(“***”),“”)的实现
如果是new String(str.getBytes(“gbk”),“gbk”)时,可以分为两步:
第一步:byte[] bytes=str.getBytes(“gbk”)
告诉java虚拟机将中文以“gbk”的方式转换为字节数组。一个汉字对应两个字节。
第二步:String s=new String(bytes,“gbk”) // 执行后的s就是第一步的str。
告诉虚拟机将字节数组中的字节以“gbk”的方式将每2个字节组装成一个汉字。此汉字s就是第一步str代表的汉字。
String xxx = "你好";
String yyy = new String(xxx.getBytes(), "utf-8");
System.out.println(yyy);
import java.io.UnsupportedEncodingException;
/**
* 字符串 通用 工具类
* @author Administrator
*/
public class StringUtil {
private static final char UNDERLINE='_';
public static String enCode(String path, String enCode) {
try {
return new String(path.getBytes(), enCode);
} catch (UnsupportedEncodingException e) {
}
return path;
}
}
String str = new String("Aa帅锅");
//编码操作
byte[] strByte = str.getBytes("GBK");
System.out.println(Arrays.toString(strByte));//[65, 97, -53, -89, -71, -8]
//解码操作
//注意编码的字符集和解码的字符集格式必须一致(是其扩展字符集也可以),否则会乱码
//第一种:编码格式为 GBK,解码格式为 ISO-8859-1 那么就会乱码
String str2 = new String(strByte,"ISO-8859-1");
System.out.println(str2); //Aa?§??
//第二种:编码和解码格式一致
String str3 = new String(strByte,"GBK");
System.out.println(str3); //Aa帅锅
注意:①、编码格式和解码格式必须一致,否则乱码
②、有时候编码为和解码格式一致了,但是还是乱码,这是因为在数据在传输过程中经过服务器的处理,而这个服务器可能是外国人编写的,那么就会将数据转换为 别的字符格式,那么你如果还是直接转为自己想要的格式是会乱码的。
解决办法:先获取经过服务器之后的数据还原编码,然后在进行解码
String str = new String("Aa帅锅");
//编码操作
byte[] strByte = str.getBytes("UTF-8");
System.out.println(Arrays.toString(strByte));//[65, 97, -27, -72, -123, -23, -108, -123]
//中间经过了服务器的传输,编码格式转成了 ISO-8859-1
String str2 = new String(strByte,"ISO-8859-1");
//解码操作 ,此时如果直接进行解码,那么会乱码
String str3 = new String(str2.getBytes(),"UTF-8");
System.out.println(str3); //Aa???????
//对于上面的乱码,我们必须先还原服务器之前的编码格式,然后在进行解码。那么就不会乱码
byte[] strByte2 = str2.getBytes("ISO-8859-1");
String str4 = new String(strByte2,"UTF-8");
System.out.println(str4); //Aa帅锅
三、编码异常UnsupportedEncodingException
例如new(s.getBytes("ISO-8859-1"), "GB2312");的构造方法为public String(byte[] bytes, Charset charset)通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。新 String 的长度是字符集的函数,因此可能不等于 byte 数组的长度。 此方法总是使用此字符集的默认替代字符串替代错误输入和不可映射字符序列。如果需要对解码过程进行更多控制,则应该使用 CharsetDecoder 类。
因此进行编码都会先抛出UnsupportedEncodingException异常
import java.io.UnsupportedEncodingException;
public static void main(String[] args) throws UnsupportedEncodingException {
String path = "f:/assa/a.xlsx";
int index = path.lastIndexOf("/");
String name = path.substring(index);
String x = new String(name.getBytes(), "ISO-8859-1");
System.out.println(x);
}
在IO操作中我们一般需要进行编码,这里的IO操作包括磁盘IO、网络IO等
前台url传递参数
+ "&fileName=" + encodeURI(_fileName);
后台接收参数并处理汉字编码
String fileName = request.getParameter("fileName")
if(StringUtil.isEmpty(fileName)){
name= fileName ;
}
name = encode(name, "ISO-8859-1");
使用java自带的URLEncoder类进行编码
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.Date;
@RequestMapping("export")
public void export(String modeCode, HttpServletRequest request, HttpServletResponse response){
try{
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/x-download");
String fileName = "导出文件"+ (StringUtil.isEmpty(modeCode)?"":("_"+modeCode))+"_"+ DateUtil.formatDefTime(new Date())+".xlsx";
fileName = URLEncoder.encode(fileName, "UTF-8");
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
service.exportExe(modeCode,request,response);
}catch(Exception e){
e.printStackTrace();
}
}
编码方法的封装
import java.net.URLEncoder;
public static String encodeStr(String str, String encoding)
{
try
{
return URLEncoder.encode(str, encoding);
}
catch (Exception e)
{
logger.debug("编码错误:" + str, e);
}
return str;
}
对文件名编码
import java.net.URLEncoder;
// 获取文件名
String fileName = StringUtils.isEmpty(attachment.getAttachmentName()) ? file.getName() : attachment.getAttachmentName();
// 设置响应头,控制浏览器下载该文件
response.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));
response.setHeader("content-type", "image/jpeg");
中文编码
@GetMapping("/get/{attId}")
public void download(@PathVariable Integer attId, HttpServletResponse response) throws IOException {
Attachment attachment = service.getById(attId);
String fileName = new String(attachment.getFileName().getBytes("UTF-8"),"ISO8859-1");//中文需要编码下
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Content-Type","application/octet-stream;charset=UTF-8");
ServletOutputStream out = response.getOutputStream();
out.write(attachment.getAttachmentContent());
out.flush();
out.close();
}
js编码
URI: Uniform ResourceIdentifiers,通用资源标识符
Global对象的encodeURI()和encodeURIComponent()方法可以对URI进行编码,以便发送给浏览器。有效的URI中不能包含某些字符,例如空格。而这URI编码方法就可以对URI进行编码,它们用特殊的UTF-8编码替换所有无效的字 符,从而让浏览器能够接受和理解。
1.encodeURI()
把字符串作为 URI整体进行编码,所以URI组件中的特殊分隔符号(;/:@&=+$?#),encodeURI() 函数不会进行转义。该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码:encodeURI不会对:/?&= 进行编码;
encodeURI()不会被此方法编码的字符:! @ # $ & * ( ) = : / ; ? + '
encodeURI("http://www.w3school.com.cn")
输出http://www.w3school.com.cn
传输json格式数据
var strwhere = "recordType:\""+recordType+"\",recordId:\""+ids+"\",isRecord:\""+isRecord;
strwhere+="\"";
var strUrl = Leopard.getContextPath() +
"/DoMyServlet?className=ExcelPoiAction&methodName=createExcel&btnCode=empdata&forWard=isFile&namesalt="+namesalt+"&func="+_func
+"&pbean="+encodeURI(encodeURI(strwhere))+"&btnCode"+empexcel;
var ifm;
if (document.getElementById("empexcel_iframe") == undefined) {
ifm = document.createElement("IFRAME");
ifm.setAttribute("id", "empexcel_iframe");
ifm.setAttribute("name", "empexcel_iframe");
ifm.style.height = "0";
ifm.style.width = "0";
ifm.style.display = "block";
ifm.src = "";
document.body.appendChild(ifm);
document.getElementById("empexcel_iframe").attachEvent(
"onload",
function() {
window.frames['empexcel'].document.getElementById("empexcel").click();
});
} else { ifm = document.getElementById("empexcel_iframe"); }
ifm.src = strUrl;
this.strWhere = java.net.URLDecoder.decode(req.getParameter("pbean"),"utf-8");
if (!StringUtils.isEmpty(strWhere)) {
try {
objWhere = new JSONObject("{" + strWhere + "}");
} catch (JSONException e) {}
}
String recordType= objWhere.getString("recordType");
String isRecord= objWhere.getString("isRecord");
其中encodeURI()主要用于整个URI(例如,http://www.jxbh.cn/illegal value.htm),而encode-URIComponent()主要用于对URI中的某一段(例如前面URI中的illegal value.htm)进行编码。它们的主要区别在于,encodeURI()不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和井字号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码。来看下面的例子:
var uri="http://www.jxbh.cn/illegal value.htm#start";
//”http: //www.jxbh.cn/illegal%20value .htm#s tart”
alert(encodeURI (uri)):
//”http% 3A%2F%2Fwww.jxbh.cn%2 Fillegal%2 0value. htm%23 start”
alert( encodaURIComponent (uri));
使用encodeURI()编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了%20。而encodeURIComponent()方法则会使用对应的编码替换所有非字母数字字符。这也正是可以对整个URI使用encodeURI(),而只能对附加在现有URI后面的字符串使用encodeURIComponent()的原因所在。一般来说,我们使用encodeURIComponent()方法的时候要比使用encodeURI()更多,因为在实践中更常见的是对查询字符串参数而不是对基础URL进行编码.
2.encodeURIComponent()
把字符串作为 URI 组件的一部分(如path/query/fragment等)进行编码,所以用于分隔 URI 各个部分的特殊分隔符号(;/?:@&=+$,#)也会被转义。返回值中某些字符将被十六进制的转义序列替换。该方法也不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码:
encodeURI 不对下列字符进行编码:“:”、“/”、“;”和“?”。 encodeURIComponent则会相应的:/?&= 编码成%3A、%2F、%3F、%26、%3D。
不会被此方法编码的字符:! * ( ) '
encodeURIComponent("http://www.w3school.com.cn")
输出http%3A%2F%2Fwww.w3school.com.cn
请注意,如果该字符串代表一个路径,例如 /folder1/folder2/default.html,则其中的斜杠也将被编码,这样,当该字符串作为请求发送到 Web 服务器时它将是无效的。如果字符串中包含多个 URI 组件,请使用 encodeURI 方法进行编码。
经我的观测,很多网站的cookie在进行编码的时候,是encodeURIComponent格式的,所以应该使用decodeURIComponent()进行解码
Performs the md5 hash on a string.
SparkMD5.hash = function (str, raw) {
// converts the string to utf8 bytes if necessary
if (/[\u0080-\uFFFF]/.test(str)) {
str = unescape(encodeURIComponent(str));
}
var hash = md51(str);
return !!raw ? hash : hex(hash);
};
前端编码两次:encodeURI(encodeURI(要编码的中文),'')
后端解码:URLDecoder.decode(request.getParameter(对应的字段名称), "UTF-8")
3.decodeURI()
解码URI中被转义的字符
decodeURIComponent(%E4%BD%A0%E5%A5%BDjavascript);
输出你好javascript
encodeURI 方法返回一个已编码的 URI。如果将编码结果传递给 decodeURI,则将返回初始的字符串。
4.decodeURIComponent
返回统一资源标识符 (URI) 的一个已编码组件的非编码形式。
5.escape()
用于对字符串进行编码,并返回编码字符串。但目前已不推荐使用该函数对URI进行编码。
:?&都被转码了,而/没有,w3school解释是,escape函数会对ascii码中字母、数字及符号( * @ - _ + . / )之外的所有字符进行编码。
var str = "http://localhost:8080/Product/index?id=123&attr=456&area=中国";
console.log(escape(str));
escape对汉字“中国”编码后结果与前两者不同。
6. unescape()
从用 escape 方法编码的 String 对象中返回已解码的字符串。
function unescape(charString : String) : String
说明:unescape 方法返回一个包含 charstring 内容的字符串值。所有以 %xx 十六进制形式编码的
字符都用 ASCII 字符集当中等效的字符代替。(以 %uxxxx 格式(Unicode 字符)编码的字符用十六
进制编码 xxxx 的 Unicode 字符代替。)
注意:unescape 方法不应用于解码“统一资源标识符”(URI)。请改用 decodeURI 和 decodeURIComponent 方法。
7.head方式设置编码
或者把请求方式变为post也能解决这个问题,想后端通过http的方式调用远程的接口,可以把中文编码,对header中加入
httpGet.setHeader("Content-Type","application/text; charset=UTF-8");这样相当于告诉了接收方字符的类型和编码方式
//这里设置统一的头信息
$.ajaxSetup({
beforeSend: function (xhr) {
//alert(this.url);
if (this.url) {
if (!this.url.contains("http"))
this.url = $Core.SERVICEPATH() + this.url;
}
},
//dataType: "josn",
headers: {
'token': $Core.USER().TOKEN,
'appid': $Core.APPID
},
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
timeout: $Core.Config.ajaxTimeout,
cache: false,
//xhrFields: {
// withCredentials: true
//},
//dataType:"json",
crossDomain: true,
complete: function (XHR, TS) {
if (TS === "success") {
var result = XHR.responseJSON;
if (result && result.code != undefined) {
if (result.code == "500") {
showError(this, 500, result.msg);
return false;
}
else if (result.code == "401") { //登录信息失败
showError(this, 401, "您的登录信息已经失效,请重新登录系统!", $Core.TimeOut);
return false;
}
else if (result.code == "403") { //资源信息失败
return false;
}
}
}
//console.log(XHR, TS);
}
});
document.cookie="name=中文";
//为了防止中文乱码,我们可以使用encodeURIComponent()编码;decodeURIComponent()解码
document.cookie = encodeURIComponent("name")+"="+encodeURIComponent("中文");
spring定义编码过滤器CharacterEncodingFilter
通过类org.springframework.web.filter.CharacterEncodingFilter,定义request和response的编码。
CharacterEncodingFilter类具有encoding和forceEncoding两个属性,其中encoding是表示设置request的编码,forceEncoding表示是否同时设置response的编码。
具体做法是,在web.xml中定义一个Filter,如下:
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {request.setCharacterEncoding(this.encoding);if (this.forceEncoding)
{response.setCharacterEncoding(this.encoding);}}filterChain.doFilter(request, response);}
在web.xml中的配置
characterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceRequestEncoding
false
forceResponseEncoding
true
characterEncodingFilter
/*
二、CharacterEncodingFilter过滤器类浅析
打开该类源码,可以看到该类有三个类属性
private String encoding; //要使用的字符集,一般我们使用UTF-8(保险起见UTF-8最好)
private boolean forceRequestEncoding = false; //是否强制设置request的编码为encoding
private boolean forceResponseEncoding = false; //是否强制设置response的编码为encoding
主要方法只有一个,也就是下面这个,代码逻辑很简单,入注释所解释
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String encoding = getEncoding();
if (encoding != null) { //如果设置了encoding的值,则根据情况设置request和response的编码
//若设置request强制编码或request本身就没有设置编码
//则设置编码为encoding表示的值
if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
//若设置response强制编码,则设置编码为encoding表示的值
if (isForceResponseEncoding()) { //请注意这行代码,下面有额外提醒
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
if (isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
是在
filterChain.doFilter(request, response);
之前执行的,这也就是说这段代码的作用是设置response的默认编码方式,在之后的代码里是可以根据需求设置为其他编码的,即这里设置的编码可能不是最终的编码
对于get请求中文参数出现乱码解决方法有两个:
修改tomcat配置文件添加编码与工程编码一致,如下:
另外一种方法对参数进行重新编码:
String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码
三、可以自定义拦截器(在web.xml中配置)
SetCharacterEncodingFilter
com.itwang.filter.SetCharacterEncodingFilter
encoding
UTF-8
SetCharacterEncodingFilter
/*
public class SetCharacterEncodingFilter implements Filter {
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
String encoding = filterConfig.getInitParameter("encoding");
if(encoding==null){
encoding = "UTF-8";
}
//POST:
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
chain.doFilter(request, response);
}
public void destroy() {
}
}