在Java中,处理文本数据时,我们经常需要将字节流转换为字符流,或者将字符流转换为字节流。这种转换通常用于读取文本文件或将数据从网络传输到应用程序。Java提供了两种主要的转换流:InputStreamReader
和 OutputStreamWriter
。
1 转换流的作用
InputStreamReader
:将字节输入流转换为字符输入流。OutputStreamWriter
:将字符输出流转换为字节输出流。
这两种转换流使用指定的字符集(如UTF-8、GBK、ISO-8859-1等)在字节流和字符流之间进行转换。
2 编码与解码
在计算机中,数据通常以二进制形式存储和传输。编码是将原始数据(如文本、图像、视频、音频等)转换为二进制形式的过程,而解码则是将二进制数据转换回原始数据的过程。常见的编码和解码方式包括:
- ASCII编码:用于表示英文字母、数字和符号。
- Unicode编码:支持多种语言和字符集,常见的编码方式有UTF-8、UTF-16等。
- Base64编码:将二进制数据转换为ASCII字符,常用于网络传输。
- 图像编码:如JPEG、PNG、GIF等,用于图像的存储和传输。
- 视频编码:如H.264、AVC、MPEG-4等,用于视频的存储和传输。
简单来说:
- 编码:字符(能看懂的)–> 字节(看不懂的)
- 解码:字节(看不懂的)–> 字符(能看懂的)
以下是一个简单的编码和解码示例:
String str = "沉默王二";
String charsetName = "UTF-8";// 编码
byte[] bytes = str.getBytes(Charset.forName(charsetName));
System.out.println("编码: " + bytes);// 解码
String decodedStr = new String(bytes, Charset.forName(charsetName));
System.out.println("解码: " + decodedStr);
在这个示例中,首先定义了一个字符串变量 str
和一个字符集名称 charsetName
。然后,使用 Charset.forName()
方法获取指定字符集的 Charset
对象。接着,使用字符串的 getBytes()
方法将字符串编码为指定字符集的字节数组。最后,使用 new String()
方法将字节数组解码为字符串。
需要注意的是,编码和解码过程中必须使用相同的字符集,以确保数据的正确转换。
3 字符集
字符集(Charset)是一组字符的集合,每个字符都有一个唯一的编码值,称为码点。常见的字符集包括:
- ASCII字符集:包含128个字符,每个字符使用7位二进制编码。
- Unicode字符集:包含世界上几乎所有的字符,支持多种语言和字符集。常见的编码方式有UTF-8、UTF-16等。
- GBK字符集:包含GB2312字符集中的字符,并扩展了许多其他汉字字符和符号。
4 乱码问题
当使用不同的编码方式读取或写入文件时,可能会出现乱码问题。例如,将字符串按GBK编码保存到文件中,然后使用UTF-8编码读取文件,就会出现乱码。
以下是一个乱码问题的示例:
String s = "沉默王二!";try {// 将字符串按GBK编码方式保存到文件中OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("logs/test_utf8.txt"), "GBK");out.write(s);out.close();FileReader fileReader = new FileReader("logs/test_utf8.txt");int read;while ((read = fileReader.read()) != -1) {System.out.print((char)read);}fileReader.close();
} catch (IOException e) {e.printStackTrace();
}
在这个示例中,文件中的GBK编码字符在使用UTF-8编码方式解析时无法正确解析,从而导致乱码。
5 使用转换流解决乱码问题
为了解决乱码问题,我们可以使用InputStreamReader
和OutputStreamWriter
进行字节流和字符流之间的转换。
5.1 InputStreamReader
InputStreamReader
是Reader
类的子类,用于将字节流转换为字符流。它支持指定的字符集编码方式。
构造方法:
InputStreamReader(InputStream in)
:使用默认字符集创建字符流。InputStreamReader(InputStream in, String charsetName)
:使用指定字符集创建字符流。
常用方法:
read()
:从输入流中读取一个字符的数据。read(char[] cbuf, int off, int len)
:从输入流中读取len
个字符的数据到指定的字符数组cbuf
中,从off
位置开始存放。ready()
:返回此流是否已准备好读取。close()
:关闭输入流。
示例:
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
解决乱码问题的示例:
String s = "沉默王二!";try {// 将字符串按GBK编码方式保存到文件中OutputStreamWriter outUtf8 = new OutputStreamWriter(new FileOutputStream("logs/test_utf8.txt"), "GBK");outUtf8.write(s);outUtf8.close();// 将字节流转换为字符流,使用GBK编码方式InputStreamReader isr = new InputStreamReader(new FileInputStream("logs/test_utf8.txt"), "GBK");// 读取字符流int c;while ((c = isr.read()) != -1) {System.out.print((char) c);}isr.close();
} catch (IOException e) {e.printStackTrace();
}
在这个示例中,我们使用InputStreamReader
将字节流转换为字符流,并指定GBK编码方式,从而避免了乱码问题。
5.2 OutputStreamWriter
OutputStreamWriter
是Writer
类的子类,用于将字符流转换为字节流。它支持指定的字符集编码方式。
构造方法:
OutputStreamWriter(OutputStream in)
:使用默认字符集创建字符流。OutputStreamWriter(OutputStream in, String charsetName)
:使用指定字符集创建字符流。
常用方法:
write(int c)
:向输出流中写入一个字符的数据。write(char[] cbuf, int off, int len)
:向输出流中写入指定字符数组cbuf
中的len
个字符,从off
位置开始。flush()
:将缓冲区的数据写入输出流中。close()
:关闭输出流。
示例:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("a.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("b.txt") , "GBK");
提高读写效率的示例:
try {// 从文件读取字节流,使用UTF-8编码方式FileInputStream fis = new FileInputStream("test.txt");// 将字节流转换为字符流,使用UTF-8编码方式InputStreamReader isr = new InputStreamReader(fis, "UTF-8");// 使用缓冲流包装字符流,提高读取效率BufferedReader br = new BufferedReader(isr);// 创建输出流,使用UTF-8编码方式FileOutputStream fos = new FileOutputStream("output.txt");// 将输出流包装为转换流,使用UTF-8编码方式OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");// 使用缓冲流包装转换流,提高写入效率BufferedWriter bw = new BufferedWriter(osw);// 读取输入文件的每一行,写入到输出文件中String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine(); // 每行结束后写入一个换行符}// 关闭流br.close();bw.close();
} catch (IOException e) {e.printStackTrace();
}
在这个示例中,我们使用缓冲流包装转换流,以提高读写效率。
6 小结
InputStreamReader
和OutputStreamWriter
是Java中用于字节流和字符流之间转换的重要工具。它们可以帮助我们解决字节流和字符流之间的转换问题,并避免乱码问题。在使用转换流时,务必指定正确的字符集编码方式,以确保数据的正确读取和写入。
7 思维导图
8 参考链接
Java 转换流:Java 字节流和字符流的桥梁