javaIO总体思想是围绕以下四个抽象类来进行扩充展开。[凡以...stream结尾的类,都是字节流/比特流;凡以Reader或Writer结尾的类,都是字符流] 1.InputStream:输入字节流(通常所说的从文件中读取字节,以字符形式显示)。 2.OutputStream:输出字节流(通常所说的将字节写入到文件中)。 3.Reader:输入字符流 4.Writer:输出字符流 上述四个抽象类,必须要用相关的子类进行实例化。 Reader类及其子类提供的字符流的读取char(16位,unicode编码),inputStream及其子类提供字节流的读取byte(8位),所以FileReader类是将文件按字符流的方式读取,FileInputStream则按字节流的方式读取文件; FileInputStream以字节为单位(非 unicode )的流处理。字节序列即:二进制数据。与编码无关,不存在乱码问题。 FileInputStream :以字节流方式读取; FileReader :把文件转换为字符流读入; 常用的Reader类 FileReader ,InputStreamReader ,BufferedReader FileReader 与 InputStreamReader 涉及编码转换,可能在不同的平台上出现乱码现象。 (FileInputStream 以二进制方式处理,不会出现乱码现象。) FileReader是InputStreamReader 类的子类。 InputStreamReader 的构造函数参数为InputStream 和编码方式,当要指定编码方式时,必须使用 InputStreamReader 类。 FileReader 构造函数的参数与 FileInputStream 同,为 File 对象或表示 path 的 String。 衍生了两个重要的输入输出转换流:InputStreaReader和OutputStreamWriter这两个类是字节流和字符流之间的转换桥梁。所以在使用这两个类的构造方法进行实例化对象时,如果不指定编码方式,则会自动采用当前操作系统的默认字符集进行编码。该两个类会牵涉到编码设置。 InputStreaReader:可以将一个字节中流中的字节解码生成字符 OutputStreamWriter:将写入的字符编码成字节后写入字节流 编码:字符串变成字节数组,写入到文件中。写的过程 解码:字节数组变成字符串,以示人们能看懂。读的过程 总结:源文件以什么方式编的码,解码也必须用原来编码时的方式进行解码,否则肯定会有乱码出现。 注意:存放在文件中的内容都是字节,而读到内存中的才可能变为字符。 (1T=1024G, 1G=1024M, 1M=1024K, 1K=1024B, 1B=8bit) 以下示例主要是理解编码与解码。 例1.EncodeDemo.java import java.io.UnsupportedEncodingException; import java.util.Arrays; /*功能:理解编码与解码。 * 编码:字符串变成字节数组。string-----> byte [] str.getBytes(); * 解码:字节数组变成字符串(字符串才可显示看得懂)。byte []-----> string str = new String(byte[]); * 字节和字符之间转换要通过两个对象(InputStreamReader,OutputStreamWriter)来完成,这两个对象在构造时,就加入了字符集[编码表]。 * 编码表的由来:计算机只能识别二进制数据,早期由来是电信号。 * 为了方便应用计算机,让它可以识别各个国家的文字 * 就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。 * */ public class EncodeDemo { public static void main(String args[]){ String s = "您好吗"; byte[] b1 = null; //b1 = s.getBytes("ISO8859-1"); //将s按操作系统默认的编码gbk进行编码 b1 = s.getBytes(); //static String toString(byte[] a) 返回指定数组内容的字符串表示形式。 System.out.println(Arrays.toString(b1)); String s1; String s2; try { //将b1按ISO8859-1编码进行解码 s1 = new String(b1,"ISO8859-1"); //因为编码时采用当前电脑操作系统的默认中文编码,而解码时采用了ISO8859-1,所以显示时s1会乱码。 System.out.println("s1="+s1); //对s1进行iso8859-1编码。等于再次编码回去。 byte [] b2 = s1.getBytes("ISO8859-1"); System.out.println(Arrays.toString(b2)); //解码.按照 gbk进行解 s2 = new String(b2,"gbk"); System.out.println("s2="+s2); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //获取系统当前编码 //System.getProperty("file.encoding"); } } 例2.EncodeDemo2.java import java.util.*; public class EncodeDemo2 { /** * 功能:当解码的方式用错了的时候,如何再次进行编码后,再正确地进行解码。 * 修改,如果将程序中的iso8859-1编码全修改成utf-8,则结果又出意外。原因是,因为utf-8也识别中文(而iso8859-1不识别中文,反而更不会乱码,所以,tomcat服务器为什么采用iso8859-1的原因在此),而经过gbk编码后的字符,用utf-8解的时候,根本找不到对应的字符。 * 所以,会在未知区域中查找,就查出一堆相似的字符,返回。 * @param args * @throws Exception */ public static void main(String args[]) throws Exception{ String s = "您好"; //将s以操作系统认的gbk方式进行编码 byte [] b1 = s.getBytes(); //打印出字节数组的字符串形式 System.out.println(Arrays.toString(b1)); //将b1字节数组以iso8859-1方式进行解码。正确的方式应该还是以gbk的方式进行解码。 String s1 = new String(b1,"iso8859-1"); //打印出s1.肯定为乱码。因为编码和解码的方式不统一 System.out.println("s1="+s1); //对s1以iso8859-1进行编码.因为上述的解码方式错了,所以,需要再次将错误的解码方式进行反编码 byte [] b2 = s1.getBytes("iso8859-1"); System.out.println(Arrays.toString(b2)); //将b2以gbk的方式进行解码 String s2 = new String(b2,"gbk"); System.out.println("s2="+s2); } } 例3.EncodeDemo3.java public class EncodeDemo3 { /** * 功能:将字符串转换成二进制数 * @param args * @throws Exception */ public static void main(String args[]) throws Exception{ String s = "联通"; String ss = "q"; byte[] b = ss.getBytes("gbk"); for (int i = 0; i < b.length; i++) { System.out.println("q的二进制为:"+Integer.toBinaryString(b[i])); } byte [] by = s.getBytes("gbk"); for (int i = 0; i < by.length; i++) { System.out.println(by[i]+"的二进制数为:"); //将4个数字转换成二进制打印出来 System.out.println(Integer.toBinaryString(by[i])); System.out.println("取后8位为:"+Integer.toBinaryString(by[i]&255)); System.out.println("=============================="); } } } 例4.InputStreamReaderBigFile.java /** 读取大文件 * 功能:用java读取文件的全部内容,读取的内容较多时,不能一个字符一个字符地读,可以一次读一行。 */ import java.io.*; public class InputStreamReaderBigFile { public static void main(String args[]) throws Exception{ readText(); } public static void readText() throws Exception{ //假定utf-8.txt文件已存在,并且写入时是以utf-8的方式编码。 InputStreamReader isr = new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8"); //BufferedReader这个对象,可以一次读取一行 BufferedReader br = new BufferedReader(isr); String str; while((str = br.readLine())!=null){ System.out.println(str); } br.close(); isr.close(); } } 例5.OutputStreamWriterGBK.java import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStreamWriter; /** * 功能:将字符串写入到指定文件中 ,主要是理解写文件时编码 * @author Administrator * */ public class OutputStreamWriterGBK { public static void main(String args[]) throws Exception{ osw(); } public static void osw() throws Exception{ try { //缺省编码方式为操作系统的字符集gbk,所以gbk可以不写 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk"); //调用父类Writer的write()方法 osw.write("您好"); osw.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 例6.InputStreamReaderGBK.java import java.io.FileInputStream; import java.io.InputStreamReader; /** * 功能:用字符char的方式读出gbk.txt文件中内容,并以gbk的方式读取。主要是理解读文件时解码 * @author Administrator * */ public class InputStreamReaderGBK { public static void main(String []arg) throws Exception{ isr(); } public static void isr() throws Exception{ InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"gbk"); //unicode:一个字符占2个字节,1个字节占8位 //因为知道了gbk.txt中的内容少,故在此用char来读取 char [] buf = new char[10]; int len = isr.read(buf); System.out.println("gbk.txt文件内容长度为:"+len+"个字符"); String string = new String(buf,0,len); System.out.println(string); isr.close(); } } 例7.OutputStreamWriterUTF8.java import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStreamWriter; /** * 功能:将字符串写入到指定文件中 。理解写入字符到文件时的编码,以utf-8 * @author Administrator * */ public class OutputStreamWriterUTF8 { public static void main(String args[]) throws Exception{ osw(); } public static void osw() throws Exception{ try { //缺省编码方式为操作系统的字符集gbk,因为创建utf-8.txt文件时,指定了utf-8的编码,所以在此必须明确写上utf-8的编码方式 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"utf-8");//在这个地方进行转码操作 osw.write("您好"); osw.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 完例7相同的将字符串写入文件功能。但用了不同的实现方式。 import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class WriteFile { public static void main(String[] args) throws Exception { //假定utf-8.txt文件的编码方式是以utf-8方式编码的 String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); OutputStream out=new FileOutputStream(f); String s = "我们"; //表示以操作系统默认字符集编码方式写入文件 //out.write(s.getBytes()); //表示以utf-8的编码方式写入文件。以后读取文件内容时,也必须用utf-8进行解码读取。将字符串转换字节的方式 //s.getBytes("utf-8")表示将一字节数组写进去 out.write(s.getBytes("utf-8"));//在这个地方进行设置编码 out.close(); } } 当然,上面的代码也可修改成一个字节一个字节地进行写入: import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class WriteFile { public static void main(String[] args) throws Exception { //假定utf-8.txt文件的编码方式是以utf-8方式编码的 String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); OutputStream out = new FileOutputStream(f); String s = "我们"; //表示以操作系统默认字符集编码方式写入文件 //in.write(s.getBytes()); //表示以utf-8的编码方式写入文件。以后读取文件内容时,也必须用utf-8进行解码读取。将字符串转换字节的方式 //s.getBytes("utf-8")表示将一字节数组写进去 // out.write(s.getBytes("utf-8"));//在这个地方进行设置编码 byte[] b = s.getBytes("utf-8"); for(int i=0; i<b.length; i++){ out.write(b[i]); } out.close(); } } 最后,我们用字符流的方式将内容写入文件。不需要转码操作。请看如下代码。 import java.io.*; public class WriteFile{ public static void main(String [] args){ String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); Writer w = new FileWriter(f); String str = "hello"; w.write(str); w.close(); } } 例8.InputStreamReaderUTF8.java import java.io.FileInputStream; import java.io.InputStreamReader; /** * 功能:用字符char的方式读出gbk.txt文件中内容,并以utf-8的方式读取 。理解读取文件中的内容解码 utf-8方式 * 将字节流转换字符流的方式进行读取文件中的内容 * @author Administrator * */ public class InputStreamReaderUTF8 { public static void main(String []arg) throws Exception{ isr(); } public static void isr() throws Exception{ //指定以utf-8的方式创建一个输入流.目的在于注意区分,如果创建文件时指定了gbk编码方式,而读取时如果指定别的编码方式,则会产生乱码。 InputStreamReader isr = new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8"); //在这个地方进行解码,将字节流转换为字符流 //unicode:一个字符占2个字节,1个字节占8位 //因为知道了gbk.txt中的内容少,故在此用char来读取 char [] buf = new char[10]; int len = isr.read(buf); System.out.println("utf-8.txt文件内容长度为:"+len+"个字符"); String string = new String(buf,0,len); System.out.println(string); isr.close(); } } 与例8完成相同的读取文件内容功能,但用了不同的实现方式: import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * 功能:完成解码与编码,以字节流的方式读取文件内容 * @author Administrator * */ public class ReadFile { public static void main(String[] args) throws Exception { //假定utf-8.txt文件的编码方式是以utf-8方式编码的 String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); byte[] b=new byte[1030]; //将文件的内容读到b数组中 in.read(b); in.close(); //如果源文件的编码方式是utf-8方式,读取时,必须指定以同样的编码方式进行解码。否则就会乱码 System.out.println(new String(b,"utf-8")); //在这个地方进行解码 /*用下面的方式进行显示,就会有乱码 System.out.println(new String(b,"gbk")); System.out.println(new String(b)); */ } } 上述程序代码读的时候,会有大量的空格,我们可以利用in.read(b);的返回值来设计程序。如下: import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * 功能:完成解码与编码。以字节流的方式读取文件内容 * @author Administrator * */ public class ReadFile { public static void main(String[] args) throws Exception { //假定utf-8.txt文件的编码方式是以utf-8方式编码的 String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); byte[] b=new byte[1030]; int len = in.read(b); in.close(); System.out.println("读取文件的长度为:"+len); //如果源文件的编码方式是utf-8方式,读取时,必须指定以同样的编码方式进行解码。否则就会乱码 System.out.println(new String(b,0,len,"utf-8")); //在这个地方进行解码 /*用下面的方式进行显示,就会有乱码 System.out.println(new String(b,"gbk")); System.out.println(new String(b)); */ } } 仔细观察上面的代码可以看出,我们预先申请了一个指定大小空间。但是,有可能这个空间太小,有时候可能太大。我们更需要准确的大小,这样可以节省空间。请看如下代码: import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * 功能:完成解码与编码。以字节流的方式读取文件内容。根据文件长度来创建数据大小 * @author Administrator * */ public class ReadFile { public static void main(String[] args) throws Exception { //假定utf-8.txt文件的编码方式是以utf-8方式编码的 String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); // byte[] b=new byte[1030]; //f.length();表示返回由此抽象路径名表示的文件的长度。(int)f.length();表示进行数据类型的转换。 //定义一个不定长的字节数组。长度根据文件的长度来创建 byte [] b = new byte[(int)f.length()]; in.read(b); in.close(); System.out.println("文件的长度为:"+f.length()); //如果源文件的编码方式是utf-8方式,读取时,必须指定以同样的编码方式进行解码。否则就会乱码 System.out.println(new String(b,"utf-8")); //在这个地方进行解码 /*用下面的方式进行显示,就会有乱码 System.out.println(new String(b,"gbk")); System.out.println(new String(b)); */ } } 再将上述代码进行优化,一个字节一个字节地读。更节省空间。看如下代码: import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * 功能:完成解码与编码。以字节流的方式读取文件内容。一个字节一个字节地读 * @author Administrator * */ public class ReadFile { public static void main(String[] args) throws Exception { //假定utf-8.txt文件的编码方式是以utf-8方式编码的 String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); // byte[] b=new byte[1030]; //f.length();表示返回由此抽象路径名表示的文件的长度。(int)f.length();表示进行数据类型的转换。 //定义一个不定长的字节数组。长度根据文件的长度来创建 byte [] b = new byte[(int)f.length()]; for ( int i = 0; i<b.length; i++){ b[i] = in.read(); } // in.read(b); in.close(); System.out.println("文件的长度为:"+f.length()); //如果源文件的编码方式是utf-8方式,读取时,必须指定以同样的编码方式进行解码。否则就会乱码 System.out.println(new String(b,"utf-8")); //在这个地方进行解码 /*用下面的方式进行显示,就会有乱码 System.out.println(new String(b,"gbk")); System.out.println(new String(b)); */ } } 再仔细想:当我们不知道文件有多大时,这种情况下,需要判断是否读到文件末尾。采用while循环。请看如下代码: import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * 功能:完成解码与编码。以字节流的方式读取文件内容。增加判断,是否读到文件末尾 * @author Administrator * */ public class ReadFile { public static void main(String[] args) throws Exception { //假定utf-8.txt文件的编码方式是以utf-8方式编码的 String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); byte[] b=new byte[1030]; int i= 0; int tmp = 0; while (( tmp=in.read())!= (-1)){ //整型强制转换成byte型.每循环一次,字节数组中就增加一个元素 b[i++} = (byte)tmp; } //f.length();表示返回由此抽象路径名表示的文件的长度。(int)f.length();表示进行数据类型的转换。 //定义一个不定长的字节数组。长度根据文件的长度来创建 /* byte [] b = new byte[(int)f.length()]; for ( int i = 0; i<b.length; i++){ b[i] = in.read(); }*/ // in.read(b); in.close(); System.out.println("文件的长度为:"+f.length()); //如果源文件的编码方式是utf-8方式,读取时,必须指定以同样的编码方式进行解码。否则就会乱码 System.out.println(new String(b,"utf-8")); //在这个地方进行解码 /*用下面的方式进行显示,就会有乱码 System.out.println(new String(b,"gbk")); System.out.println(new String(b)); */ } } 最后,以字符的方式从文件中读取内容。看如下代码: public class ReadFile{ public static void main(String [] args){ String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); char [] ch = new char[100]; Reader r = new FileReader(f); int count = r.read(ch); r.close(); System.out.println("读入的长度为:"+count); System.out.println("内容为:"+new String(ch,0,count)); } } 有时候,我们还不知道文件到底有多大,所以,我们更宜采用循环的方式读取(以字符方式读取)。看如下代码: public static void main(String [] args){ String fileName="D:"+File.separator+"Workspaces"+File.separator+"charset"+File.separator+"utf-8.txt"; File f=new File(fileName); char [] ch = new char[100]; Reader r = new FileReader(f); int tmp = 0; int i = 0; while ((tmp = r.read())!= (-1)){ ch[i++] = (char)tmp; } // int count = r.read(ch); r.close(); //System.out.println("读入的长度为:"+count); System.out.println("内容为:"+new String(ch,0,count)); } }
延伸:文件的复制。基本思路还是从一个文件中读入内容,边读边写入另一个文件,就是这么简单.请看ReadANDWrite.java import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Writer; /* * 功能:将一个文件的内容读出来后,将所有内容写入到另一个文件。实现文件复制功能 */ public class ReadANDWrite { public static void main(String []args) throws Exception{ File file = new File("c:\\gbk.txt"); InputStream is = new FileInputStream(file); //OutputStream os = new FileOutputStream(new File("c:\\gbk-copy.txt")); Writer writer = new OutputStreamWriter(new FileOutputStream("c:\\gbk-copy.txt")); byte [] b = new byte[100]; //int len = is.read(b); int len; int i=0; //循环判断是否读到文件末尾 while ((len = is.read())!=(-1)) { // String s = new String(b); // writer.write(s); //设置计数器。看看总共有几个字节 int c = i++; //按字节进行读取,读到一个字节后,赋值给字节数组变量 b[c] = (byte)len; System.out.println("b"+"["+i+"]"+":"+b[c]); } //将字节数组转换成字符串,为了不出现多余的空格,必须需要接上加上相关参数,从哪开始读,读到哪结束 String s = new String(b,0,i); writer.write(s); System.out.println(new String(b,0,i)); //os.write((byte)is.read(b)); is.close(); writer.close(); //os.close(); } } |
|