tomcat中文乱码怎么解决
web开发时,tomcat可能会引起中文乱码问题。
测试环境用的tomcat7,而这个有中文乱码问题。web.xml中加了org.springframework.web.filter.CharacterEncodingFilter的过滤也一样。
而本地用的tomcat8一点问题也没有.
项目中前辈们是这么解决这个问题的:
protected String getParamStrByISO_8859_1(String param) { if (param != null) { try { String retStr = new String(param.getBytes("ISO-8859-1"), "UTF-8"); if (StringUtils.isNullOrEmpty(retStr.replace('?', ' ').trim())) { return param; } else { return retStr; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return param; }这样先按IOS-8859-1解码,再用UTF-8编码。如果都是“?",那么认为这个就是正确的编码,不必转码,直接返回,否则用处理过的字符。这在tomcat8有问题,比如传个"中文66中文",就会得到"??66??",返回处理过的字符就成了乱码了。
后面问题又换了个方法:
protected String getParamStrByISO_8859_1(String param) { if (param != null) { try { String reg = "[^\u4e00-\u9fa5]"; String onlyChines = param.replaceAll(reg, ""); if (onlyChines.length() == 0) { param = new String(param.getBytes("ISO-8859-1"), "UTF-8"); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return param; }
这个正则的解释:这两个unicode值正好是Unicode表中的汉字的头和尾。匹配汉字的写法。"[]"代表里边的值表示匹配范围
这样就是,用正则得到所有中文,如果没有中文那么就用ISO-8859-1转一下。有中文就表示是正常的,不转直接返回。这个好一些,至少在tomcat8中遇到中文就直接返回了,但在tomcat7中也是有问题的,比如转入"222,99"。注意中间的逗号是中文,这个会返回没有匹配上中文,那么就要转一下,就被转成"222?99"了。
我想了个方法,前端传一个中文字"中",因为我知道它的ISO-8859-1是一个字节,而UTF-8是3个字节,那么我就是这个字节数来判断是否需要转码。
if(StringUtils.isNotBlank(schoolName)&&StringUtils.isNotBlank(codeFlag)) { //codeFlag传了个"中",如果得到的字节为1位则是ISO-8859-1,如果大于1位则是UTF-8 boolean needConvert=false; if(codeFlag.getBytes().length!=3) { needConvert=true; } if(codeFlag.getBytes().length==3) { byte bs[]=codeFlag.getBytes(); if(bs[0]!=-28||bs[1]!=-72||bs[2]!=-83) { needConvert=true; } } if(needConvert) { try { schoolName=new String(schoolName.getBytes("ISO-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { log.error(e.getMessage(),e); } } }
我又分析了下,到底测试环境是怎么回事,最发现是参数先被转成了UTF-8再转成ISO-8859-1而引起的中文乱码,如下,最后“中"字被转成了6个字节:
中: byte:45 UTF-8:-28|-72|-83| ISO-8859-1:63| 项目里的情况:-61|-92|-62|-72|-62|-83|源码:
char c='中'; System.out.println("中:"); System.out.println("byte:"+(byte)c); System.out.print("UTF-8:"); byte bts[]="中".getBytes("UTF-8"); for(byte b:bts) { System.out.print(b+"|"); } System.out.println(); System.out.print("ISO-8859-1:"); byte bts2[]="中".getBytes("ISO-8859-1"); for(byte b:bts2) { System.out.print(b+"|"); } System.out.println(); //[-61, -92, -62, -72, -62, -83] System.out.print("项目里的情况:"); byte bts3[]=new String("中".getBytes("UTF-8"),"ISO-8859-1").getBytes(); for(byte b:bts3) { System.out.print(b+"|"); } System.out.println();
后面想了下,为什么tomcat7会中文乱码。才想起来,以前我们在tomcat7的server.xml中Connector中加一个URIEncoding,如下:
<connector connectiontimeout="20000" port="8080" protocol="HTTP/1.1" redirectport="8443" uriencoding="UTF-8"/>
就可以了,spring的CharacterEncodingFilter也放在web.xml。但我测试了下,不用这个也可以得到正常的中文,无论是加了URIEncoding的tomcat7还是普通的tomcat8都不需要配spring的CharacterEncodingFilter。
2019-02-27增加记录
上面的getParamStrByISO_8859_1有坑,会把"。"转为"?"
于是改了下:
private boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { return true; } return false; } /** * getParamStrByISO_8859_1有bug,会把一个"。"转为"?" * 因为,不需要转码的情况给转了。经处理后onlyChines==0,而这表示没有中文,要转码,就转出问题了。 * * @param param * @return */ protected String getChineseString(String param) { if (param != null) { try { boolean existChinese=false; for(int i=0;i<param.length();i++) { if(isChinese(param.charAt(i))) { existChinese=true; break; } } if (!existChinese) { //不存在中文的情况,转一下,如果都是英文没问题,如果有中文就转成正确的了 param = new String(param.getBytes("ISO-8859-1"), "UTF-8"); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return param; }
但这还是解决不了 “③②①" 这样的,这还是会被转为"???",那么还是用上面的前端传中文配合的方法吧,代码如下:
private boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { return true; } return false; } protected Boolean existCheckParam(HttpServletRequest req) { String codeFlag=req.getParameter("charConvertFlag"); if(StringUtils.isNotBlank(codeFlag)&&codeFlag.equals("中")) { if(codeFlag.getBytes().length!=3) { return true; } if(codeFlag.getBytes().length==3) { byte bs[]=codeFlag.getBytes(); if(bs[0]!=-28||bs[1]!=-72||bs[2]!=-83) { return true; } } return false; }else { return null; } } protected String getChineseString(Boolean needConvert,String param) { if(needConvert==null) { return getChineseString(param); } if(needConvert) { try { return new String(param.getBytes("ISO-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block logger.error(e.getMessage(),e); return param; } }else { return param; } }调用方法:
Boolean convertFlag=existCheckParam(request); question.setOptionA(getChineseString(convertFlag,optionA));