字符是我们能读懂的一些文字和符号,但在计算机中存储的却是我们看不懂的byte 字节,那这就存在关于字符编码解码的问题。所以在学习Io流的字符流前我们先了解些关于编码问题。
一、字符集与字符编码
1、什么要有字符集
我们在计算机屏幕上看到的是实体化的文字,而在计算机存储介质中存放的实际是二进制的比特流。那 么在这两者之间的转换规则就需要一个统一的标准,否则就会出现乱码了现象;小伙伴QQ上传过来的文件,在我们本地打开又乱码了。 于是为了实现转换标准,各种字符集标准就出现了。
2、什么是字符集
简单的说字符集就规定了某个文字对应的二进制数字存放方式(编码)和某串二进制数值代表了哪个文字(解 码)的转换关系。
3、什么是字符编码
字符编码:计算机只能存储0,1之类2进制数字,怎么样让它表示那么多各种各样的字符呢?就需要对各种字符指定一个数值的编码代号,这就是字符编码。如:a这个字符,在ascii字符集编码表中对应的编号是97,而“中”在gb2312字符集中对应的编号是:16进制是D6D0 10进制是54992 。通过编号就可以找到计算机对应字符。不用将那么复杂的字符保存在计算机中,只需要保存它代号就好。字符集只是指定了一个集合中有哪些字符,而字符编码,是为这个集合中所有字符定义个编号,这就是字符集与编码区别所在。
计算机中只保存字符在某字符集中对应的字符编号值,计算机只需要维持一份字符集清单,当读到这种编号值(编码),就在对应字符清单中找出该字符显示出来即可。字符大小颜色都是程序按照字符样式绘制而成的。如图:
4、乱码
如果一个字符按照一个字符集存储在计算机中(编码),而使用另一种字符集读取该字符(解码)那就会出现乱码现象。应为计算机中存储使用的字码表与读取使用的字码表不匹配。
二、java中字符的编码与解码
计算机中存储和传输的基本单位是字节,而我们看到的信息是字符(人能看懂的)。
编码:编码就是真实字符与二进制串的对应关系,真实字符转换成二进制串。编码是信息从一种形式或格式转换为另一种形式的过程也称为计算机编程语言的代码简称编码。用预先规定的方法将文字、数字或其它对象编成数码,或将信息、数据转换成规定的电脉冲信号。
java中的编码:
在byte[] buffer=string.getBytes();中,如果没有给.getBytes();指定字符集,那么在编码过程中,就会按照系统默认的编码格式进行编码。
String str="中";
byte [] b_gbk=str.getBytes("GBK");
byte [] b_utf=str.getBytes("UTF-8");
byte [] b_unic=str.getBytes("UTF-16");
byte [] b_Iso=str.getBytes("ISO8859-1");
解码:解码就是二进制串与真实字符的对应关系,二进制串转换成真实字符。将信息从已经编码的形式恢复到编码前原状的过程。也就是用特定方法把数码还原成它所代表的内容或将电脉冲信号、光信号、无线电波等转换成它所代表的信息、数据等的过程。
java中的解码:
//str按照 GBK编码 后 ,再按照GBK解码
String str_1=new String(str.getBytes("GBK"), "GBK");
String str_2=new String(str.getBytes("UTF-8"), "UTF-8");
String str_3=new String(str.getBytes("UTF-32"), "UTF-32");
String str_4=new String(str.getBytes("ISO8859-1"), "ISO8859-1");
三、字符流
字符流:字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串。字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的。字符流操作的是缓冲区(当我们对文件进行读写操作时如果不调用close() 或 flush()方法时不能看到数据的变化)。
(一)、字符输入流
将磁盘(文件)中的数据读入内存中
Reader 是所有的输入字符流的父类,它是一个抽象类。
public abstract class Reader implements Readable, Closeable { protected Object lock; protected Reader() { this.lock = this; } protected Reader(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; } //试图将字符读入指定的字符缓冲区。缓冲区可照原样用作字符的存储库:所做的唯一改变是 put 操作的结果。不对缓冲区执行翻转或重绕操作。 public int read(java.nio.CharBuffer target) throws IOException {} //读取单个字符。在字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。 用于支持高效的单字符输入的子类应重写此方法。 public int read() throws IOException { } //将字符读入数组。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。 public int read(char cbuf[]) throws IOException {} // 将字符读入数组的某一部分。在某个输入可用、发生 I/O 错误或者到达流的末尾前,此方法一直阻塞。 abstract public int read(char cbuf[], int off, int len) throws IOException; //跳过字符。在某个字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。 public long skip(long n) throws IOException {} //判断是否准备读取此流。 public boolean ready() throws IOException { } //判断此流是否支持 mark() 操作。默认实现始终返回 false。子类应重写此方法。 public boolean markSupported() {} //标记流中的当前位置。对 reset() 的后续调用将尝试将该流重新定位到此点。并不是所有的字符输入流都支持 mark() 操作。 public void mark(int readAheadLimit) throws IOException { } //重置该流。如果已标记该流,则尝试在该标记处重新定位该流。如果已标记该流,则以适用于特定流的某种方式尝试重置该流, //例如,通过将该流重新定位到其起始点。并不是所有的字符输入流都支持 reset() 操作,有些支持 reset() 而不支持 mark()。 public void reset() throws IOException { } //关闭该流并释放与之关联的所有资源。在关闭该流后,再调用 read()、ready()、mark()、reset() 或 skip() 将抛出 IOException。关闭以前关闭的流无效。 abstract public void close() throws IOException; }
InputStreamReader:将字节输入流转换为字符输入流。是字节流通向字符流的桥梁,可以指定字节流转换为字符流的字符集。够造方法如下:
//InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。 // InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。
InputStreamReader 读取文件内容的三种方式
//public int read() throws IOException //读取单个字符。在字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。 public static void readOneStr() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\test\javaIo\1.txt")); int ch=0; while((ch=isr.read())!=-1){ System.out.print((char)ch); } isr.close(); } //public int read(char[] cbuf) throws IOException //将字符读入数组。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。 public static void readOneStrArr() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\test\javaIo\1.txt")); char [] ch=new char[1024]; int len=0; while((len=isr.read(ch))!=-1){ System.out.print(new String(ch,0,len)); } isr.close(); } //public int read(char[] cbuf) throws IOException //将字符读入数组的某一部分。在某个输入可用、发生 I/O 错误或者到达流的末尾前,此方法一直阻塞。 public static void readOneStrArrLen() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\test\javaIo\1.txt")); char [] ch=new char[1024]; int len=0; while((len=isr.read(ch,0,ch.length))!=-1){ System.out.print(new String(ch,0,len)); } isr.close(); }
FileReader:该类从InputStreamReader类继承而来,可以关联源文件。
1、构造方法
FileReader fr = new FileReader(String fileName);//使用带有指定文件的String参数的构造方法。创建该输入流对象。并关联源文件。
该类读取文件方式与InputStreamReader基本一样,使用该类代码较简洁建议使用 ,例如:
public static void main(String[] args) throws IOException { FileReader fr=new FileReader("E:\test\javaIo\1.txt"); char[] ch=new char[1024]; int len=0; while((len=fr.read(ch))!=-1){ System.out.println(new String(ch,0,len)); } fr.close(); }
BufferedReader(缓冲流):字节输入缓冲流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。BufferedReader 由Reader类扩展而来,提供通用的缓冲方式文本读取,而且提供了很实用的readLine。
该类读取文本的方法就是父类Reader提供的read()系列方法。在这里演示他自己特有的方法readLine()
//public String readLine()throws IOException读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (' ')、回车 (' ') 或回车后直接跟着换行。 public static void readLineTest() throws IOException{ BufferedReader br=new BufferedReader(new FileReader("E:\test\javaIo\1.txt")); String str=null; while((str=br.readLine())!=null){ System.out.println(str); } br.close(); }
LineNumberReader(关于行号缓冲流):它是BufferedReader的子类,LineNumberReader比BufferedReader多了个功能,就是可以获取当前行号 getLineNumber()与设置起始行号 setLineNumber(int lineNumber)。默认情况下,行编号从 0 开始。该行号随数据读取在每个行结束符处递增,并且可以通过调用 setLineNumber(int)
更改行号。但要注意的是,setLineNumber(int)
不会实际更改流中的当前位置;它只更改将由 getLineNumber() 返回的值。
public static void main(String[] args) throws IOException { LineNumberReader lnr=new LineNumberReader(new FileReader("E:\test\javaIo\2.txt")); //public void setLineNumber(int lineNumber)设置当前行号。 如果不设置该值 行号默认从0开始 lnr.setLineNumber(10); String str=null; while((str=lnr.readLine())!=null){ //public int getLineNumber()获得当前行号。 System.out.println(lnr.getLineNumber()+":"+str); } lnr.close(); }
(二)、字符输出流
将内存中的数据按照字符形式写入磁盘(文件)中
Writer:是所有的输出字符流的父类,它是一个抽象类。该抽象类的方法如下:
public abstract class Writer implements Appendable, Closeable, Flushable { private char[] writeBuffer; private final int writeBufferSize = 1024; protected Object lock; protected Writer() { this.lock = this; } protected Writer(Object lock) { } // 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 用于支持高效单字符输出的子类应重写此方法。 public void write(int c) throws IOException { } //写入字符数组。 public void write(char cbuf[]) throws IOException { } //写入字符数组的某一部分。 abstract public void write(char cbuf[], int off, int len) throws IOException; //写入字符串 public void write(String str) throws IOException { } //写入字符串的某一部分。 public void write(String str, int off, int len) throws IOException { } //将指定字符序列添加到此 writer。 public Writer append(CharSequence csq) throws IOException { } //将指定字符序列的子序列添加到此 writer.Appendable。 public Writer append(CharSequence csq, int start, int end) throws IOException { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } //将指定字符添加到此 writer public Writer append(char c) throws IOException { write(c); return this; } //刷新该流的缓冲。如果该流已保存缓冲区中各种 write() 方法的所有字符,则立即将它们写入预期目标。 //然后,如果该目标是另一个字符或字节流,则将其刷新。因此,一次 flush() 调用将刷新 Writer 和 OutputStream 链中的所有缓冲区。 abstract public void flush() throws IOException; //关闭此流,但要先刷新它。在关闭该流之后,再调用 write() 或 flush() 将导致抛出 IOException。关闭以前关闭的流无效 abstract public void close() throws IOException; }
OutputStreamWriter:将字节输出流转为字符输出流,是字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。常用够造方法如下:
//public OutputStreamWriter(OutputStream out, String charsetName); 可以设置编码格式 //public OutputStreamWriter(OutputStream out)创建使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter将内存中的数据写入文件的5中方式如下:
//public void write(int c)throws IOException //写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 public static void writerOneChar() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\test\javaIo\1.txt")); osw.write(97);//char ch=(char)97; osw.write('c'); osw.flush(); osw.close(); } //public void write(char[] cbuf)throws IOException 写入字符数组。 public static void writerCharArr() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\test\javaIo\1.txt")); char [] ch=new char[]{'我','爱','中','国'}; osw.write(ch); osw.flush(); osw.close(); } //public abstract void write(char[] cbuf,int off,int len)throws IOException 写入字符数组的某一部分。 public static void writerCharArrLen() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\test\javaIo\1.txt")); char [] ch=new char[]{'我','爱','中','国'}; osw.write(ch,0,ch.length-1); osw.flush(); osw.close(); } //public void write(String str) throws IOException 写入字符串。 public static void writerOneStr() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\test\javaIo\1.txt")); osw.write("中国"); osw.flush(); osw.close(); } //public void write(String str,int off, int len)throws IOException; 写入字符串的某一部分。 public static void writerOneStrArrLen() throws IOException{ OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\test\javaIo\1.txt")); String str="我爱中国"; osw.write(str,0,str.length()-2); osw.flush(); osw.close(); }
FileWriter:类从OutputStreamWriter类继承而来。该类按字符向流中写入数据,可以关联源文件。主要够造函数:
FileWriter fw = new FileWriter(String fileName);//创建字符输出流类对象和已存在的文件相关联。文件不存在的话,并创建。 如:FileWriter fw = new FileWriter("C:\demo.txt"); FileWriter fw = new FileWriter(String fileName,boolean append);//创建字符输出流类对象和已存在的文件相关联,并设置该该流对文件的操作是否为续写。 // 如:FileWriter fw = new FileWriter("C:\demo.txt",ture); //表示在fw对文件再次写入时,会在该文件的结尾续写,并不会覆盖掉。
该类写文件方式与OutputStreamWriter基本一样,例如:
public static void test() throws IOException{ FileWriter fw=new FileWriter("E:\test\javaIo\4.txt"); String str="爱我中华"; fw.write(str); fw.flush(); fw.close(); }
BufferedWriter(缓冲流):字节输出缓冲流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。 该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。该类写入文本的方法就是父类Writer提供的write()系列方法,在这里我们演示他特有的newLine( )方法。
public static void newLineTest() throws IOException{ BufferedWriter bw=new BufferedWriter(new FileWriter("E:\test\javaIo\2.txt")); bw.write("爱我"); bw.newLine(); bw.write("中华"); bw.flush(); bw.close(); }
//结果:(加入了换行符)
爱我
中华
四、文本文件的copy
1、使用转换流copy文件:
/** * 在这里使用了指定编码方式的对象,一般我们使用平台默认编码方式的够造函数创建对象 * @throws IOException */ public static void fileCopy() throws IOException{ InputStreamReader isr=new InputStreamReader(new FileInputStream("E:\test\javaIo\1.txt"),"utf-8"); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("E:\test\javaIo\4.txt"),"utf-8"); char [] ch=new char[1024]; int len=0; while((len=isr.read(ch))!=-1){ osw.write(ch,0,len); osw.flush(); } osw.close(); isr.close(); }
2、使用关联源文件的字符流copy文件:
public static void fileCopyFile() throws IOException{ FileReader fr=new FileReader("E:\test\javaIo\1.txt"); FileWriter fw=new FileWriter("E:\test\javaIo\2.txt"); char [] ch=new char[1024]; int len=0; while((len=fr.read(ch))!=-1){ fw.write(ch, 0, len); fw.flush(); } fw.close(); fr.close(); }
3、字节缓冲流+转换流实现文件的copy
public static void fileCopy() throws IOException{ BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("E:\test\javaIo\1.txt"))); BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:\test\javaIo\2.txt"))); char [] ch=new char[1024]; int len=0; while((len=br.read(ch))!=-1){ bw.write(ch, 0, len); bw.flush(); } bw.close(); br.close(); }
4、字节缓冲流+源文件字符流实现文件copy
public static void bufferedReaderTest1() throws IOException{ BufferedReader br=new BufferedReader(new FileReader("E:\test\javaIo\1.txt")); BufferedWriter bw=new BufferedWriter(new FileWriter("E:\test\javaIo\2.txt")); char [] ch=new char[1024]; int len=0; while((len=br.read(ch))!=-1){ bw.write(ch, 0, len); bw.flush(); } bw.close(); br.close(); }
5、使用缓冲流特定的行方法(readLine(),newLine() )实现文件的copy
//BufferedReader public String readLine() throws IOException //读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (' ')、回车 (' ') 或回车后直接跟着换行。 // BufferedWriter bw.newLine();//写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 (' ') 符。 public static void copyLine() throws IOException{ BufferedReader br=new BufferedReader(new FileReader("E:\test\javaIo\1.txt")); BufferedWriter bw=new BufferedWriter(new FileWriter("E:\test\javaIo\2.txt")); String str=null; while((str=br.readLine())!=null){ bw.write(str); bw.newLine();//写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 (' ') 符。 bw.flush(); } bw.close(); br.close(); }