`
lifethinker
  • 浏览: 71200 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

用Java自动检查文件的编码方式

    博客分类:
  • java
阅读更多

大多数文本编辑器在打开文件时都能够自动检测文件的编码,那它是怎样做到的呢?我虽然没有实现过一个文本编辑器,但是可以猜测的是,它有一个默认的编码集合,然后尝试用每一个编码去解码打开的文件,如果能够解码则表示这就是文件的正确编码。有一些特殊情况,有些编码在文件开头有特殊的标记字节,因而可以很快检测,这里不考虑。现在的核心问题就是如何决定一个编码是否能够解码一个文件,在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就不能解码了。

 

 

分享到:
评论
1 楼 zhuhelong520 2013-03-29  
测试输入字节流是否能够使用指定的字符集解码。
这个可能会出现死循环哟
走28行 n值永远不变的话···卡死在里面了

相关推荐

    Java自动识别文件字符编码工具类.rar

    Java自动识别文件字符编码工具类 参考博客 https://blog.csdn.net/superbeyone/article/details/103036914 使用方式: String encode = EncodingDetect.getFileEncode(geoJsonFile); log.info("系统检测到文件[ {}...

    java源码包---java 源码 大量 实例

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    JAVA上百实例源码以及开源项目

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    java源码包4

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    java源码包3

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    java源码包2

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    JAVA上百实例源码以及开源项目源代码

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java编写的显示器显示模式检测程序 2个目标文件 内容索引:JAVA源码,系统相关,系统信息检测 用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作...

    java开源包11

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包6

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包8

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包9

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包4

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包101

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包5

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包10

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java jdk实列宝典 光盘源代码

    12反射 是java程序开发的特征之一,允许java程序对自身进行检查,并能直接操作程序的内部属性; instanceof操作符,instanceof.java; 获取类的信息,ViewClassInfoJrame.java; 动态调用类的方法,CallMetod.java; ...

    java开源包3

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    java开源包1

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

Global site tag (gtag.js) - Google Analytics