Java 如何确定字符串是否包含无效的编码字符
使用场景 我们已经实现了一个web服务,我们的web前端开发人员(通过php api)在内部使用该服务来显示产品数据。用户在网站上输入内容(即查询字符串)。网站在内部通过api调用服务 注意:我们使用restlet,而不是tomcat 原始问题 Firefox 3.0.10似乎尊重浏览器中选择的编码,并根据选择的编码对url进行编码。这确实会导致ISO-8859-1和UTF-8的查询字符串不同 我们的网站转发来自用户的输入,而不转换它(它应该转换),因此它可以通过api调用服务,使用包含德语umlauts的查询字符串调用web服务 例如,对于一个查询部分Java 如何确定字符串是否包含无效的编码字符,java,string,unicode,encoding,Java,String,Unicode,Encoding,使用场景 我们已经实现了一个web服务,我们的web前端开发人员(通过php api)在内部使用该服务来显示产品数据。用户在网站上输入内容(即查询字符串)。网站在内部通过api调用服务 注意:我们使用restlet,而不是tomcat 原始问题 Firefox 3.0.10似乎尊重浏览器中选择的编码,并根据选择的编码对url进行编码。这确实会导致ISO-8859-1和UTF-8的查询字符串不同 我们的网站转发来自用户的输入,而不转换它(它应该转换),因此它可以通过api调用服务,使用包含德语um
...v=abcädef
如果选择“ISO-8859-1”,则发送的查询部分如下所示
...v=abc%E4def
...v=abc%C3%A4def
但如果选择了“UTF-8”,则发送的查询部分如下所示
...v=abc%E4def
...v=abc%C3%A4def
所需解决方案
在我们控制服务时,因为我们已经实现了它,所以我们希望检查服务器端调用是否包含非utf-8字符,如果是,则以4xx http状态响应
当前解决方案的详细信息
检查每个字符(=字符串.子字符串(i,i+1))
protected List< String > getNonUnicodeCharacters( String s ) {
final List< String > result = new ArrayList< String >();
for ( int i = 0 , n = s.length() ; i < n ; i++ ) {
final String character = s.substring( i , i + 1 );
final boolean isOtherSymbol =
( int ) Character.OTHER_SYMBOL
== Character.getType( character.charAt( 0 ) );
final boolean isNonUnicode = isOtherSymbol
&& character.getBytes()[ 0 ] == ( byte ) 63;
if ( isNonUnicode )
result.add( character );
}
return result;
}
而且它不会抛出一个非法的argumentexception信号,将解码为给定的编码。这应该适当地标记错误。然而,文件指出:
这个解码器有两种可能的方法来处理非法字符串。它可以不使用非法字符,也可以抛出IllegalArgumentException。解码器采用哪种方法取决于实现
所以你应该试试看。另请注意(来自decode()方法文档):
声明应使用UTF-8。不这样做可能会导致不兼容
所以还有别的事情要考虑
编辑:Apache Commons声称会对错误编码抛出适当的异常。您需要从一开始就设置字符编码。尝试发送适当的内容类型标题,例如内容类型:text/html;charset=utf-8以修复正确的编码。Web服务的标准一致性。检查您的响应标题 此外,在服务器端,如果浏览器没有正确处理服务器发送的编码,则通过分配新字符串强制编码。还可以通过执行单个each_byte&0x80检查编码的utf-8字符串中的每个字节,验证结果是否为非零
boolean utfEncoded = true;
byte[] strBytes = queryString.getBytes();
for (int i = 0; i < strBytes.length(); i++) {
if ((strBytes[i] & 0x80) != 0) {
continue;
} else {
/* treat the string as non utf encoded */
utfEncoded = false;
break;
}
}
String realQueryString = utfEncoded ?
queryString : new String(queryString.getBytes(), "iso-8859-1");
布尔值=真;
字节[]strBytes=queryString.getBytes();
对于(int i=0;i
还有,我希望它能对你有所帮助。我问了同样的问题 我最近找到了一个解决方案,对我来说效果很好。你可能想试试看。这是你需要做的
String name = fixEncoding(request.getParameter("name"));
你可以一直这样做。未更改编码正确的字符串
代码见附件。祝你好运
public static String fixEncoding(String latin1) {
try {
byte[] bytes = latin1.getBytes("ISO-8859-1");
if (!validUTF8(bytes))
return latin1;
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
// Impossible, throw unchecked
throw new IllegalStateException("No Latin1 or UTF-8: " + e.getMessage());
}
}
public static boolean validUTF8(byte[] input) {
int i = 0;
// Check for BOM
if (input.length >= 3 && (input[0] & 0xFF) == 0xEF
&& (input[1] & 0xFF) == 0xBB & (input[2] & 0xFF) == 0xBF) {
i = 3;
}
int end;
for (int j = input.length; i < j; ++i) {
int octet = input[i];
if ((octet & 0x80) == 0) {
continue; // ASCII
}
// Check for UTF-8 leading byte
if ((octet & 0xE0) == 0xC0) {
end = i + 1;
} else if ((octet & 0xF0) == 0xE0) {
end = i + 2;
} else if ((octet & 0xF8) == 0xF0) {
end = i + 3;
} else {
// Java only supports BMP so 3 is max
return false;
}
while (i < end) {
i++;
octet = input[i];
if ((octet & 0xC0) != 0x80) {
// Not a valid trailing byte
return false;
}
}
}
return true;
}
公共静态字符串固定编码(字符串1){
试一试{
字节[]字节=1.getBytes(“ISO-8859-1”);
如果(!validUTF8(字节))
返回拉丁语1;
返回新字符串(字节,“UTF-8”);
}捕获(不支持的编码异常e){
//不可能,不受限制地扔
抛出新的IllegalStateException(“无拉丁文1或UTF-8:+e.getMessage());
}
}
公共静态布尔validUTF8(字节[]输入){
int i=0;
//检查物料清单
如果(input.length>=3&&(input[0]&0xFF)==0xEF
&&(输入[1]&0xFF)==0xBB&(输入[2]&0xFF)==0xBF){
i=3;
}
内端;
对于(int j=input.length;i
编辑:由于各种原因,您的方法不起作用。当存在编码错误时,您不能指望从Tomcat得到什么。有时你会� 或其他时候,您不会得到任何结果,getParameter()返回null。假设您可以检查“?”,那么您的查询字符串包含有效的“?”
此外,你不应该拒绝任何请求。这不是用户的错。正如我在最初的问题中提到的,浏览器可以用UTF-8或拉丁语-1对URL进行编码。用户没有控制权。你需要两者都接受。将servlet更改为Latin-1将保留所有字符,即使它们是错误的,这样我们就有机会修复它或将其丢弃
我在这里发布的解决方案并不完美,但它是迄今为止我们找到的最好的解决方案 我一直在研究一个类似的“猜测编码”问题。最好的解决方案是了解编码。除此之外
module Encoding
UTF8RGX = /\A(
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*\z/x unless defined? UTF8RGX
def self.utf8_file?(fileName)
count = 0
File.open("#{fileName}").each do |l|
count += 1
unless utf8_string?(l)
puts count.to_s + ": " + l
end
end
return true
end
def self.utf8_string?(a_string)
UTF8RGX === a_string
end
end
CharsetDecoder UTF8Decoder =
Charset.forName("UTF8").newDecoder().onMalformedInput(CodingErrorAction.REPORT);
value = value.replaceAll("\\p{Cntrl}", "");
CharsetDecoder ebcdicDecoder = Charset.forName("IBM1047").newDecoder();
ebcdicDecoder.onMalformedInput(CodingErrorAction.REPORT);
ebcdicDecoder.onUnmappableCharacter(CodingErrorAction.REPORT);
CharBuffer out = CharBuffer.wrap(new char[3200]);
CoderResult result = ebcdicDecoder.decode(ByteBuffer.wrap(bytes), out, true);
if (result.isError() || result.isOverflow() ||
result.isUnderflow() || result.isMalformed() ||
result.isUnmappable())
{
System.out.println("Cannot decode EBCDIC");
}
else
{
CoderResult result = ebcdicDecoder.flush(out);
if (result.isOverflow())
System.out.println("Cannot decode EBCDIC");
if (result.isUnderflow())
System.out.println("Ebcdic decoded succefully ");
}