1.转换流的引入
在项目中新建了一个a.txt的文件,内容是 hello中国。我现在用字节输入流来读取并输出到控制台。当然复制文件是没有问题的啦。编码是utf-8。
package cn; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class FileInputStreamDemo1 { public static void main(String[] args) throws IOException { //创建字节输入流对象 InputStream is = new FileInputStream("a.txt"); //读取数据 int temp = 0; while((temp=is.read()) != -1){ System.out.print((char)temp); } //释放资源 is.close(); } }
hello
package cn; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class FileInputStreamDemo1 { public static void main(String[] args) throws IOException { //创建字节输入流对象 InputStream is = new FileInputStream("a.txt"); //读取数据 byte[] b = new byte[1024]; int len = 0; while((len=is.read(b)) != -1){ System.out.println(new String(b,0,len)); } //释放资源 is.close(); } }
hello中国
到这里,各位看官,是否觉得,字节流处理中文也是可以的啦,但是如果第1023是个中文字符,那么就惨了吧,一个中文字符是2个字节的啦。那么截取的时候,可能只截取一半。
所以呢,由于字节流操作中文不是"特别"方便,所以,java就提供了转换流。
字符流=字节流+编码表。
2.编码表
编码表:由字符及其对应的数值组成的一张表。
计算机只能识别二进制数据,早期由来是电信号。
为了方便应用计算机,让它可以识别各个国家的文字。
就将各个国家的文字用数字来表示,并一一对应,形成一张表。
ASCII:美国标准信息交换码。用一个字节的7位可以表示。
ISO8859-1:拉丁码表,欧洲码表。用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
GB18030:GBK的取代版本。
BIG-5码:通行于台湾、香港地区的一种繁体字编码方案,俗称"大五码"。
Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,java语言使用的就是unicode。
UTF-8:最多用三个字节来表示一个字符。
UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:
它将Unicode编码为00000000-0000007F的字符,用单个字节来表示
它将Unicode编码为00000080-000007FF的字符用两个字节表示
它将Unicode编码为00000800-0000FFFF的字符用3字节表示
3.编码和解码
首先,我来讲一个故事,情景设在抗战时期。那时候,我们的先辈,为了发送消息,都是通过某种手段,将明文转换为密文来发送。但是,各位看官,可能想明文是什么,密文又是什么鬼?比如:我们的先辈要发送“10月29号 凌晨2点 花果山水帘洞集合”,如果发报的人员,将这个字符串通过电报发送过去,那么就惨了,如果敌人一旦进行电报的拦截,那么我们的先辈就完蛋了,但是,我们的先辈们有这么傻吗?他们准备了一个小本子,里面记载了文字和数值对应的关系,比如我对应1,诸如此类,然后将上面的字符串,即“10月29号 凌晨2点 花果山水帘洞集合”,其实这就是明文了,通过那个小本子,当然专业的说话,这个小本本就是编码表,将“10月29号 凌晨2点 花果山水帘洞集合”转换为编码表对应的数值,然后通过电报发送过去,那么,接收方只要有和发送方一样的编码表就可以了。然后接收方在接收到电报的时候,需要通过编码表,将数值转换为字符串,即将密文转换为明文。那么,即使这中间,敌人截取了电报,也没卵用,因为他们没有编码表。
package com; import java.util.Arrays; /** * public byte[] getBytes(String charsetName):使用指定的字符串集合把字符串编码为字节数组 * public String(byte[] bytes,String charsetName):通过指定的字符集解码字节数组 * * 编码:把看的懂的变成看不懂的 String-->byte[] * 解码:把看不懂的变成看的懂的byte[]-->String * */ public class StringDemo { public static void main(String[] args) throws Exception { //编码 String-->byte[] String s = "你好"; byte[] b = s.getBytes("utf-8"); System.out.println("编码:"+Arrays.toString(b));//编码:[-28, -67, -96, -27, -91, -67] //解码 byte[]-->String String s1 = new String(b,"utf-8"); System.out.println("解码:"+s1);//解码:你好 } }
编码:[-28, -67, -96, -27, -91, -67, -17, -68, -116, -28, -72, -106, -25, -107, -116]
解码:你好,世界
4.OutputStreamWriter:编码 是使用制定的字符集将字符编码为字节
package com; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; /** * public OutputStreamWriter(OutputStream out) 根据默认编码把字节流转换为字符流 * public OutputStreamWriter(OutputStream out,String charsetName) 根据指定编码将字节流转换为字符流 * * 字符流=字节流+编码表 */ public class OutputStreamWriterDemo { public static void main(String[] args) throws IOException { //创建转换流对象 Writer w = new OutputStreamWriter(new FileOutputStream("w.txt"),"utf-8"); //输出 w.write("你好,世界"); //释放资源 w.close(); } }
5.InputStreamReader:解码 是使用制定的字符集读取字节并解码为字符
package com; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; public class InputStreamReaderDemo { public static void main(String[] args) throws Exception{ //创建转换流对象 Reader r = new InputStreamReader(new FileInputStream("w.txt"), "utf-8"); int temp = 0; while((temp = r.read()) != -1){ System.out.print((char)temp); } //释放资源 r.close(); } }
6.OutputStreamWriter的写数据方法
package cn; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; /** * OutputStreamWriter的方法 * public void write(int c) 写入一个字符 * public void write(char[] chuf) 写一个字符数组 * public void write(char[] chuf,int off,int len) 写一个字符数组的一部分 * public void write(String str) 写一个字符串 * public void write(String str,int off,int len)写一个字符串的一部分 */ public class OutputStreamWriterDemo { public static void main(String[] args) throws IOException { //创建转换流对象 Writer w = new OutputStreamWriter(new FileOutputStream("wr.txt")); //public void write(int c) 写入一个字符 w.write(‘a‘); w.write(97); //public void write(char[] chuf) 写一个字符数组 char[] chs = new char[]{‘a‘,‘b‘,‘c‘,‘d‘,‘e‘}; w.write(chs); //public void write(char[] chuf,int off,int len) 写一个字符数组的一部分 w.write(chs, 0, 2); //public void write(String str) 写一个字符串 w.write("我爱你"); //public void write(String str,int off,int len)写一个字符串的一部分 w.write("你爱我", 0, 1); /** * 如果没有将转换流对象flush()或close()? * 原因:字符=2字节 * 文件中数据存储的基本单位是字节。 */ w.flush(); //释放资源 w.close(); } }
close()和flush()的区别?
close()的作用是关闭流对象,但是先刷新缓冲区。关闭之后,流对象不可以继续使用。
flush()的作用是刷新缓冲区,刷新之后,流对象可以继续使用。
7.InputStreamReader 读取数据
package cn; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; /** * InputStreamReader 读取数据 * * int read() 读取单个字符 * int read(char[] chs) 将字符读取入数组 * */ public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { //创建转换流对象 Reader r = new InputStreamReader(new FileInputStream("wr.txt")); //读取单个字符 int ch = 0; while((ch = r.read()) != -1){ System.out.print((char)ch); } //关闭流对象 r.close(); } }
package cn; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; /** * InputStreamReader 读取数据 * * int read() 读取单个字符 * int read(char[] chs) 将字符读取入数组 * */ public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { //创建转换流对象 Reader r = new InputStreamReader(new FileInputStream("wr.txt")); //将字符读取入数组 char[] chs = new char[1024]; int len = 0; while((len = r.read(chs)) != -1){ System.out.println(new String(chs,0,len)); } //关闭流对象 r.close(); } }
8.转换流复制文件
package cn; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; /** * 读取的是a.txt,要保证a.txt存在 */ public class FileCopy { public static void main(String[] args) throws IOException { Reader r = new InputStreamReader(new FileInputStream("a.txt")); Writer w = new OutputStreamWriter(new FileOutputStream("b.txt")); //一次读取一个字符 int ch = 0; while((ch = r.read()) != -1){ w.write(ch); } r.close(); w.close(); } }
package cn; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; /** * 读取的是a.txt,要保证a.txt存在 */ public class FileCopy { public static void main(String[] args) throws IOException { Reader r = new InputStreamReader(new FileInputStream("a.txt")); Writer w = new OutputStreamWriter(new FileOutputStream("b.txt")); //读取到字符数组 char[] chs = new char[1024]; int len = 0; while((len = r.read(chs)) != -1){ w.write(chs,0,len); } r.close(); w.close(); } }
9.FileWriter和FileReader
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流提供了对象的子类--FileWriter和FileReader。
package cn; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; /** * 数据源:a.txt 读取数据--字符转换流--InputStreamReader--FileReader * * 目的地:b.txt 写入数据--字符转换流--OuputStreamWriter--FileWriter * */ public class FileCopy2 { public static void main(String[] args) throws IOException { Reader r = new FileReader("a.txt"); Writer w = new FileWriter("b.txt"); int ch = 0; while((ch = r.read()) != -1){ w.write(ch); } r.close(); w.close(); } }
package cn; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; /** * 数据源:a.txt 读取数据--字符转换流--InputStreamReader--FileReader * * 目的地:b.txt 写入数据--字符转换流--OuputStreamWriter--FileWriter * */ public class FileCopy2 { public static void main(String[] args) throws IOException { Reader r = new FileReader("a.txt"); Writer w = new FileWriter("b.txt"); char[] chs = new char[1024]; int len = 0; while((len = r.read(chs)) != -1){ w.write(chs, 0, len); } r.close(); w.close(); } }