正确使用字节流按照指定字符编码获取字符串

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

一、一个方法引发的奇葩现象

   在实际工作中,我们经常会遇到读取文本或网络中的内容,但是由于编码格式的不统一,往往得到的结果总是一团乱码,这就需要将文本按照它原本保存的编码解析成正确的内容。下面这段代码使用了字节按照指定字符编码获取字符串,可能很多人也使用过它,其实呢这段代码是错误的,如果读取的内容很多的话,会发现只有个别的字符乱码,而大部分的字符都能正确显示。

private String inputStreamToString(InputStream is, String encoding) {
    try {
         byte[] b = new byte[1024];
         String res = "";
         if (is == null) {
                return "";
         }
         
         int bytesRead = 0;
         while (true) {
             bytesRead = is.read(b, 0, 1024); // return final read bytes counts
             if (bytesRead == -1) {// end of InputStream
                    return res;
             }
             res += new String(b, 0, bytesRead, encoding); // convert to string using bytes
          }
      } catch (Exception e) {
            e.printStackTrace();
            System.out.print("Exception: " + e);
            return "";
      }
}
一段编码为UTF8的测试文字
使用上面的方法输出结果

之所以会出现如此奇葩的现象,是因为这个方法没有正确理解字符编码和字节的关系。

二、字符编码和字节的关系

       世界上存在着很多编码,比如ASCII码,unicode编码等。ASCII码只能表示英文字符和其他诸如空格等特殊字符,占用八位二进制位,最前面以为统一设为0,后面的7位根据ASCII码表对应表示128个字符;而unicode编码的野心最大,它理论上包含了世界上所有的字符,它和ASCII一样,设置了字符的对应位置编号,比如1表示A,2表示B,10001表示“好”(这里只是打个比方,并不是真实的编码与字符对应),但是这样编号计算机是看不懂的,计算机只知道用二进制位来表示字符,但是我们不可能只简单的用二进制表示字符,比如用1表示A,用10表示B...,计算机无法知道10表示是一个整体还是1和0的组合,所以需要设计让计算机能读懂的unicode编码,担当此重任的就包括UTF8、UTF16和UTF32等。

       UTF-8是一种变长的编码方式,字节长度从1到4不等;UTF-16要么是2个字节,那么是4个字节;UTF-32使用4个字节。(具体编码原理点击阮一峰的博客

三、奇葩现象的大揭秘

       开篇的方法将字节流(InputStream是以字节为单位的输入流)以1024个字节为单位按照UTF-8编码为字符串的,最后将所有的字符串拼接起来。而UTF-8是变长的字节编码方式,编码成一个字符需要1至4个不等的字节,1024个字节想要编码成正确的字符时,可能会多出字节或缺少字节,往往导致1024字节中的最后几个字节无法正常识别成为乱码(字符和编码的对应关系根据编码方式的不同,采取了标志位和编号对应等算法得出正确字符,否则就会乱码,具体原理请看  阮一峰的博客)。

       要想使用1024个字节读取字节流而永不乱码,只能是读取使用UTF-32编码的字节流,因为UTF-32使用了4个字节编码,恰好1024是4的倍数,所以编码时所有的字节都能正确的被编码成字符。

四、如何远离奇葩现象

       既然无法像开篇方法中使用固定长度字节编码字符串再拼接的方式,那么只能取得字节流中的全部字节,再将全部字节编码成字符串,才不会导致编码时多出字节和缺少字节的情况,请看下面的正确方法。

private static String inputStreamToString(InputStream is, String encoding) throws IOException{
		
		int count = is.available();	// 字节流的字节数
		
		/** 由于InputStream.read(byte[] b)方法并不能一次性读取太多字节,所以需要判断是否已读取完毕 **/
		byte[] b = new byte[count];	// 
		int readCount = 0; // 已经成功读取的字节的个数
		while (readCount < count) {
		  readCount += is.read(b, readCount, count - readCount);
		}
		
		return new String(b, 0, count, encoding);
}

测试编码结果正常

👉👉👉 自己搭建的租房网站:全网租房助手,m.kuairent.com,每天新增 500+房源

JavaIO流学习总结,字符编码字节流+字符 Java 中的 IO 流主要包括字节流字符流两种,字节流主要用于处理二进制数据,字符流主要用于处理文本数据。推荐使用commons-io或Hutool-io第三方工具类。 阅读详情

相关推荐

SyberOS应用开发之字符编码解决方案

1. 字符编码的起源和解决方案 为什么会谈起字符编码呢?这个事情的根源是英文字符使用的ASCII编码无法表示世界上其他语言的字符.所以就会导致ASCII之外的字符显示的时候的乱码问题. 为了解决字符显示乱码的问题,不同国家和地区针对自己使用字符进行了编码,但每种编码都无法涵盖所有字符,所以Unicode编码诞生了.Unicode的诞生是为了解决所有字符使用统一的一套编码,这样就统一了字符显示. 但是实际中,各个系统都有自己的编码,以中文为例,Window系统下编辑的文本有多种编码方式,如GBK,要显

m0_49214895的博客 335

bytes 字节数组,指定编码格式转换为一般的字符串

前提 部分数据存储或者转换为字节(通过getBytes方法)后,一般获取出来的都是字节数组的数据形式 转化 使用string构造方法,注意导包路径(这里使用UTF-8做示例) string为java.lang包下,不是其他的同名String. // StandardCharsets来源 package java.nio.charset; String columnMappingStr = new String(columnMapping, StandardCharsets.UTF_8); ...

weixin_44131922的博客 1951

字节bytes,编码和解码

00000000 :8位代表一个字节bytes gbk编码(国标扩):一个中文占用2个字节 utf-8(万国码):一个中文占用3个字节 gbk和utf-8不能直接进行转换,否则会出现乱码(因为他们的字节占用不一样) # bytes,编码和解码 # asci码 s='周杰伦' s1 = s.encode('gbk') print(s1) # 输出:b'\xd6\xdc\xbd\xdc\xc2\xd7' # gbk编码,一个中文占2个字节,所以这里3个中文,占用6个字节 s2 = s.encode(

wanggaoxingH的博客 3498

UTF8编码字节流错误小析

对于对称加密,首先生成字节流形式的key与iv,之后对指定字符串进行加密,然后将key与iv转换为字符串传输到另一端,另一端再将字符串转换回字节流完成解密工作.在key与iv的字节流字符串之间的转换中如果选用Utf8编码,则可能会让转换回的二进制流与之前的字节流不一致.代码如下: byte[] bytes = new byte[]{146, 174, 27, 74, 223, 159, 52, 1

Chydison 1702

JAVA Io 流 字符编码编码和解码的方法

字节流,用中文可能会出现乱码计算机中存储的信息都是二进制表示的按照某种规则将字符编程二进制 再存储到计算机中,成为编码按照同样规则,在计算机中二进制数解析出来,成为解码编码和解码的方式必须一致,否则会导致乱码。

m0_73859807的博客 1434

【转】关于字符编码,你所需要知道的

字符编码的问题看似很小,经常被技术人员忽视,但是很容易导致一些莫名其妙的问题。这里总结了一下字符编码的一些普及性的知识,希望对大家有所帮助。 还是得从ASCII码说起 说到字符编码,不得不说ASCII码的简史。计算机一开始发明的时候是用来解决数字计算的问题,后来人们发现,计算机还可以做更多的事,例如文本处理。但由于计算机只识“数”,因此人们必须告诉计算机哪个数字来代表哪个特定字符,例如65代表字母‘A’,66代表字母‘B’,以此类推。但是计算机之间字符-数字的对应关系必须得一致,否则就会造成同一段数字在

sinolover的专栏 933

字符串字符编码

由于计算机是美国人发明的,因此,最早只有127个字符编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。 但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。 因此,Unicode应运而生。Unicode把所有语言都...

徐志的博客 2550

【PHP字符编码必知必会】:使用mb_strlen准确获取中文字符串长度的5个关键点

轻松解决PHP中文字符串长度计算难题,准确使用mb_strlen函数处理多字节字符。涵盖编码设置、参数选择、中英文混合场景等5个关键点,避免strlen的常见错误。掌握正确方法,确保字符统计精准无误,值得收藏。

DeepNest的博客 828

字节流字符流的区别详解

字节流字符流 先来看一下流的概念: 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。 程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。 字节流字符流 在java.io包中操作文件内容的主要有两大类:字节流字符流,两类都分为输入和输出操作。在字节流中输出数据主要是

zxman660的专栏 16万+

C语言实现字节流与十六进制字符串的相互转换

//字节流转换为十六进制字符串 //(例如:char数组中原先存储的{0x2e},转换之后变成了char数组中存储的{0x32,0x65}) void ByteToHexStr(const unsigned char* source, char* dest, int sourceLen) {     short i;     unsigned char highByte, lowByte;  ...

u012871930的博客 1425

js 字符串字节流字节流字符串,无编码问题

<script type="text/javascript"> function stringToByte(str) { var bytes = new Array(); var len, c; len = str.length; for (var i = 0; i < len; i++) { c = str.charCodeAt(i); if (c >= 0x010000 && c <= 0x1

小程子的博客 4982

字符串+流+java_java字节流字符

java字节流字符流java字节流字符流JavaIO流,是一种计算机用语。主要是用于处理数据的传输。我们可以把流理解为一种信息传输的媒介。流的分类字节传输方式字符传输方式输入输出输入输出IO流字节流字符流InputStreamOutputStreamReaderWriter这里列举的类都是顶层的类,没有详细展开,对于各个细分的应用领域会用到不同的类,比如文件流就会用到FileInputStre...

weixin_29557763的博客 1174

字节流是什么

字节流(Byte Stream)是一种数据传输方式,它是字节的有序序列。在计算机中,所有数据都以字节的形式表示,因此字节流是最基本的数据传输单元。字节流可以用来传输各种类型的数据,例如文本文件、图像、音频、视频等。它们通常被处理为一系列连续的字节,不考虑其具体的数据类型或语义。因此,字节流可以是纯文本数据、二进制数据,甚至是复杂的文件格式(如PDF、JPEG等)。在计算机编程中,字节流经常用于文件输入/输出(I/O)操作,网络通信,以及各种数据处理任务中。

u010674101的专栏 2629

在什么时候使用字节流字符流?

字节流(FileInputStream和FileOutputStream) 1、InputStream 和OutputStream,两个是为字节流设计的,主要用来处理字节或二进制对象 2、字节流处理单元为1个字节,操作字节和字节数组。 3、如果是音频文件、图片、歌曲,就用字节流好点,所以它对多国语言支持性比较好! 字符流(FileReader和FileWriter) 1、Reader和 Write...

yicmsun的博客 2447

字节流字符

在Java.io包中操作文件内容主要有两大类:字节流字符流,两类都分为输入输出操作。所有文件的存储都是按字节(byte)来存储的,在磁盘上保留的并不是文件的字符而是把字符编码成字节,再存储这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节的读取以形成字节序列。

qq_59808309的博客 2386

C++_字符串常量Literal的前缀, 整数后缀, unicode字符编码, utf-8

catalog整数常量前缀二进制0B八进制0十六进制0X后缀U字符常量八进制十六进制unicodeis_integral 整数常量 前缀 二进制0B 14 == 0B1110 0B 0b都可以 写成: 0B2, 自然是报错的; 因为二进制只能有0 和 1 八进制0 很奇怪, 八进制很特别, 他不像 (2/16)进制的 命名规则0b, 0x, 他就是一个0, 并不是0o (octal: 八进制) 8 == 010 08自然是错的 十六进制0X 16 == 0X10 0X 0x都可以 0X?, ? = [0 -

WChang的博客 2243

Java:字符编码,Java 字节流字符流的转换

转换流可以将一个包装成,或者将一个字符流包装成字节流。这种转换通常用于处理文本数据,如读取文本文件或将数据从网络传输到应用程序。转换流主要有两种类型:InputStreamReader 和 OutputStreamWriter。InputStreamReader 将一个字节输入流转换为一个字符输入流,而 OutputStreamWriter 将一个字节输出流转换为一个字符输出流。它们使用指定字符集将字节流字符流之间进行转换。常用的字符集包括 UTF-8、GBK、ISO-8859-1 等。

weiweiqiao的专栏 194

字符流和字节流使用和区别

本文内容: 什么是流 字节流(可以处理文本,图片和视频资源) 字符流(用来处理文本)   什么是流   流是个抽象的概念,是对输入输出设备的抽象,输入流可以看作一个输入通道,输出流可以看作一个输出通道。 输入流是相对程序而言的,外部传入数据给程序需要借助输入流。 输出流是相对程序而言的,程序把数据传输到外部需要借助输出流。   什么是字节流字节流--传输过程中,传输数据的...

qq_38125626的博客 789

uwa和java_java字节流字符

JavaIO流,是一种计算机用语。主要是用于处理数据的传输。我们可以把流理解为一种信息传输的媒介。流的分类字节传输方式字符传输方式输入输出输入输出IO流字节流字符流InputStreamOutputStreamReaderWriter这里列举的类都是顶层的类,没有详细展开,对于各个细分的应用领域会用到不同的类,比如文件流就会用到FileInputStream等,线程间的通信就会用到PipedIn...

weixin_34337678的博客 1565
上一篇: prototype详解及prototype、new之间的关系
下一篇: 对面向对象设计OOD七大原则的理解
祥先生
博客等级 码龄14年 48粉丝 25原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值