JAVASE-25:IO流之字节流 字符流

IO

按照流向划分,分为输入流输出流(我们站在内存角度看输入/输出);

按照读写文件类型的划分,我们分为字节流字符流

字节流可以读写任意类型文件

字符流只能读写文本文件


1.字节流

字节流的抽象基类:InputStream ,OutputStream

字节输入流(读)–父类InputStream–子类ByteArrayInputStream 、FileInputStream 、ObjectInputStream

字节输出流(写)–父类OutputStream–子类ByteArrayOutputStream 、FileOutputStream 、ObjectOutputStream

1.1 文件输出流–FileOutputStream

FileOutputStream(File file)

创建一个向指定 File 对象表示的文件中写入数据的文件输出流。

输出流所关联的文件如果不存在,会自动帮你创建。

FileOutputStream(String name)

创建一个向具有指定名称的文件中写入数据的输出文件流。

public static void main(String[] args) throws IOException {
    File file = new File("a.txt");
    // file.createNewFile();
    FileOutputStream out = new FileOutputStream(file);

    FileOutputStream out2 = new FileOutputStream("b.txt");
}

.write(int b); 一次写入一个字节。

.write(byte[ ] b); 一次写入一个字节数组。

字符串.getBytes(); 转为字节数组。

.write(字节数组,开始索引,多少个字节); 一次写入一个字节数组的一部分。

也可以写入换行,.write("\r\n".getBytes()); (windows换行\r\n)。

.close; 流使用完毕,一定要释放资源,记得关闭。

public static void main(String[] args) throws IOException {
    FileOutputStream out = new FileOutputStream("a.txt");
    // 一次写入一个字节
    out.write(97);// a
    out.write(98);// b
    out.write(99);// c
    
    // 一次写入一个字节数组
    byte[] bytes = "像我这样优秀的人,本该灿烂过一生,怎么二十多年到头来,还在人海里浮沉。".getBytes();// 将字符串转为字节数组
    out.write(bytes);
    // 一次写入字节数组的一部分 0 从字节数组中的那个索引开始 24 写24个字节。

    /*
    windows下的换行符只用是 \r\n
    Linux   \n
    Mac        \r*/
    out.write(bytes,0,24);
    // 写入换行符
    out.write("\r\n".getBytes());
    out.write(bytes);
    // 流使用完毕,记得要关闭。
    out.close();
}

当new一个输出流时,到底做了哪些事情:

a.调用系统资源创建a.txt文件

b.创建了一个out对象

c.把out对象指向这个文件

为什么一定要close() ?

a : 通知系统释放关于管理a.txt文件的资源
b: 让Io流对象变成垃圾, 等待垃圾回收器对其回收

FileOutputStream(Stringname,是否要追加写入true/false);

public static void main(String[] args) throws IOException {
    //参2:是否追加写入,true 就是追加写入,false 就是默认覆盖。
    FileOutputStream out = new FileOutputStream("b.txt",true);
    out.write("去年今日此园中".getBytes());
    out.write("\r\n".getBytes());
    out.write("人与桃花相映红".getBytes());
    out.write("\r\n".getBytes());
    out.write("人面不知何处去".getBytes());
    out.write("\r\n".getBytes());
    out.write("桃花依旧笑春风".getBytes());
    out.write("\r\n".getBytes());
    //释放资源
    out.close();
}

流的异常处理:

package org.westos.demo4;
import java.io.FileOutputStream;
import java.io.IOException;
public class MyTest {
    public static void main(String[] args) {
        FileOutputStream out = null;
        try {
            // System.out.println(1 / 0);
            out = new FileOutputStream("c.txt", true);
            out.write("踢巴萨踢阿森纳踢拜仁慕尼黑别杀列拜仁".getBytes());
            out.write("\t\n".getBytes());
            out.write("像我这样聪明的人,早就告别了单纯".getBytes(),0,24);
            out.write("\t\n".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

1.2 文件输入流–FileInputStream

从文件系统中的某个文件中获得输入字节

FileInputStream(File file)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
输入流所关联的文件,如果不存在,不会自动帮你创建,会报错。(因为想要获得文件中输入字节 文件得先存在)

FileInputStream(Stringname)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

public static void main(String[] args) throws IOException {
    File file = new File("c.txt");
    file.createNewFile();
    FileInputStream in = new FileInputStream(file);

    // 读取文件中的数据.read()
    // 一个读取一个字节
    int by = in.read();
    System.out.println(by);
    by = in.read();
    System.out.println(by);
    // 如果读取不到数据就会 返回 -1 我们可以使用 -1 来判断文件是否读取完毕

    // 释放资源。
    in.close();
}

读取文件中的数据

.read(); 一次读取一个字节,读不到返回-1,可以使用-1判断文件是否读取完毕,返回的是 实际读取到子节个数
最后也记得释放资源 .close();

.read(byte[ ] b); 一次读取一个字节数组 (自己定义一个数组,来充当一个容器,把读到的字节放入数组中)返回的是实际读取到的字节个数

.read(bytes,从某索引开始填入,长度); 一次读取一个字节数组的一部分,返回的是 实际读取到子节个数

public static void main(String[] args) throws IOException {
    FileInputStream in = new FileInputStream("c.txt");
    // 定义一个数组,来充当一个容器
    byte[] bytes = new byte[1024];
    // 一个读取换一个字节数组 ,返回的是 实际读取到子节个数
    int len = in.read(bytes);
    System.out.println(len); // 6
    System.out.println("===================================");
    // 循环打印容器中读取到的内容
    for (byte aByte : bytes) {
        System.out.println(aByte);
    }
    System.out.println("===================================");
    String s = new String(bytes, 0, len);
    System.out.println(s);// abcdef
    in.close();
}
public static void main(String[] args) throws IOException {
    FileInputStream in = new FileInputStream("c.txt");
    byte[] bytes = new byte[1024];
    // 一次读取字节数组的一部分
    int len = in.read(bytes, 0, 3);
    System.out.println(len);// 3
    System.out.println("===================================");
    for (byte aByte : bytes) {
        System.out.println(aByte);
    }
    System.out.println("===================================");
    String s = new String(bytes, 0, len);
    System.out.println(s);// abc
    in.close();
}
复制文件:

一次一个字节—

public static void main(String[] args) throws IOException {
    // 很显然 一次复制一个字节,太慢。
    FileInputStream in = new FileInputStream("C:\\Users\\ShenMouMou\\Desktop\\a.mp3");
    FileOutputStream out = new FileOutputStream("E:\\test\\情书.mp3");
    int by = 0;
    while ((by = in.read()) != -1) {
        out.write(by);
        out.flush();
    }
    in.close();
    out.close();
    System.out.println("复制完成");
}

推荐使用—一次复制一个字节数组

public static void main(String[] args) throws IOException {
    // 读取一个字节,写入一个字节,来复制文本文件。
    FileInputStream in = new FileInputStream("C:\\Users\\ShenMouMou\\Desktop\\MyTest.java");
    
    File file = new File("E:\\test");
    // 如果此目录不存在,创建它。
    if (!file.exists()) {
        file.mkdirs();
    }
    
    FileOutputStream out = new FileOutputStream(new File(file, "MyTest.java"));
    // 定义一个变量用来记录每次读取到的字节
    int by=0;
    while ((by=in.read())!=-1){
        out.write(by);
        out.flush();
    }
    // 释放资源
    in.close();
    out.close();
    System.out.println("复制完成");
}
public static void main(String[] args) throws IOException {
    // 推荐使用一次复制一个字节来做
    FileInputStream in = new FileInputStream("C:\\Users\\ShenMouMou\\Desktop\\a.mp3");
    FileOutputStream out = new FileOutputStream("E:\\test\\情书2.mp3");
    // 定义一个数组来充当缓冲区。
    byte[] bytes = new byte[1024 * 8];
    // 定义一个变量,来记录实际读取到的字节个数
    int len=0;
    while ((len=in.read(bytes))!=-1){
        out.write(bytes,0,len);
        out.flush();
    }
    in.close();
    out.close();
    System.out.println("复制完成");
}

1.3 高效的字节流

缓冲思想:
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流。

BufferedOutputStream(OutputStream out) 文件输出流
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

BufferedInputStream(InputStream in) 文件输入流
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

public static void main(String[] args) throws IOException {
    BufferedInputStream in = new BufferedInputStream(new FileInputStream("a.txt"));
    BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("d.txt"));
    byte[] bytes = new byte[1024];
    int len=0;
    while ((len=in.read(bytes))!=-1){
        out.write(bytes,0,len);
        out.flush();
    }
    in.close();
    out.close();
}

流的异常处理:
public static void main(String[] args) {
    FileInputStream fileInputStream = null;
    FileOutputStream fileOutputStream= null;
    try {
        fileInputStream = new FileInputStream("C:\\Users\\Administrator\\Desktop\\JAVA\\08-09\\one.mp4");
        fileOutputStream= new FileOutputStream("C:\\Users\\Administrator\\Desktop\\20200809-JavaSE-课件");
        byte[] bytes = new byte[1024 * 8];
        int len=0;
        while((len=fileInputStream.read(bytes))!=-1) {
            fileOutputStream.write(bytes, 0, len);
            fileOutputStream.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileInputStream.close();
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.字符流

只能操作文本文件,进行读写

字符输入流的父类 Reader —子类BufferReader 、InputStreamReader、StringReader

字符输出流的父类 Writer —子类BufferWriter、InputStreamWriter、StringWriter

字符流=字节流+编码表

编码,把字符串转换为字节数组 .getBytes();
解码,把字节数组转换成字符串 new String(bytes);
乱码:编解码没有采用同一张码表。

.getBytes(); 采用平台默认的码表进行编码

new String(bytes); 采用平台默认的码表进行解码

也可以自己指定编码表 .getBytes(GBK);

new String(bytes,GBK); 采用指定的码表进行解码

public static void main(String[] args) throws UnsupportedEncodingException {
    // getBytes(); 采用平台默认的码表进行编码。
    // getBytes("GBK"); 自己指定编码表
    byte[] bytes = "今天晚上学校小树林见".getBytes("UTF-8");
    for (byte aByte : bytes) {
        System.out.println(aByte);
    }

    System.out.println("=================================");

    // new String(bytes); 采用平台默认的码表进行解码。
    // String s = new String(bytes, "GBK"); 指定码表进行解码
    String s = new String(bytes,"GBK");
    System.out.println(s);
}

2.1 转换流OutputStreamWrite

OutputStreamWrite是字符流通向字节流的桥梁

构造方法:
OutputStreamWriter(OutputStream out):根据默认编码(GBK)把字节流的数据转换为字符流

OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流,可以使用指定的码表将要写入流

public static void main(String[] args) throws IOException {
    // OutputStreamWriter 是字符流通向字节流的桥梁:
    // 可使用指定的 码表 将要写入流中的字符编码成字节。
    // 它使用的字符集可以由名称指定或显式给定,
    // 否则将接受平台默认的字符集。
    // OutputStreamWriter(OutputStream out)
    // 创建使用默认字符编码的 OutputStreamWriter。
    // 输出流所关联的文件,如果不存在,会自动创建
    java.io.OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("a.txt",true));
    // 一次写一个字符
    writer.write('a');
    writer.write('a');
    writer.flush();
    writer.write("家有小女初长成,养在深闺人未识,天生丽质难自弃,一朝选在君王侧,从此君王不早朝");
    writer.write("\r\n");
    writer.write(new char[]{'好','好','学','习'});
    writer.write("\r\n");
    writer.write("杨家有女初长成,养在深闺人未识,天生丽质难自弃,一朝选在君王侧,从此君王不早朝",0,7);
    writer.write("\r\n");
    writer.write(new char[]{'好', '好', '学', '习'},0,2);
    writer.flush(); //字符流记得刷新一下
    writer.close(); //刷新并关闭。
}

.write(int b) 一次写入一个字符

.write(String str) 一次写入一个字符串

.write(String str,int off,int len) 写一个字符串的一部分

.write(char[] cbuf); 一次写入一个字符数组

.write(char[] cbuf,int off,int len) 写一个字符数组的 一部分

也可以写入换行,.write("\r\n"); (windows换行\r\n)

流使用完毕,一定要释放资源,记得刷新 关闭

.flush();-- 刷新

.close; – 刷新并关闭

public static void main(String[] args) throws IOException {
    /* OutputStreamWriter(OutputStream out, String charsetName)
    创建使用指定字符集的 OutputStreamWriter。*/
    OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("b.txt"), "UTF-8");
    writer.write("江畔何人初见月,江月何年初照人。");
    writer.flush();
    writer.close();
}

如果要追加写入,给封装的字节流加true

2.2 转换流InputStreamReader

是字节流通向字符流的桥梁,它使用指定的 charset 读取字节并将其解码为字符。
它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
所关联的文件 如果不存在 会报错。

构造方法:
InputStreamReader(InputStream is):用默认的编码(GBK)读取数据, 创建一个使用默认字符集的 InputStreamReader。

InputStreamReader(InputStream is,String charsetName):用指定的编码读取数据,创建使用指定字符集的 InputStreamReader。

public static void main(String[] args) throws IOException {
    // 输入流,所关联的文件,如果不存在,就会报错。
    InputStreamReader reader = new InputStreamReader(new FileInputStream("c.txt"));
    int ch = reader.read();
    System.out.println(ch);
    ch = reader.read();
    System.out.println(ch);
    // 如果读取不到有效字符,返回 -1 我们使用 -1 来判断文件是否读取完。
    ch = reader.read();
    System.out.println(ch);
}

.read(); 一次读取一个字符,读不到返回-1,可以使用-1判断文件是否读取完毕,返回的是读取到的有效的字符个数

最后也记得释放资源 .close();

.read(char[ ] chars); 一次读取一个字符数组 (自己定义一个数组new char[1000],来充当一个容器,把读到的字节放入数组中)返回的是实际读取到的字节个数

.read(); 一次读取一个字符数组的一部分

public static void main(String[] args) throws IOException {
    InputStreamReader reader = new InputStreamReader(new FileInputStream("c.txt"));
    char[] chars = new char[1000];
    // 一次读取一个字符数组,返回值,返回的是读取到的有效的字符个数
    // int len = reader.read(chars);
    int len = reader.read(chars,0,3);
    System.out.println(len);// 3
    // 循环打印读取到的内容
    for (char aChar : chars) {
        System.out.println(aChar);
    }
    reader.close();
}

案例:复制文本文件

public static void main(String[] args) throws IOException {
    // 一次读取一个字符,写一个字符来复制文本文件
    InputStreamReader in = new InputStreamReader(new FileInputStream("MyTest.java"));
    OutputStreamWriter out= new OutputStreamWriter(new FileOutputStream("C:\\Users\\ShenMouMou\\Desktop\\MyTest.java"));

    int ch=0;
    while ((ch=in.read())!=-1){
        out.write(ch);
        out.flush();
    }

    in.close();
    out.close();

}
public static void main(String[] args) throws IOException {
    // 一次读写一个字符数组,来复制
    InputStreamReader in = new InputStreamReader(new FileInputStream("MyTest.java"));
    OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("C:\\Users\\ShenMouMou\\Desktop\\MyTest.java"));

    char[] chars = new char[1000];
    int len=0;
    while ((len=in.read(chars))!=-1){
        // System.out.println("循环次数");
        out.write(chars,0,len);
        out.flush();
    }
    in.close();
    out.close();
}

2.3 FileReader()-FileWriter()

FileReader()是InputStreamReader的子类

FileWriter()是OutputStreamWrite的子类

转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流提供了对应的子类。
FileWriter–FileReader异常处理:

public static void main(String[] args)  {
    FileReader in=null;
    FileWriter out=null;
    try {
        in = new FileReader("C:\\Users\\ShenMouMou\\Desktop\\MyTest.java");
        out = new FileWriter("MyTest.java");
        char[] chars = new char[1000];
        int len = 0;
        while ((len = in.read(chars)) != -1) {
            // System.out.println("循环次数");
            out.write(chars, 0, len);
            out.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.4 BufferedWriter-BufferedReader

(ctrl+H 可以看一个类的继承关系)

高效的字符输出流:BufferedWriter,创建一个使用默认大小输出缓冲区的缓冲字符输出流。
构造方法:public BufferedWriter(Writer out)
高效的字符输入流:BufferedReader
构造方法:public BufferedReader(Reader in)

public static void main(String[] args) throws IOException {
    // 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
    BufferedWriter out = new BufferedWriter(new FileWriter("d.txt"));
    // BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("e.txt")));

    BufferedReader  in= new BufferedReader(new FileReader("MyTest.java"));

    char[] chars = new char[1000];
    int len = 0;
    while ((len = in.read(chars)) != -1) {
        // System.out.println("循环次数");
        out.write(chars, 0, len);
        out.flush();
    }
    out.close();
    in.close();
}

高效的字符流特有的方法:

.newLine(); 写入一个换行符,根据系统来决定换行符 具有系统兼容性的换行符

.readLine(); 读取一行,返回一个字符串,一次读取一行数据,是以换行符为标记的 读到换行符就换行 没读到数据返回null;包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null

读一行写一行复制文本文件:

public static void main(String[] args) throws IOException {
    BufferedWriter out = new BufferedWriter(new FileWriter("ff.txt"));
    // BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("e.txt")));
    BufferedReader in = new BufferedReader(new FileReader("MyTest.java"));
    // 读取一行,写入一行,来复制文件
    String line=null;
    while ((line=in.readLine())!=null){
        out.write(line);
        out.newLine();
        out.flush();
    }
    in.close();
    out.close();
}

案例演示1:把ArrayList集合中的字符串数据存储到文本文件

public static void main(String[] args) throws IOException {
    ArrayList<String> list = new ArrayList<>();
    list.add("贾宝玉");
    list.add("林黛玉");
    list.add("袭人");
    list.add("晴雯");
    BufferedWriter writer = new BufferedWriter(new FileWriter("hong.txt"));
    for (String name : list) {
        writer.write(name);
        writer.newLine();
        writer.flush();
    }
    writer.close();
}

案例演示2:把文本文件中的数据储存到ArrayList集合中字符串数据,并随机抽取其中一个

public static void main(String[] args) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader("hong.txt"));
    ArrayList<String> list = new ArrayList<>();
    while (true){
        String name = reader.readLine();
        if (name != null) {
            list.add(name);
        }else{
            break;
        }

    }
    // System.out.println(list);

    // 随机取一个人
    Random random = new Random();
    int index = random.nextInt(list.size());
    String s = list.get(index);
    System.out.println(s);
    // 这种设计,是要把数据和程序解耦
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值