字节流与字符流梳理

字节流与字符流梳理

哈喽大家好今天又见面了,今天想写的是IO流的一些东西,之前承诺的单例模式的东西我在微信公众号(狗蛋科技)里面已经写好了,就是想着自己重新整理一下可能会比较好,所以今天我暂且不提单例模式,今天着重学习一下IO流的基础知识。

分类

首先上来就需要我们了解IO流的分类,之前面试有面试官问我,自己当时确实也不是很会,所以和工作机会错失良机,所以今天我们先看一张图:
IO流分类
首先我们可以看到IO流按照类型来分类的话,有两种,一种是字符流,一种是字节流。这个细分再往下看,可能你会觉得有很多,所以咱们暂且不看这张图,只需要了解一下就可以了。我接下来要讲的是File。

File

首先我们要知道,File是个类,也是需要实例化的,实例化过程和平时一样,这里着重介绍几个常用的方法:

public class TestCase {
    public static void main(String[] args) {
        File file = new File("src/com/hzy/IO/data/IO.txt");
        //File file = new File("C:/...../IO.txt");  绝对路径也是可以的。

        File directory = new File("src/com/hzy/IO/data");
        System.out.println(directory.isDirectory());
        System.out.println(file.isFile());
    }
}

首先这里我是踩过坑的,挖坑的地方就是自己当初创建文件夹时,是在src目录下创建了一个叫做“com.hzy”的文件夹,我以为路径是这样的:(src/com.hzy),实际上在文件夹里面,不是有个文件夹叫做“com.hzy”,而是src下有个com文件夹,com文件夹下面又有一个hzy的文件夹,所以路径应该是:(src/com/hzy)。

这里解释一下File里面的方法:

  1. isDirectory() 是否为文件夹
  2. isFile() 是否为一个文件

file.length()

这个是文件长度,常用于文件拷贝,拷贝前查看文件长度以及文件夹大小,进行比较,如果文件夹过小就拷贝失败,比他大就拷贝成功。


File file1 = new File("D:/");
        System.out.println(file1.getFreeSpace());

这个是查看目录剩余空间大小的,这里是查看了D盘的剩余空间,出来的是一个很大的数字,因为是用字节b表示的,大家换算成GB就可以看懂了。(1千兆字节(gb)=1073741824字节(b))


File file2 = new File("src/com/hzy/IO/data/新建.txt");
file2.createNewFile();

这是创建一个新的文件,注意要throws Exception给JVM,否则编译不通过。


这里我复制了一下之前我的一份文件,重命名成IOO,并且设置成只读。
只读
这里我用了一个方法叫做:

File file3 = new File("src/com/hzy/IO/data/IOO.txt");
        System.out.println(file3.canWrite());

答案是false,要记住,设置成只读,意思就是不能修改重写了,所以用Boolean类型的canWrite()方法答案是false。如果是canRead()当然就是true啦~
当然你也不需要这么麻烦,你可以直接set就可以轻松修改权限:
在这里插入图片描述
这个只要还是看你平时的积累,比方说还有一个方法叫做lastModified()方法,可以看出你最后一次多会修改的文件。


当然你还可以删除文件:file3.delete();


获取文件名:file3.getName();


重命名:file3.renameTo();




字节流

说完File类的常用方法,我们了解一下字节流,之前我们只是会操控文件了,可以进行一下增删查改的操作,那么如何将文件加载到内存中去呢?这里我们就得用到字节流的知识点了。
首先字节流和字符流都是分为读和写两个分支的,但是有所不同,字符流是分为(Reader 和 Writer),字节流分为(InputStream 和 OutputStream)
更简单来说就是:
字符流是人能看懂的文字,字节流是更专注于底层的一种代码。
举例说一下图片吧,看似能看懂,其实底层都是那种乱七八糟的代码,这就是字节流。视频也是如此。
这四个类都是抽象类,意味着我们不能直接new出来对象。
所以需要用多态的方法具体new出来实例


字节流输入流(InputStream)

public class TestCase {
    public static void main(String[] args) throws Exception{
        File file = new File("src/com/hzy/IO/data/IO.txt");
        InputStream inputStream = new FileInputStream(file);
        System.out.println(inputStream.available());
        System.out.println(inputStream.read());
        System.out.println(inputStream.read());
        System.out.println(inputStream.available());
    }
}

这里InputStream inputStream = new FileInputStream(file);就是运用多态实例化对象,这时文件就进入内存中了,这里我使用了一个方法叫做.available(),这个方法可以在读写操作前先得知数据流里有多少个字节可以读取。我们可以看一下控制台输出:

44
213
226
42

这里的213和226是我文件里面第一个和第二的ACSII值,44是.available()可用值,在经过两次阅读之后,.available()值当然减小了两个变成了42.

这里别看他比较简单,通过这两个方法就可以将视频播放器的进度条做出来,并且还能完成播放器的断点播放功能。

如果有想法想自己动手的同学,可以了解一下.mark();.skip();方法,一个是用来标记的,一个是用来跳过用的,这里咱们比较熟知的“跳过片头片尾”就是运用的这种方法。


这里我们可以再看一下.read()方法的另一种返回值类型,那就是数组类型,这里我们创建一个数组,将当前可用的字节流传进去,转化成字符串类进行输出,就可以看到我们文件里面写的什么内容了~

byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
String string = new String(bytes);
System.out.println(string);

这里是控制台输出(也是文章水印hhh):

HZY CSDN blog
VX:652355283
ABCDEFGHIJKL

字节流输出流(OutputStream)

首先我们需要了解的是怎么去实例出这样一个字符节流输出流,其实具体方法还是和输入流是一样的,都是需要具体的一个类去实现,也就是用到了多态的方法。具体方法如下:

OutputStream outputStream = new FileOutputStream("src/com/IO_out.txt")

这里总结一下,输入流使用的FileInputStream,输出流用的是FileOutputStream

OutputStream outputStream = new FileOutputStream("src/com/IO_out.txt");
outputStream.write(bytes);
outputStream.flush();//清空输出缓冲区

这里需要注意有一个.flush()方法,具体注意事项是这样的:flush() 是清空输出缓冲区的意思,而不是刷新,一般主要用在IO中,即清空缓冲区数据,就是说你用读写流的时候,其实数据是先被读到了内存中,然后用数据写到文件中,当你数据读完的时候不代表你的数据已经写完了,因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了 close()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之前先flush(),先清空数据。


字节流读取复写到新文件(复制)

接下来我们就可以了解一下如何复制一个文件了!具体思想就很简单,将读入的东西一个个读出不就好了,所以代码如下:

public class Copy {
    public static void main(String[] args) throws Exception{
FileInputStream in = new FileInputStream("src/com/data/github.jpg");
FileOutputStream out = new FileOutputStream("src/com/data/github_02.jpg");
        int data = 0;
        while ((data = in.read()) != -1){
            out.write(data);
        }
        out.flush();
    }
}

这里别说你了,我自己看的都麻烦,所以说有必要解释一下这个代码了,这里首先我们能看到的是实例化了一个输入流,一个输出流,然后通过循环语句用.read()读取流中的第一个字节数据,一次读一个字节,依次读取后续的数据,最后将这些数据一个个输出到"src/com/data/github_02.jpg"里面就完成了数据的复制。


BASE_64转码

这里说一个题外话,也是我的老师讲的,就是假如你给别人传递消息,比方说“新年快乐”这四个字,你不想让别人很轻易的获取这个文字,你就可以“加密”,让这四个字转化成“*&¥…@%…&#(反正就是别人看不懂的字符)”,这里的“加密”其实就是一种转码方式,叫做base64

就比方说:JXU4RkQ5JXU5MUNDJXU2NjJGJXU1NDdDJXU1MzUzJXU1Qjg3JXU3Njg0Q1NETiV1NTM1QSV1NUJBMg==
这串字符翻译过来就是:这里是呼卓宇的CSDN博客

我是真没有骗大伙,网上可以找个base64解密工具看看,挺好玩的还是~


接下来我就具体说说这个转码工具怎么运行的,首先这个base_64来自sun.misc.BASE64Encoder;这个包,先用输入流把文件读入内存中,然后声明一个数组,把读出来的东西都扔到数组里面,然后声明一个字符串,实例化一个对象BASE64Encoder encoder = new BASE64Encoder();,然后把这个数组编码,然后出来就是base_64。具体代码如下:

import sun.misc.BASE64Encoder;

import java.io.FileInputStream;

public class TestCase {
    public static void main(String[] args) throws Exception{
        FileInputStream in = new FileInputStream("src/com/data/github.jpg");
        byte[] bytes = new byte[in.available()];
        in.read(bytes);
        String base64 = new String();
        base64.replaceAll("","+");
        BASE64Encoder encoder = new BASE64Encoder();
        base64 = encoder.encode(bytes);
        System.out.println(base64);

    }
}

Buffer的厉害之处

这里我还得穿插一个小知识点,大家跟着我慢慢来,先看代码:

public class Copy {
    public static void main(String[] args) throws Exception{
        FileInputStream in = new FileInputStream("src/com/data/github.jpg");
        FileOutputStream out = new FileOutputStream("src/com/data/github_02.jpg");


        long start = System.currentTimeMillis();  //※
        

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

        long end = System.currentTimeMillis();   //※
        System.out.println(end-start);
    }
}

这里计算了一下复制一个图片需要多长时间,答案190ms。此时要引入一个新的知识,那就是BufferInputStream 和 BufferOutputStream

这个就和String和StringBuffer一样,一加buff就牛逼,这里也是一样的。之前相当于考试作弊,别人写一笔,你抄人家一笔,但是一加Buffer以后,相当于你扯过人家卷子就抄,因为有个Buffer以后,你有缓存区了,你牛逼了,抄的就快了,所以如果执行以下代码,时间自然而然缩短不少:

public class Copy {
    public static void main(String[] args) throws Exception{
        FileInputStream in = new FileInputStream("src/com/data/github.jpg");
        FileOutputStream out = new FileOutputStream("src/com/data/github_02.jpg");

        BufferedInputStream bin = new BufferedInputStream(in);
        BufferedOutputStream bout = new BufferedOutputStream(out);


        long start = System.currentTimeMillis();
        int data = 0;
        while ((data = bin.read()) != -1){
            bout.write(data);
        }
        bout.flush();
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
}

这里通过BufferedInputStreamBufferedOutputStream承接了一下字节流,用Buffer包裹住了字节流,架设在Buffer之上,让他就能拥有Buffer的功能。最后复制时间是5ms,数量级缩小了100倍,这就是有了Buff的力量。

BufferedInputStream bin = new BufferedInputStream(in);
BufferedOutputStream bout = new BufferedOutputStream(out);

字符流

字符流读取

public class TestCase {
    public static void main(String[] args) throws Exception{
        Reader reader = new InputStreamReader(new FileInputStream("src/com/hzy/IO/data/IO.txt"));
        BufferedReader buffer = new BufferedReader(reader);

        while (buffer.ready()){
            System.out.println(buffer.readLine());
        }
    }
}

先看一段代码,听我慢慢解释,第一句Reader reader = new InputStreamReader(new FileInputStream("src/com/hzy/IO/data/IO.txt"));是一个字符流,然后架设在了缓冲流上面BufferedReader buffer = new BufferedReader(reader);这两个,一个是数据源,一个是优化器,然后用一个循环语句判断缓冲流是否准备好,如果准备好那就“一行一行读”,buffer.readLine()返回的是字符串String类型。大家看这个返回值是不是不需要转化了,所以这就是一种高级流,所以这就是字符流。


字符流读取复写到新文件

public class TestCase_02 {
    public static void main(String[] args) throws Exception{
        Reader reader = new InputStreamReader(new FileInputStream("src/com/hzy/IO/data/IO.txt"));
        Writer writer = new OutputStreamWriter(new FileOutputStream("src/com/hzy/IO/data/IOOUT.txt"));
        BufferedReader buffer = new BufferedReader(reader);
        BufferedWriter bwrite = new BufferedWriter(writer);

        while (buffer.ready()){
            String line = buffer.readLine();
            bwrite.write(line);
            bwrite.newLine();
            bwrite.newLine();
        }
        bwrite.flush();
    }
}

这里还是老样子,数据源+Buffer优化器,四条语句很快写完,然后通过循环语句,用字符串String类型承接读到的内容,然后进行打印输出,这里是用bwrite.newLine();添加了两行空行(等同于回车)来作为区别。最后文件中的文字确实比之前多了两空行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值