【Java】IO流概述、字符集、字节流、相关练习

IO流的概述

概念:存储和读取数据的解决方案

作用:用于读写数据(本地、网络文件)

内存的特点:不能修改数据

按流向分:输入流(程序→文件)、输出流(文件→程序)

按操作文件的类型分:字节流(可操作所有类型的文件)、字符流(只可操作纯文本文件)

纯文本文件:Windows自带的记事本能打开并读得懂的文件(txt,md)

与File类的区别:File类只能对文件本身进行操作,不能读取文件里面储存的数据

IO流的体系和字节输出流的基本用法

IO流体系

字节流

FileOutputStream(字节输出流)

作用

可以把程序中的数据写到本地文件中,是字节流的基本流

书写步骤
  1. 创建对象

参数可以为字符串表示的路径或文件对象

如果文件不存在会创建一个新的空文件,但父级路径一定要存在

如果文件已存在,会清空文件

  1. 写出数据

write方法中写的是整数,但写到本地文件中的是ASCII表中对应的字符

  1. 释放资源

每次写完流后都要释放资源,作用为解除资源的占用(如不解除,则无法对文件进行操作,如修改、删除)

写出一段文字到本地文件中(暂时不写中文)
//出现异常原因是可能不存在此文件
        FileOutputStream fileOutputStream = new FileOutputStream("aaa");
        fileOutputStream.write(10);
        //释放资源
        fileOutputStream.close();
写数据的形式

方法名称

作用

void write(int b)

一次写一个字节数据

void write(byte [ ] b)

一次写一个字节数组数据

void write(byte [ ] b, int off, int len)

一次写一个字节数组的部分数据

方法演示
//void write(int b) 一次写一个
        FileOutputStream f = new FileOutputStream("a.txt");
        f.write(97);//写入本地文件的为 “a”
        f.close();

        //void write(byte[] b) 一次写一个数组
        byte [] a = {1, 2, 3};
        f.write(a);

       // void write(byte [ ] b, int off, int len)一次写一个数组的一部分
        /*
        * 参数含义:
        *   参数一:数组名称
        *   参数二:起始索引
        *   参数三:个数
        * */
        f.write(a, 1, 3);
换行和续写
换行

在两个输入方法之间加一个换行符

换行符:

  • Windows:\r\n

  • Linux:\n

  • Mac:\r

\r\n的由来:在Windows系统早期,\r表示回车(鼠标光标回到行首),\n表示换行

在Java中对换行符进行了优化,换行可以只写\r或\n,Java会自动补全,但还是建议写全,不要省略

续写

如果想要续写的话,需要打开续写开关,是FileOutputStream对象的第二个参数,默认是关闭的(false),这时创建对象会清空文件,当我们手动打开时,创建对象时就 不会清空文件

FileOutputStream f = new FileOutputStream("a.txt");
        //换行
        String str1 = "zxvcxzzxcv";
        String str2 = "\r\n";
        String str3 = "dafhdfhidhg";
        byte[] b1 = str1.getBytes();
        byte[] b2 = str2.getBytes();
        byte[] b3 = str3.getBytes();
        f.write(b1);
        f.write(b2);
        f.write(b3);
        f.close();
        
        //续写
        FileOutputStream fos = new FileOutputStream("a.txt", true);

FileInputStream(字节输入流)

作用

操作对象为本地文件,将本地文件的数据读取到程序之中

书写步骤
  1. 创建字节输入流对象

如果文件不存在,Java会报错

  1. 读出数据

调用一次read方法只会读取一个数据,读出来的数据是在ASCII上对应的数字,默认的返回值为int型。

如果读到文件末尾没有数据了则返回-1

如果要读取的数据为-1,会把它分开来读

  1. 释放资源

每次使用完必须释放,解除占道

读取文件中的数据(暂时不写中文)
FileInputStream f = new FileInputStream("a.txt");
        int read = f.read();
        f.close();
循环读取

创建局部变量b的原因:read方法是读取一次变量移动一次指针,如果不创建会造成数据丢失,还可能使最后一个数据的打印结果为-1

FileInputStream fis = new FileInputStream("a.txt");
        int b;
        while ((b = fis.read()) != -1) {
            System.out.println((char) b);
        }
        fis.close();
最基本的拷贝文件
FileInputStream fis = new FileInputStream("a.txt");
        FileOutputStream fos = new FileOutputStream("copy.txt");
        int b;
        while((b = fis.read()) != -1) {
            fos.write(b);
        }
        fos.close();
        fis.close();

核心思想:边读边写

释放资源规则:最先开的流最后释放

弊端:read方法一次只能读取一个字节,如果文件稍大速度就会大幅下降

一次读取多个数据的方法

方法名称

作用

public int read(byte [ ] buffer)

一次读取一个字节数组中的数据

注意:设定的数组越大,内存越大。要平衡内存与速度,最好数组可以读完全部字节不读取空字节,所以一般设定的大小为1024的倍数

读取完一次,数组中的数据就更新一次,如果到最后没有读取到数据,数组中的数据仍是上一次读取的残留数据。如果不想读取出残留数据,需要在创建对象的时候写上第二和第三个参数

byte [] b = new byte[2];
        FileInputStream fis = new FileInputStream("a.txt");
        int len = fis.read(b);
        String str = new String(b, 0, len);
        System.out.println(str);
拷贝文件改写
FileInputStream fis = new FileInputStream("a.txt");
        FileOutputStream fos = new FileOutputStream("b.txt");
        
        int len;
        byte[] b = new byte[1024 * 1024];//一次拷贝1MB
        while ((len = fis.read()) != -1) {
            fos.write(b, 0, len);
        }
        fos.close();
        fis.close();

捕获异常

try...catch异常处理

finally里面的代码一定会执行,除非JVM退出

基本处理,但释放资源代码太繁琐
FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("a.txt");
            fos = new FileOutputStream("b.txt");
            int b;
            while((b = fis.read()) != -1) {
                fos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
        //释放资源
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
不同JDK版本捕获异常的方式
JDK7

try后面的小括号中写创建对象的代码

只有实现了AutoCloseable接口的类,才能在小括号中创建对象

try (FileInputStream fis = new FileInputStream("a.txt");
             FileOutputStream fos = new FileOutputStream("b.txt")){
            int len;
            byte[] b = new byte[2];
            while((len = fis.read()) != -1) {
                fos.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
JDK9

创建对象在小括号外面,括号中写流的名称

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("a.txt");
        FileOutputStream fos = new FileOutputStream("b.txt");
        try (fis; fos){
            int len;
            byte[] b = new byte[2];
            while((len = fis.read()) != -1) {
                fos.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字符集

ASCII,GBK

在计算机中,所有数据都是以二进制的形式来存储的,最小的存储单元是一个字节

在简体中文版的Windows中,默认使用GBK字符集,其完全兼容ASCII字符集

一个英文占一个字节,二进制中第一位是零

一个中文占两个字节,二进制中第一位是一(如果只占一个字节就只能存储256个汉字,数量太少)

Unicode

一个英文占一个字符,默认以零开头(0xxxxxxx)

一个中文占三个字符,第一个字符前是1110,其他字符前为10(1110xxxx 10xxxxxx 10xxxxxx)

出现乱码的原因

  1. 读取数据是未读完整个汉字

字节流一次只能读取一个字节,负数在ASCII中无对应的值

  1. 编码和解码的方式不一样

拷贝文件没有出现乱码的原因

字节流虽然是一次拷贝一个字节,但在拷贝的过程中没有数据的丢失,只要你选择的打开文件的方式和原来的解码方式相同,就不会出现乱码

字符流

字符流的底层就是字节流,它等于字节流加字符集

特点:能根据读取数据类型的不同确定一次读取几个数据

目的:有效解决了因读取数据不全而出现的乱码现象

FileReader

  1. 创建字符输入流对象

构造方法

作用

public FileReader (File file)

创建字符输入流关联本地文件

public FileReader (String pathname)

创建字符输入流关联本地文件

如果文件不存在则直接报错

  1. 读取数据

成员方法

作用

public int read ( )

读取一个数据,读到末尾返回-1

public int read (char [ ] buffer)

读取多个数据,读到末尾返回-1

  1. 释放资源

FileReader fr = new FileReader("a.txt");
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.println((char)ch);
        }
        fr.close();

空参read方法读取数据默认是以十进制的数据返回的

带参的read构造
FileReader fr = new FileReader("a.txt");
       char [] c = new char[2];
       int len;
       while ((len = fr.read()) != -1) {
           System.out.println(new String(c, 0, len));
       }
       fr.close();

有参的read方法是将数据读取、解码并进行强转储存在插入数组中

FileWriter

FileWriter fw = new FileWriter("a.txt");
       fw.write(23132);//数字随便乱写的
       fw.close();

当文件不存在是,会自动创建一个新的文件,但要保证父级路径一定存在

当文件已经存在时,默认清空文件中的内容,如不想清空,在创建对象的时候将第二个参数改为true(续写,默认是false)

FileWriter fw = new FileWriter("a.txt",true);
       fw.write(23132);//数字随便乱写的
       fw.close();

练习

拷贝文件

将一个文件夹里的内容拷贝到另一个文件夹当中(需要考虑子文件夹)
public static void copy (File in, File out) throws IOException {
        out.mkdirs();
        File[] files = in.listFiles();
        for (File file : files) {
            if (file.isFile()) {
                FileInputStream fis = new FileInputStream(in);
                FileOutputStream fos = new FileOutputStream(new File(out, file.getName()));
                int len;
                byte [] bytes = new byte[2];
                while ((len = fis.read()) != -1) {
                    fos.write(bytes, 0, len);
                }
                fos.close();
                fis.close();
            } else {
                copy(file, new File(out, file.getName()));
            }
        }
    }

文件加密

为了保证文件的安全性,对原始文件进行加密存储,使用时进行解密
加密原理:对原始文件中的每一个字节数据进行更改,然后储存到新文件中
解密原理:对加密后的文件进行反向操作,使之变为原始文件
public static void key(File in, File out) throws IOException {
        FileInputStream fis = new FileInputStream(in);
        FileOutputStream fos = new FileOutputStream(out);
        int len;
        while ((len = fis.read()) != -1) {
            fos.write(len ^ 2);//2是随便写的,这里可以写任何数
        }
        fos.close();
        fis.close();
    }

“^”表示异或,相同为false,不同为true。一个二进制数异或同一个数两次,得到的还是原来的数

修改文件中的数据

将文本文件中的数据进行排序
public static void sort (File file) throws IOException {
        FileReader fr = new FileReader(file);
        StringBuilder sb = new StringBuilder();
        int len;
        while ((len = fr.read()) != -1) {
            sb.append((char)len);
        }
        fr.close();
        String str = sb.toString();
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < str.length(); i ++ ) {
            list.add(Integer.parseInt(str.substring(0, 1)));
            str = str.substring(1);
        }
        Collections.sort(list);
        System.out.println(list);

        FileWriter fw = new FileWriter("a.txt");
        for (Integer integer : list) {
            fw.write(integer);
        }
        fw.close();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啊呜冷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值