I/O梳理

一、创建文件

File file = new File("D:/yunqingPojects/lol.txt");  // 可以用绝对路径或者相对路径创建
file.getParentFile().mkdirs();
file.createNewFile();

二、流 的概念

1.为什么需要流?
不同介质之间数据交互,比如读取硬盘上文件到程序中
数据源可以是文件,数据库,网络甚至是其他的程序
2.输入流or输出流 - 默认是站在程序的角度来说来说输入输出
e.g.从文件中读数据 就是输入流

三、ASCII码 的概念

所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放。
包含简单的英文字母,符号,数字等等。 不包含中文,德文,俄语等复杂的。

四、字节流(InputStream,OutputStream)

InputStream,OutputStream都是抽象类;
FileInputStream 是InputStream子类;FileOutputStream 是OutputStream子类

  • 以字节流的形式读取文件内容(即 将文件数据读取到 中):
try {
    //准备文件lol.txt其中的内容是AB,对应的ASCII分别是65 66
    File f =new File("d:/lol.txt");
    //创建基于文件的输入流
    FileInputStream fis =new FileInputStream(f);
    //创建字节数组,其长度就是文件的长度
    byte[] all =new byte[(int) f.length()];
    //以字节流的形式读取文件所有内容
    fis.read(all);
    for (byte b : all) {
        //打印出来是65 66
        System.out.println(b);
    }   
    //每次使用完流,都应该进行关闭
    fis.close();  
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
  • 以字节流的形式向文件写入数据(即 通过 的形式向文件写数据)::
try {
    // 准备文件lol2.txt其中的内容是空的
    File f = new File("d:/lol2.txt");
    // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
    byte data[] = { 88, 89 };
    // 创建基于文件的输出流
    FileOutputStream fos = new FileOutputStream(f);
    // 把数据写入到输出流
    fos.write(data);
    // 关闭输出流
    fos.close();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

五、关闭流的方式

把流定义在try()里,try,catch或者finally结束的时候,会自动关闭。
JDK7之后,所有的流都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

public static void main(String[] args) {
    File f = new File("d:/lol.txt");
    //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
    try (FileInputStream fis = new FileInputStream(f)) {
        byte[] all = new byte[(int) f.length()];
        fis.read(all);
        for (byte b : all) {
            System.out.println(b);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

六、字符流(Reader,Writer)

Reader,Writer都是抽象类
FileReader 是Reader子类,FileWriter 是Writer的子类

  • 使用字符流读取文件(以 为基础)
public static void main(String[] args) {
    // 准备文件lol.txt其中的内容是AB
    File f = new File("d:/lol.txt");
    // 创建基于文件的Reader
    try (FileReader fr = new FileReader(f)) {
        // 创建字符数组,其长度就是文件的长度
        char[] all = new char[(int) f.length()];
        // 以字符流的形式读取文件所有内容
        fr.read(all);
        for (char b : all) {
            // 打印出来是A B
            System.out.println(b);
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
  • 使用字符流把数据写入到文件(以 为基础)
public static void main(String[] args) {
    // 准备文件lol2.txt
    File f = new File("d:/lol2.txt");
    // 创建基于文件的Writer
    try (FileWriter fr = new FileWriter(f)) {
        // 以字符流的形式把数据写入到文件中
        String data="abcdefg1234567890";
        char[] cs = data.toCharArray();
        fr.write(cs);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

七、中文问题

1.编码概念:

  • 计算机上的所有的数据都是以数字的形式进行存储的。
    那么一个文件到底在硬盘上是由哪些数字组成的,取决于用的什么编码方式(可以理解为:文件中存储的都是对应棋盘的坐标)


    字符编码的理解.png
  • 同一个字符在不同的坐标体系中坐标是不一样的,即在硬盘中的实际数值不一样
    2.常见编码方式
    工作后经常接触的编码方式有如下几种:
    ISO-8859-1 ASCII 数字和西欧字母
    GBK GB2312 BIG5 中文
    UNICODE (统一码,万国码)
    其中
    ISO-8859-1 包含 ASCII
    GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
    UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
    3.用 字节流 正确读取中文
    为了能够正确的读取中文内容
    1>. 必须了解文本是以哪种编码方式保存字符的
    2>. 使用字节流读取了文本后,再使用对应的编码方式去识别这些数字,得到正确的字符.
    如本例,一个文件中的内容是字符 ,编码方式是GBK,那么读出来的数据一定是D6D0。
    再使用GBK编码方式识别D6D0,就能正确的得到字符

public static void main(String[] args) {
        File f = new File("E:\\project\\j2se\\src\\test.txt");
        try (FileInputStream fis = new FileInputStream(f);) {
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            //文件中读出来的数据是
            System.out.println("文件中读出来的数据是:");
            for (byte b : all)
            {
                int i = b&0x000000ff;  //只取16进制的后两位
                System.out.println(Integer.toHexString(i));
            }
            System.out.println("把这个数字,放在GBK的棋盘上去:");
            String str = new String(all,"GBK");
            System.out.println(str);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

4.用 字符流 正确读取中文
FileReader得到的是字符,所以一定是已经把字节 了
而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 

在本例中,用记事本另存为UTF-8格式,然后用UTF-8就能识别对应的中文了。

public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
        File f = new File("E:\\project\\j2se\\src\\test.txt"); // 此处文件是以UTF8 的编码方式存储的一个 "中"字
        //FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替
        //并且使用new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 这样的形式
        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) {
            char[] cs = new char[(int) f.length()];
            isr.read(cs);
            System.out.printf("InputStreamReader 指定编码方式UTF-8,识别出来的字符是:%n");
            System.out.println(new String(cs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }     
    }

八、缓存流

  1. 为什么出现了缓存流(BufferedReader,PrintWriter)
  • 字符流 和 字节流 每一次读写的时候 都会访问硬盘,性能不佳
  • 缓存流 在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取;
    缓存流 在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作
  • 就好比吃饭,不用缓存就是。用缓存就是
  • 缓存流必须建立在一个存在的流的基础上

2.使用缓存流写入数据
PrintWriter 缓存字符输出流, 可以一次写出一行数据
API:pw.println()

public void testCacheStrem(){
        // 向文件lol3.txt中写入三行语句
        File f = new File("D:/pojects/lol3.txt");
        try (FileWriter fw = new FileWriter(f);PrintWriter pw = new PrintWriter(fw)) {
            pw.println("garen kill teemo");
            pw.println("teemo revive after 1 minutes");
            pw.println("teemo try to garen, but killed again");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

3.使用缓存流读取数据
BufferedReader 缓存字符输入流,可以一次读取一行数据
br.readLine()

public void testCacheStremRead(){
        // 从文件lol3.txt中写入三行语句
        File f = new File("D:/pojects/lol3.txt");
        try (FileReader fr = new FileReader(f);BufferedReader br = new BufferedReader(fr)) {
            while (true){
                String line = br.readLine();
                if (line == null) break;
                System.out.println(line);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
  1. 强制写入硬盘 flush()
    有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush
public static void main(String[] args) {
        //向文件lol2.txt中写入三行语句
        File f =new File("d:/lol2.txt");
        //创建文件字符流
        //缓存流必须建立在一个存在的流的基础上
        try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {
            pw.println("garen kill teemo");
            //强制把缓存中的数据写入硬盘,无论缓存是否已满
                pw.flush();           
            pw.println("teemo revive after 1 minutes");
                pw.flush();
            pw.println("teemo try to garen, but killed again");
                pw.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
} 

九、数据流(DataInputStream,DataOutputStream)

1.What for
如果向文件中先后写入一个浮点数11.3 和一个整数38,那么文件中的内容为11.338,
需要自己添加特殊字符进行在写入的时候插入才能在读取的时候判别第一次写的浮点数和第二次写的整数。
数据流帮我们进行了维护。
2.用数据流直接进行数字的读写

public void testDataStream(){
        // 向文件中顺序写入两个数字,然后顺序读出
        write();
        read();
    }
    public void write(){
        File f = new File("D:/yunqingPojects/lol3.txt");
        try (FileOutputStream fos = new FileOutputStream(f);DataOutputStream dos = new DataOutputStream(fos)) {
            dos.writeFloat(11.2f);
            dos.writeInt(30);
        }catch (IOException e){

        }
    }
    public void read(){
        File f = new File("D:/yunqingPojects/lol3.txt");
        try (FileInputStream fis = new FileInputStream(f);DataInputStream dis = new DataInputStream(fis)) {
            float num1 = dis.readFloat();
            int num2 = dis.readInt();
            System.out.println(num1);
            System.out.println(num2);
        }catch (IOException e){

        }
    }

十、对象流( ObjectInputStream,ObjectOutputStream)

对象流指的是可以直接把一个 给其他的介质,比如硬盘 。
一个对象以流的形式进行传输叫做。 该对象所对应的类,必须是实现Serializable接口。
2.API:

oos.writeObject(h); // 将对象写入文件
Hero h2 = (Hero) ois.readObject();  // 将对象从文件读出

2.例子
创建一个Hero对象,设置其名称为garen。
把该对象序列化到一个文件garen.lol。然后再通过序列化把该文件转换为一个Hero对象

public void testObjStream(){
    Hero hero = new Hero();
    hero.name = "gareen";
    hero.hp = 616;

    File f = new File("D:/yunqingPojects/lol4.txt");
    try (
            // 创建对象输出流
            FileOutputStream fos = new FileOutputStream(f); ObjectOutputStream oos = new ObjectOutputStream(fos);
            //创建对象输入流
            FileInputStream fis = new FileInputStream(f);ObjectInputStream ois = new ObjectInputStream(fis)) {

            oos.writeObject(hero);
            Hero h = (Hero) ois.readObject();

        System.out.println(h.name);
        System.out.println(h.hp);
    }catch (IOException ex){

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

十一、控制台输入流(System.in,Scanner)

1.System.in

public static void main(String[] args) {
    // 控制台输入
    try (InputStream is = System.in;) {
        while (true) {
            // 敲入a,然后敲回车可以看到
            // 97 13 10
            // 97是a的ASCII码
            // 13 10分别对应回车换行
            int i = is.read();
            System.out.println(i);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
 }

2.Scanner读取字符串(sc..nextLine())
使用System.in.read虽然可以读取数据,但是很不方便,
使用Scanner就可以逐行读取了

public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    while(true){
        String line = s.nextLine();
        System.out.println(line);
     }
}

3.Scanner从控制台读取整数(sc.nextInt())

public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    int a = s.nextInt();
    System.out.println("第一个整数:"+a);
    int b = s.nextInt();
    System.out.println("第二个整数:"+b);
}

十二、流关系图

StreamRelationship.png

你可能感兴趣的:(I/O梳理)