大多数文本编辑器在打开文件时都能够自动检测文件的编码,那它是怎样做到的呢?我虽然没有实现过一个文本编辑器,但是可以猜测的是,它有一个默认的编码集合,然后尝试用每一个编码去解码打开的文件,如果能够解码则表示这就是文件的正确编码。有一些特殊情况,有些编码在文件开头有特殊的标记字节,因而可以很快检测,这里不考虑。现在的核心问题就是如何决定一个编码是否能够解码一个文件,在Java1.4中可以利用nio中的Charset来解决这个问题。
/**
* 测试输入字节流是否能够使用指定的字符集解码。
*/
public static boolean canDecode(InputStream input, Charset charset) throws IOException {
ReadableByteChannel channel = Channels.newChannel(input);
CharsetDecoder decoder = charset.newDecoder();
ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
CharBuffer charBuffer = CharBuffer.allocate(1024);
boolean endOfInput = false;
while (!endOfInput) {
int n = channel.read(byteBuffer);
byteBuffer.flip(); // flip so it can be drained
endOfInput = (n == -1);
CoderResult coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);
charBuffer.clear();
if (coderResult == CoderResult.OVERFLOW) {
while (coderResult == CoderResult.OVERFLOW) {
coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);
charBuffer.clear();
}
}
if (coderResult.isError()) {
return false;
}
byteBuffer.compact(); // compact so it can be refilled
}
CoderResult coderResult;
while ((coderResult = decoder.flush(charBuffer)) == CoderResult.OVERFLOW) {
charBuffer.clear();
}
if (coderResult.isError()) {
return false;
}
return true;
}
要理解上面的代码必须熟悉对Buffer和Channel的操作以及解码的过程。上面的代码只是决定能不能解码,下面代码能够解码出的内容写到字符输出流中(也就是Writer),它要更复杂一些。
/**
* 使用指定的字符集解码字节输入流,并将它写入到字符输出流中,如果发生解码错误则返回false,否则返回true,
* 输入中的无效字节序列将被忽略。
*/
public static boolean decode(InputStream input, Writer output, Charset charset) throws IOException {
ReadableByteChannel channel = Channels.newChannel(input);
CharsetDecoder decoder = charset.newDecoder();
ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
CharBuffer charBuffer = CharBuffer.allocate(1024);
boolean endOfInput = false;
boolean error = false;
while (!endOfInput) {
int n = channel.read(byteBuffer);
byteBuffer.flip(); // flip so it can be drained
endOfInput = (n == -1);
CoderResult coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);
error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);
if (coderResult != CoderResult.UNDERFLOW) {
while (coderResult != CoderResult.UNDERFLOW) {
coderResult = decoder.decode(byteBuffer, charBuffer, endOfInput);
error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);
}
}
byteBuffer.compact(); // compact so it can be refilled
}
CoderResult coderResult;
while ((coderResult = decoder.flush(charBuffer)) != CoderResult.UNDERFLOW) {
error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);
}
error = drainCharBuffer(error, byteBuffer, charBuffer, coderResult, output);
output.flush();
return !error;
}
private static boolean drainCharBuffer(boolean error, ByteBuffer byteBuffer,
CharBuffer charBuffer, CoderResult coderResult, Writer output) throws IOException {
// write charBuffer to output
charBuffer.flip();
if (charBuffer.hasRemaining())
output.write(charBuffer.toString());
charBuffer.clear();
if (coderResult.isError()) {
error = true;
byteBuffer.position(byteBuffer.position() + coderResult.length()); // ignore invalid byte sequence
}
return error;
}
要注意byteBuffer的大小不能太小以至于比一个字符的最大字节数还要小,比如说utf-8的每个字符最多可能占用4个字节,如果设置byteBuffer的大小为3,解码结果可能总是CoderResult.UNDERFLOW,但是又无法再往byteBuffer填充数据,因而会出现死循环。
另外要注意的是,程序可能得到错误的结果,如:
String s = "abc中国";
byte[] utf8Bytes = s.getBytes(Charset.forName("utf-8"));
byte[] gbkBytes = s.getBytes(Charset.forName("gbk"));
CharArrayWriter writer = new CharArrayWriter();
System.out.println(decode(new ByteArrayInputStream(utf8Bytes), writer, Charset.forName("utf-8")));
System.out.println(writer.toString());
writer = new CharArrayWriter();
System.out.println(decode(new ByteArrayInputStream(utf8Bytes), writer, Charset.forName("gbk")));
System.out.println(writer.toString());
输出结果:
true
abc中国
true
abc涓浗
可以看到用utf-8编码的字节流仍然可以用gbk进行解码,但是解码的结果却不对。这是偶然情况,将字符串换成"中国人",则用gbk就不能解码了。
分享到:
相关推荐
Java自动识别文件字符编码工具类 参考博客 https://blog.csdn.net/superbeyone/article/details/103036914 使用方式: String encode = EncodingDetect.getFileEncode(geoJsonFile); log.info("系统检测到文件[ {}...
用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...
用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...
用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...
用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...
用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...
用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...
Java编写的显示器显示模式检测程序 2个目标文件 内容索引:JAVA源码,系统相关,系统信息检测 用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
12反射 是java程序开发的特征之一,允许java程序对自身进行检查,并能直接操作程序的内部属性; instanceof操作符,instanceof.java; 获取类的信息,ViewClassInfoJrame.java; 动态调用类的方法,CallMetod.java; ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...