java inputstream编码格式_纯文本-FileInputStream的编码与解码方式

本文详细探讨了Java中FileInputStream在处理纯文本文件时的编码问题。默认情况下,FileInputStream按文件原有编码读取内容。以GBK编码的文本为例,使用GBK解码可得到正确结果,而使用UTF-8解码则会出现乱码。反之,UTF-8编码的文本用GBK解码同样会导致乱码。此外,对于中英文混合的文本,直接使用字节数组解码会出现问题,需要使用InputStreamReader进行字符转换。分析了字节数组容量对解码的影响,强调了在不同编码格式下,字节数组容量应与编码规则匹配,否则可能导致乱码。

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

前言:以下分析只针对纯文本

1.FileInputStream默认的编码方式就是文件的编码方式

即:源文件是什么编码方式,则利用FileInputStream默认读取的字节数组,就是什么编码方式。

例:纯文本采用“GBK”编码,文本内容如下(注意:文本是纯汉字):

你好世界我是潘小白

利用“GBK”字符集解码如下:

packagecn.edu.uestc.IO;import java.io.*;public classTestFileInputStream03 {public static voidmain(String[] args){//流

File file = new File("abc3.txt");//源

InputStream is = null;try{

is= newFileInputStream(file);//操作

byte[] bytes = new byte[4];//这里数组容量必须采用2的倍数,具体原因后面后谈

int len = -1;while ((len = is.read(bytes))!=-1){

String str= new String(bytes,0,len,"GBK");//利用GBK字符集,对FileInputStream读取的字节数组进行解码

System.out.print(str);

}

}catch(FileNotFoundException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}finally{//释放资源

try{if (null!=is){

is.close();

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

}/*output:

你好世界我是潘小白*/

分析:通过代码可知,我采用FileInputStream对格式为“GBK”的纯汉字文本读取,得到的字节数组,可以用"GBK"字符集对其完美解码;反推可知,FileInputStream默认读取的字节数组,其编码格式和原文件编码格式相同。接下来,用"UTF-8"对其进行解码试一试。。。

利用“UTF-8”字符集解码如下:

packagecn.edu.uestc.IO;import java.io.*;public classTestFileInputStream03 {public static voidmain(String[] args){//流

File file = new File("abc3.txt");//源

InputStream is = null;try{

is= newFileInputStream(file);//操作

byte[] bytes = new byte[4];//这里数组容量采用3的倍数,区别于上面GBK解码时2的倍数,具体原因后面谈

int len = -1;while ((len = is.read(bytes))!=-1){

String str= new String(bytes,0,len,"UTF-8");//利用UTF-8字符集对字节数组进行解码

System.out.print(str);

}

}catch(FileNotFoundException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}finally{//释放资源

try{if (null!=is){

is.close();

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

}/*output:

�������������С��//输出无法解码*/

分析:利用UTF-8无法解码,再次说明,FileInputStream默认读取的字节数组的编码格式,就是原文件的编码格式。

同理读者可以将纯文本(纯汉字文本)设置成UTF-8的编码格式,再分别采用“GBK”和“UTF-8”方式解码试一试,特别注意数组容量的选择,即:“纯汉字文本,GBK解码时,字节数组容量是2的倍数”、““纯汉字文本,UTF-8解码时,字节数组容量是3的倍数”,原因下面分析。

————————简单的分割————————

2.采用“GBK”对纯汉字文本解码时,字节数组容量是2的倍数;“UTF-8”对纯汉字文本解码时,字节数组容量是3的倍数。

原因是:“GBK”编码时,一个汉字是2个字节,“UTF-8”对常规汉字编码时,一个汉字是3个字节(UTF-8方式下,生僻汉字也可能会占4个字节,这种方式此处不谈)。

所以,你要对字节数组解码时,你首先必须成组的取字节(“GBK”模式下2的倍数一组,“UTF-8”模式下3的倍数一组),否则会将一个汉字的字节拆开,这样肯定会乱码,其对应着我上一篇文章提到的“字节数不全或者丢失情况,产生的乱码”。

此处,我们用代码做一下简单示范,原文本采用“GBK”编码,字节数组容量采用3,不是2的倍数:

packagecn.edu.uestc.IO;import java.io.*;public classTestFileInputStream03 {public static voidmain(String[] args){//流

File file = new File("abc3.txt");//源

InputStream is = null;try{

is= newFileInputStream(file);//操作

byte[] bytes = new byte[3];//不是2的倍数

int len = -1;while ((len = is.read(bytes))!=-1){

String str= new String(bytes,0,len,"GBK");//却用GBKJ解码

System.out.print(str);

}

}catch(FileNotFoundException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}finally{//释放资源

try{if (null!=is){

is.close();

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

}/*output:

你�檬�界�沂�潘�“�//也就第一个字取全了,解码出来,但是后面字节数乱了,也就无法解码了*/

结果看到,基本全部乱码。

同理,读者可以采用UTF-8的文本,而设置字节数组容量不是3的倍数,从而进行UTF-8解码,试试看;你会发现,即使编码-解码的字符集同步,但是字节数组中字节个数不对,同样乱码。

——————简单的分割线——————

上面问题2中,“编码-解码的字符集同步,字节数组中字节个数不匹配出现乱码”可以进一步延伸;

我们看到上面,都是纯汉字文本,没有任何英文字符(包括英文字母和英文标点),如果文本是,中英文混合怎么办,还能否采用上面的方式,对FileInputStream读取的字节进行解码呢??

答案是:不能,见下面分析。

3.中英文混合纯文本,用FileInputStream读取时,得到的字节数组无法采用上面 String str = new String(bytes,0,length,"CharacterSet")方式解码,应该采用字符转换流InputStreamReader。

(提示:这里不再考虑标点符号的事了,你可以将英文标点符号看出一个英文字母,中文下的标点看成一个普通汉字分析,因为同一种编码格式下,中文字母和中文标点占用字节数一样,英文字母和英文标点占用字节数一致)

原因:无论是"GBK"还是"UTF-8",英文占用1个字节,所以,当插入引文时,一定会改变字节个数混乱,无法保证“在GBK格式下,每个汉字的两个字节同时被字节数组读取”,也无法保证“在UTF-8格式下,每个汉字的三个字节同时被字节数组读取”,那么将导致后期解码时,出现乱码。

示例:文本格式是“GBK”,文本中插入了一个英文字母

你好p世界我是潘小白

代码如下:

packagecn.edu.uestc.IO;import java.io.*;public classTestFileInputStream03 {public static voidmain(String[] args){//流

File file = new File("abc3.txt");//源

InputStream is = null;try{

is= newFileInputStream(file);//操作

byte[] bytes = new byte[2];//字节数组容量采用2

int len = -1;while ((len = is.read(bytes))!=-1){

String str= new String(bytes,0,len,"GBK");//GBK解码,实现编码-解码格式匹配

System.out.print(str);

}

}catch(FileNotFoundException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}finally{//释放资源

try{if (null!=is){

is.close();

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

}/*output:

你好p�澜缥沂桥诵“�*/

结果分析:输出结果从字母p以后,出现乱码;这里选取得字节数组的容量是2,所以前两个汉字被一一读取,并完美解码,但是读取字母p得时候,因为其只占用一个字节,所以汉字“世”被取一个字节,留下一个字节,未被取走,所以导致“世”字无法被正确解码,而且这也引发连锁效应,后面得字都将被错误得读取,从而乱码。

总之:这也是乱码得一种情况,即“字节数丢失或者不完整造成乱码”。

这里,可能有人会有疑问,“如果将字节数组容量设置非常大,一次将中英文混合文本全部读取,然后再解码,这样不出现文字多次读取,造成汉字字节截断得情况,不就行了吗?”

是的,这种情况可以实现正确解码,但是如果文本超级大,这种方式是不现实得,因为字节数组得容量过大,不现实,还是乖乖的用字符转换流InputStreamReader吧。

下面用一个超大字节数组,将文本一次读取,并完美解码得代码示例:

packagecn.edu.uestc.IO;import java.io.*;public classTestFileInputStream03 {public static voidmain(String[] args){//流

File file = new File("abc3.txt");//源

InputStream is = null;try{

is= newFileInputStream(file);//操作

byte[] bytes = new byte[20];//数组容量超级大,一次能将中英混合文本全部读取完

int len = -1;while ((len = is.read(bytes))!=-1){

String str= new String(bytes,0,len,"GBK");

System.out.print(str);

}

}catch(FileNotFoundException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}finally{//释放资源

try{if (null!=is){

is.close();

}

}catch(IOException e) {

e.printStackTrace();

}

}

}

}/*output:

你好p世界我是潘小白//完美解码*/

除了上面的情况,读者也可以试试,对于中英文结合的文档,采用UTF-8编码-解码;或者故意将英文字体排布规则,即GBK格式下,2个英文一起排列,放在中文文本中,或者GBK格式下,2个英文一起排列,放在中文文本中。对其进行编码和解码,并分析一下原因。

补充一点:UTF-8编码格式下,一些生僻汉字占4个字节,所以将字节数组容量设置成3的倍数时,面对有生僻字的纯汉字文本,解码时也会出现乱码情况。

——————分割线——————

总结:

上面讨论的三个问题,问题1就是属于编码-解码字符集匹配问题,只是进一步说明了FileInputStream读取的字节数组是哪种编码方式;

问题2和3,是讨论在编码-解码字符集匹配情况下,字节个数不完整或者丢失时,解码时出现乱码的情况,从而说明了用FileInputStream读取时,得到的字节数组无法采用上面 String str = new String(bytes,0,length,"CharacterSet")方式解码,应该采用字符转换流InputStreamReader。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值