我的Java开发学习之旅------>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常

本文详细分析了在使用 Java NIO 的 Channel 和 Buffer 进行文件操作时,遇到的 MalformedInputException 异常的原因,并提供了四种有效的解决方法,包括调整缓冲区大小、更改字符集解码方式、逐字节判断合法性以及使用 FileChannel.map 方法。通过实例演示了解决方案的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了 java.nio.charset.MalformedInputException: Input length = 1异常,具体如下:
java.nio.charset.MalformedInputException: Input length = 1
	at java.nio.charset.CoderResult.throwException(CoderResult.java:260)
	at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:781)
	at cn.fuxi.nio.ReadFile.main(ReadFile.java:37)

具体的Java源代码如下:ReadFile.java
public class ReadFile {
	public static void main(String[] args) {
		FileInputStream fis;
		try {
			fis = new FileInputStream("a.txt");
			FileChannel channel = fis.getChannel();
			// 定义一个ByteBuffer,用于重复读取数据
			ByteBuffer byteBuffe = ByteBuffer.allocate(64);// 每次取出64字节
			// 将FileChannel的数据放入ByteBuffer中
			while (channel.read(byteBuffe) != -1) {
				// 锁定ByteBuffer的空白区
				byteBuffe.flip();
				/* 创建Charset对象 */
				Charset charset = Charset.forName("GBK");
				// 创建解码器
				CharsetDecoder charsetDecoder = charset.newDecoder();
				// 将ByteBuffer的内容转码
				CharBuffer charBuffer = charsetDecoder.decode(byteBuffe);
				// CharBuffer charBuffer = charset.decode(byteBuffe);
				System.out.println(charBuffer);
				// 将ByteBuffer初始化,为下一次读取数据做准备
				byteBuffe.clear();
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}
我要读取的a.txt文件内容很简单,如下所示:
This is just test for FileChannel

小心会报异常: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()方法。
即将下面的代码:
/* 创建Charset对象 */
				Charset charset = Charset.forName("GBK");
				// 创建解码器
				CharsetDecoder charsetDecoder = charset.newDecoder();
				// 将ByteBuffer的内容转码
//				CharBuffer charBuffer = charsetDecoder.decode(byteBuffe);
改为:
/* 创建Charset对象 */
				Charset charset = Charset.forName("GBK");
				CharBuffer charBuffer = charset.decode(byteBuffe);

但是这样改掉之后,也会出现下面的乱码问题,所以也不提倡。
 
This is just test for FileChannel
小心会报异常:java.nio.charset.MalformedInputException: Input length = 1,看到底是什么鬼原因弄成的。
This is just test for FileChannel
小心会报异常:java.nio.charset.MalformedInputException: Input length = 1,看到底?
鞘裁垂碓蚺傻摹?

第三个解决方法:

每次都去判断一下Bytebuffer中最后一个字节是否合法。如果不合法,则说明这个字节是双字节汉字的一部分,这样我们解码时就不要包含这个字节,而是把这个字节放进下次解码之前的Bytebuffer中。这样做,系统就不会抛出“无法正确解码”这类的异常了。

该方法的具体解决代码怎么改,今天头脑有点痛,没时间改了,下次改了再发上来。(可以看看 http://songjianyong.iteye.com/blog/1399241 寻找思路)

第四种方法:

使用FileChannel.map()方法一次将所有文件内容映射到内存中,但是这样如果读取的文件过大的话,会引起性能的下降。代码如下:
public class FileChannelTest {
	public static void main(String[] args) {
		try {
			File file=new File("abc.txt");
			//以文件输入流FileInputStream创建FileChannel,以控制输入
			FileChannel inChannel=new FileInputStream(file).getChannel();
			//以文件输出流FileOutputStream创建FileChannel,以控制输出
			FileChannel outChannel=new FileOutputStream("a.txt").getChannel();
			//将FileChannel里的全部数据映射成ByteBuffer
			MappedByteBuffer  buffer=inChannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
			//直接将buffer里的数据全部输出
			outChannel.write(buffer);
			//再次调用buffer的clear()方法,复原limit、position的位置
			buffer.clear();
			//使用GBK字符集来创建解码器
			Charset charset=Charset.forName("GBK");
			//创建解码器(CharsetDecoder)对象
			CharsetDecoder decoder=charset.newDecoder();
			//使用解码器将ByteBuffer转换成CharBuffer
			CharBuffer charBuffer=decoder.decode(buffer);
			System.out.println(charBuffer);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}
哎,忙了一个晚上,还是没有真正的解决java.nio.charset.MalformedInputException: Input length = 1异常,惭愧。如果有大神来帮我解决解决,感激不尽。

==================================================================================================

  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址欧阳鹏_优快云博客-我的Android进阶之旅,Android常见错误解决之道,Java学习领域博主

==================================================================================================

`java.nio.charset.MalformedInputException` 是一种常见的编码异常,通常发生在处理字符集转换时遇到无法识别的字节序列。这种错误可能由多种原因引起,比如文件编码不匹配、数据传输过程中损坏或者读取的数据本身存在问题。 以下是针对该问题的具体分析和解决方案: ### 错误的根本原因 当程序尝试将字节数组解码为字符串时,如果输入流中的某些字节不符合目标字符集的规定,则会抛出 `MalformedInputException` 异常[^2]。这通常是由于源数据使用的编码与预期的目标编码不符造成的。 ### 解决方案 #### 方法一:指定正确的字符编码 确保在读写文件或网络请求时指定了正确的字符编码。例如,在 Android 中可以显式设置 UTF-8 编码来避免此类问题: ```java InputStream inputStream = ...; // 假设这是你的输入流 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } reader.close(); ``` 上述代码通过 `StandardCharsets.UTF_8` 明确设置了字符集,从而减少了因默认编码不同而导致的潜在问题[^3]。 #### 方法二:捕获并处理 MalformedInputException 可以通过捕获异常的方式优雅地处理非法输入的情况: ```java try { CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) .replaceWith("[?]"); ByteBuffer byteBuffer = ByteBuffer.wrap(...); // 替换为实际字节缓冲区 CharBuffer charBuffer = CharBuffer.allocate(1024); CoderResult result = decoder.decode(byteBuffer, charBuffer, true); if (!result.isUnderflow()) { result.throwException(); // 如果不是正常结束则抛出异常 } } catch (CharacterCodingException e) { System.err.println("Invalid character encoding detected."); } ``` 这里使用了 `CharsetDecoder` 并配置其行为替换非法字符而不是直接错[^4]。 #### 方法三:验证 HTTP 请求头中的 Content-Type 和 Encoding 如果你正在执行一个基于 HTTP 的操作(如调用 RESTful API),请确认服务器返回的内容类型及其对应的编码是否正确。例如,你可以利用 OkHttp 库手动解析响应体如下所示: ```java Request request = new Request.Builder().url("http://example.com/").build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); // 获取内容的实际编码方式 MediaType mediaType = response.body().contentType(); Charset charset = mediaType != null ? mediaType.charset(StandardCharsets.UTF_8) : StandardCharsets.UTF_8; // 使用指定编码读取正文 String body = response.body().string(); System.out.println(body); ``` 此方法能够有效防止因为服务端未声明明确编码而引发的问题[^5]。 --- ### 总结 为了彻底解决 `java.nio.charset.MalformedInputException` 错误,建议从以下几个方面入手: 1. **统一编码标准**:始终采用一致的字符集(推荐 UTF-8)。 2. **增强健壮性**:合理设计逻辑以应对可能出现的非法输入情况。 3. **检查外部依赖**:对于来自第三方接口的数据,务必核实其元信息(如 MIME 类型和编码说明)。 ---
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

字节卷动

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值