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));

文/中中 浏览次数:0次   2018-09-27 17:15:14

相关阅读

微信扫描-捐赠支持
加入QQ群-技术交流

评论: