分享

重要!!!javaIO总结及心得(总概及编码解码示例)

 英昌知识汇聚馆 2011-12-22
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。


衍生了两个重要的输入输出转换流:InputStreaReaderOutputStreamWriter这两个类是字节流和字符流之间的转换桥梁。所以在使用这两个类的构造方法进行实例化对象时,如果不指定编码方式,则会自动采用当前操作系统的默认字符集进行编码。该两个类会牵涉到编码设置
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();
    }
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多