JAVA学习5_Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常

71 篇文章 0 订阅
在使用Flume读取文件的过程中,遇到了以下的错误:
ERROR ({pool-4-thread-1} SpoolDirectorySource.java[run]:256) [2015-10-30 02:06:36,030] - FATAL: Spool Directory source r1: { spoolDir: /home/hadoop/flumeSpool }: Uncaught exception in SpoolDirectorySource thread. Restart or reconfigure Flume to continue processing.
java.nio.charset.MalformedInputException: Input length = 1
	at java.nio.charset.CoderResult.throwException(CoderResult.java:281)
	at org.apache.flume.serialization.ResettableFileInputStream.readChar(ResettableFileInputStream.java:195)
继续查找原因,发现是读取的文件有问题。

这是网上的一片相关说明文章,虽然不是在使用Flume过程中遇到的,但是错误的原因是相同的。
最后问题没能解决,需要继续查找相关的方法。


今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了java.nio.charset.MalformedInputException: Input length = 1异常,具体如下:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. java.nio.charset.MalformedInputException: Input length = 1  
  2.     at java.nio.charset.CoderResult.throwException(CoderResult.java:260)  
  3.     at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:781)  
  4.     at cn.fuxi.nio.ReadFile.main(ReadFile.java:37)  

具体的Java源代码如下:ReadFile.java
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class ReadFile {  
  2.     public static void main(String[] args) {  
  3.         FileInputStream fis;  
  4.         try {  
  5.             fis = new FileInputStream("a.txt");  
  6.             FileChannel channel = fis.getChannel();  
  7.             // 定义一个ByteBuffer,用于重复读取数据  
  8.             ByteBuffer byteBuffe = ByteBuffer.allocate(64);// 每次取出64字节  
  9.             // 将FileChannel的数据放入ByteBuffer中  
  10.             while (channel.read(byteBuffe) != -1) {  
  11.                 // 锁定ByteBuffer的空白区  
  12.                 byteBuffe.flip();  
  13.                 /* 创建Charset对象 */  
  14.                 Charset charset = Charset.forName("GBK");  
  15.                 // 创建解码器  
  16.                 CharsetDecoder charsetDecoder = charset.newDecoder();  
  17.                 // 将ByteBuffer的内容转码  
  18.                 CharBuffer charBuffer = charsetDecoder.decode(byteBuffe);  
  19.                 // CharBuffer charBuffer = charset.decode(byteBuffe);  
  20.                 System.out.println(charBuffer);  
  21.                 // 将ByteBuffer初始化,为下一次读取数据做准备  
  22.                 byteBuffe.clear();  
  23.             }  
  24.         } catch (Exception e) {  
  25.             // TODO: handle exception  
  26.             e.printStackTrace();  
  27.         }  
  28.     }  
  29. }  

我要读取的a.txt文件内容很简单,如下所示:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. This is just test for FileChannel  
  2.   
  3. 小心会报异常:java.nio.charset.MalformedInputException: Input length = 1,看到底是什么鬼原因弄成的。  



查看了Java API的官方关于 MalformedInputException的说明如下:

Checked exception thrown when an input byte sequence is not legal for given charset, or an input character sequence is not a legal sixteen-bit Unicode sequence.

翻译过来就是:当输入字节序列对于给定 charset 来说是不合法的,或者输入字符序列不是合法的 16 位 Unicode 序列时,抛出此经过检查的异常。


说白了,会出现java.nio.charset.MalformedInputException异常,原因是“半个中文问题”。分析上面的程序,就是因为CharsetDecoder对ByteBuffer进行解码的时候,不能保证都可以成功解码成汉字,也许里面有“半个汉字“也说不准。说以当有半个汉字的时候就会出现该异常。
 举个例子,因为在GBK中字母占1byte,汉字占2byte。如"我ABC汉字d"这个字符串,截取5个字节的时候,应该是"我ABC",而截取8个字节的时候,应该是"我ABC汉",而不应该是"我ABC汉?",其中"?"为半个汉字,可理解为向前截取 。所以就会报异常。    (备注:将字符编码GBK改为UTF-8,则每个中文长度按3个字符计算 

我第一个的解决方法是:
将ByteBuffer byteBuffe = ByteBuffer.allocate(64);这行代码改为ByteBuffer byteBuffe = ByteBuffer.allocate(1024);
因为我要读取的a.txt文件不大,如果一次性读取1024个字节的话,大于a.txt文件的总大小,所以a.txt文件一次性就读完了。因此并不会报异常了。
但是如果我要读取的a.txt文件的大小大于1024个字节的话,该异常还是有可能会爆出来。所以该方法不对。

我第二个解决方法是:
CharsetDecoder.decode()方法去掉,直接直接使用Charset.decode()方法。
即将下面的代码:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 创建Charset对象 */  
  2.                 Charset charset = Charset.forName("GBK");  
  3.                 // 创建解码器  
  4.                 CharsetDecoder charsetDecoder = charset.newDecoder();  
  5.                 // 将ByteBuffer的内容转码  
  6. //              CharBuffer charBuffer = charsetDecoder.decode(byteBuffe);  
改为:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 创建Charset对象 */  
  2.                 Charset charset = Charset.forName("GBK");  
  3.                 CharBuffer charBuffer = charset.decode(byteBuffe);  

但是这样改掉之后,也会出现下面的乱码问题,所以也不提倡。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. This is just test for FileChannel  
  2. 小心会报异常:java.nio.charset.MalformedInputException: Input length = 1,看到底是什么鬼原因弄成的。  
  3. This is just test for FileChannel  
  4. 小心会报异常:java.nio.charset.MalformedInputException: Input length = 1,看到底?  
  5. 鞘裁垂碓蚺傻摹?  


第三个解决方法:每次都去判断一下Bytebuffer中最后一个字节是否合法。如果不合法,则说明这个字节是双字节汉字的一部分,这样我们解码时就不要包含这个字节,而是把这个字节放进下次解码之前的Bytebuffer中。这样做,系统就不会抛出“无法正确解码”这类的异常了。
该方法的具体解决代码怎么改,今天头脑有点痛,没时间改了,下次改了再发上来。(可以看看 http://songjianyong.iteye.com/blog/1399241 寻找思路)

第四种方法:使用FileChannel.map()方法一次将所有文件内容映射到内存中,但是这样如果读取的文件过大的话,会引起性能的下降。代码如下:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class FileChannelTest {  
  2.     public static void main(String[] args) {  
  3.         try {  
  4.             File file=new File("abc.txt");  
  5.             //以文件输入流FileInputStream创建FileChannel,以控制输入  
  6.             FileChannel inChannel=new FileInputStream(file).getChannel();  
  7.             //以文件输出流FileOutputStream创建FileChannel,以控制输出  
  8.             FileChannel outChannel=new FileOutputStream("a.txt").getChannel();  
  9.             //将FileChannel里的全部数据映射成ByteBuffer  
  10.             MappedByteBuffer  buffer=inChannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());  
  11.             //直接将buffer里的数据全部输出  
  12.             outChannel.write(buffer);  
  13.             //再次调用buffer的clear()方法,复原limit、position的位置  
  14.             buffer.clear();  
  15.             //使用GBK字符集来创建解码器  
  16.             Charset charset=Charset.forName("GBK");  
  17.             //创建解码器(CharsetDecoder)对象  
  18.             CharsetDecoder decoder=charset.newDecoder();  
  19.             //使用解码器将ByteBuffer转换成CharBuffer  
  20.             CharBuffer charBuffer=decoder.decode(buffer);  
  21.             System.out.println(charBuffer);  
  22.         } catch (Exception e) {  
  23.             // TODO: handle exception  
  24.             e.printStackTrace();  
  25.         }  
  26.     }  
  27. }  


哎,忙了一个晚上,还是没有真正的解决java.nio.charset.MalformedInputException: Input length = 1异常,惭愧。如果有大神来帮我解决解决,感激不尽。

原文来自:http://blog.csdn.net/ouyang_peng/article/details/46462379

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值