美文网首页
IO流、File操作

IO流、File操作

作者: 少博先生 | 来源:发表于2018-01-30 18:03 被阅读0次

    流是对字节序列的抽象,我们可以将其想象成一个水流,区别就是流的不是水而是字节。

    一、分类

    1、输入流/输出流

    水流是有方向的,java中的流也是有方向的,分为输入流、输出流。这个输入输出是相对的,都是以程序的角度来看的。从程序将数据写入文件叫做输出流,将数据从文件读入程序叫做输入流。

    2、字符流/字节流

    字节流处理字节的最小单位是单个字节,通常用来处理二进制数据,比如图片文件、音频文件等。最基本的字节流输入流是InputStream, 最基本的字节流输出流是OutputStream。
    字符流处理字节的最小单位是Unicode码元,占两个字节,通常用来处理文本数据。它存在的意义是比如像汉字,占着两个字节,如果使用字节流一个字节一个字节读取的话读的都是半个字,无法显示。最基本的字符流输入流是Reader,最基本的字符输出流是Writer。


    image.png
    3、节点流/处理流

    节点流可以从特定的数据源读取数据(节点可以是文件、内存)。
    处理流是在节点流或处理流的基础上,通过数据处理能够提供更加强大的数据存取功能。


    节点流类型
    处理流类型

    二、字节流

    1、InputStream

    InputStream:字节输入流基类,抽象类是表示字节输入流的所有类的超类 。
    常用方法:
    abstract int read() // 从输入流中读取数据的下一个字节
    int read(byte[] b) // 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中
    int read(byte[] b, int off, int len) // 将输入流中最多 len 个数据字节读入 byte 数组
    long skip(long n) // 跳过和丢弃此输入流中数据的 n个字节
    void close() // 关闭此输入流并释放与该流关联的所有系统资源

    2、OutputStream

    OutputStream:字节输出流基类,抽象类是表示输出字节流的所有类的超类。
    常用方法:
    void write(byte[] b) // 将 b.length 个字节从指定的 byte 数组写入此输出流
    void write(byte[] b, int off, int len) // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
    abstract void write(int b) // 将指定的字节写入此输出流
    void close() // 关闭此输出流并释放与此流有关的所有系统资源
    void flush() // 刷新此输出流并强制写出所有缓冲的输出字节

    3、FileInputStream

    FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流,继承自InputStream。
    构造方法:
    FileInputStream(File file)
    FileInputStream(String name)

    4、FileOutputStream

    FileOutputStream:字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。
    FileOutputStream(File file)
    FileOutputStream(File file, boolean append) //append代表是否追加
    FileOutputStream(String name)
    FileOutputStream(String name, boolean append)

    package file;
    
    import java.io.*;
    
    public class FileInputStreamTest {
    
        public static void main(String[] args) {
            try {
                //这个可以打印 Hello,world
                FileInputStream fileInputStream = new FileInputStream("D://a.txt");
                int ch = fileInputStream.read();
                while(-1 != ch){
                    System.out.print((char) ch);
                    ch = fileInputStream.read();
                }
                /**********************************/
                //这个没有打印,因为fileInputStream中的东西已经被读完了
                byte[] buffer = new byte[8];
                fileInputStream.read(buffer);
                for(int i = 0; i < buffer.length; i++){
                    System.out.print((char)buffer[i]);
                }
                /**********************************/
                //这个打印了 Hello,wo  刚好是八个字符,先把输入流中的东西读入缓存字节数组中,然后再读
                FileInputStream fileInputStream1 = new FileInputStream("D://a.txt");
                byte[] buffer1 = new byte[8];
                fileInputStream1.read(buffer1);
                System.out.println();
                for(int i = 0; i < buffer1.length; i++){
                    System.out.print((char) buffer1[i]);
                }
                /**********************************/
                //在D盘下创建了一个b.txt, 内容是 wangxban ,首先写了一个wangxb,然后再在后面加一个an
                FileOutputStream fileOutputStream = new FileOutputStream("D://b.txt");
                byte[] buffer2 = {'w','a','n','g','x','b'};
                fileOutputStream.write(buffer2);
                fileOutputStream.write(buffer2, 1,2);
                /**********************************/
                //在D盘下创建一个b文件夹,然后在b文件夹中创建一个b.txt
                //直接这样写会报错,因为没有b这个文件夹
                /*FileOutputStream fileOutputStream1 = new FileOutputStream("D:\\b\\b.txt");
                fileOutputStream1.write(buffer2);*/
                //先创建一个b文件夹,然后再往里面写
                File file = new File("D://b");
                if(!file.exists()){
                    file.mkdir();
                }
                FileOutputStream fileOutputStream1 = new FileOutputStream("D://b//b.txt");
                fileOutputStream1.write(buffer2);
    
                fileInputStream.close();
                fileInputStream1.close();
                fileOutputStream.close();
                fileOutputStream1.close();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    a.txt中的内容

    控制台执行结果:


    image.png
    5、BufferedInputStream

    BufferedInputStream:字节缓冲输入流,提高了读取效率。缓冲区(Buffer)就是内存里面的一小块区域,读写数据时都是先把数据放到这块缓冲区域里面,减少io对硬盘的访问次数。缓冲区就像水桶,把数据想象成水,把要处理的数据先放入水桶,装满后再做处理。先把数据放置到缓冲区上,等到缓冲区满了以后,再一次把缓冲区里面的数据写入到硬盘上或者读取出来,这样可以有效地减少对硬盘的访问次数。
    构造方法:
    BufferedInputStream(InputStream in)
    BufferedInputStream(InputStream in, int size)

    package file;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    public class BufferStreamDemo {
        public static void main(String[] args) {
            try {
                //读取
                FileInputStream fileInputStream = new FileInputStream("D://b.txt");
                BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
                int a = bufferedInputStream.read();
                while(-1 != a){
                    System.out.print((char) a);
                    a = bufferedInputStream.read();
                }
                /***************************************/
                //写入
                FileOutputStream fileOutputStream = new FileOutputStream("D://c.txt");
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
                byte[] bytes = {'w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b',
                        'w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b','w','a','n','g','x','b'};
                bufferedOutputStream.write(bytes);
                //flush的作用是将清空缓存区的数据,如果不flush,不会将内容写入文件
                //一定要写flush,并且flush要写在close之前,不然会导致java.io.IOException: Stream Closed
                bufferedOutputStream.flush();
                fileOutputStream.close();
                bufferedOutputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    

    三、字符流

    1.Reader

    Reader:读取字符流的抽象类.
    常用方法:
    int read() // 读取单个字符
    int read(char[] cbuf) // 将字符读入数组
    abstract int read(char[] cbuf, int off, int len) // 将字符读入数组的某一部分
    long skip(long n) // 跳过字符
    abstract void close() // 关闭该流并释放与之关联的所有资源

    2.Writer

    Writer:写入字符流的抽象类.
    常用方法:
    void write(char[] cbuf) // 写入字符数组
    abstract void write(char[] cbuf, int off, int len) // 写入字符数组的某一部分
    void write(int c) // 写入单个字符
    void write(String str) // 写入字符串
    void write(String str, int off, int len) // 写入字符串的某一部分
    Writer append(char c) // 将指定字符添加到此 writer
    Writer append(CharSequence csq) // 将指定字符序列添加到此 writer
    Writer append(CharSequence csq, int start, int end) // 将指定字符序列的子序列添加
    abstract void close() // 关闭此流,但要先刷新它
    abstract void flush() // 刷新该流的缓冲

    package file;
    
    import java.io.*;
    
    public class BufferedReaderWriterDemo {
    
        /*mkdirs()可以建立多级文件夹, mkdir()只会建立一级的文件夹, 如下:
                new File("/tmp/one/two/three").mkdirs();
        执行后, 会建立tmp/one/two/three四级目录
        new File("/tmp/one/two/three").mkdir();
        则不会建立任何目录, 因为找不到/tmp/one/two目录, 结果返回false*/
    
        public static void main(String[] args) {
            try {
                FileReader fileReader = new FileReader("D://test1.txt");
                BufferedReader bufferedReader = new BufferedReader(fileReader);
                File file = new File("D://test1");
                if(!file.exists()){
                    file.mkdir();
                }
                FileWriter fileWriter = new FileWriter("D://test1/test1.txt");
                BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
                String str = bufferedReader.readLine();
                while(str != null){
                    bufferedWriter.write(str);
                    bufferedWriter.newLine();
                    str = bufferedReader.readLine();
                }
    
                bufferedWriter.flush();
                fileReader.close();
                bufferedReader.close();
                fileWriter.close();
                bufferedWriter.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    四、字节缓冲流

    ByteArrayOutputStream/ByteArrayInputStream

    package file;
    
    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class ByteArrayStreamDemo {
        //ByteArrayOutputStream在创建实例时,内部创建一个byte型的数组缓冲区,
        //然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写
        //入或读出byte型数据。在网络传输中我们往往要传输很多变量,我们可以利用
        //ByteArrayOutputStream把所有的变量收集到一起,然后一次性把数据发送出去。
    
        public static void main(String[] args) {
            try {
                FileInputStream fileInputStream = new FileInputStream("D://a.txt");
                //在创建ByteArrayOutputStream类实例时,内存中会创建一个byte数组类型的缓冲区,
                //缓冲区会随着数据的不断写入而自动增长
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                //将内容读入到buffer中
                while((len = fileInputStream.read(buffer)) != -1){
                    //将每次督导字节数组中的内容写入内存缓冲区
                    byteArrayOutputStream.write(buffer, 0 , len);
                }
                //可使用toByteArray()和toString()获取数据
                byte[] data = byteArrayOutputStream.toByteArray();
                //关闭ByteArrayOutputStream无效,此类中的方法在关闭此流后仍可被调用,
                //而不会产生任何IOException
                fileInputStream.close();
                String result = new String(data, "UTF-8");
                System.out.println(result);
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    五、数据流

    DataOutputStream/DataInputStream

    package file;
    
    import java.io.*;
    
    public class DataStreamDemo {
        //来读取和写各种类型的数据,如字符串、boolean、double、float
        //FileOutPutStream也能写,但只能写int、byte
        //一定要注意 DataOutputStream 与DataInputStream配合使用,而且二者读写的顺序要一样
        public static void main(String[] args) {
            //定义字节数组流
            try {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
                dataOutputStream.writeUTF("Hello");  //字符串
                dataOutputStream.writeChar('C');      //单个字符
                dataOutputStream.writeBoolean(true);
                dataOutputStream.writeDouble(3D);
                dataOutputStream.writeFloat(3F);
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                //返回从这个输入流中读取余下的字节数
                System.out.println(byteArrayInputStream.available());
                DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
                System.out.println(dataInputStream.readUTF());
                System.out.println(dataInputStream.readChar());
                System.out.println(dataInputStream.readBoolean());
                System.out.println(dataInputStream.readDouble());
                System.out.println(dataInputStream.readFloat());
                dataOutputStream.close();
                dataInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    六、打印流

    PrintStream/PrintWriter

    package file;
    
    import java.io.*;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class PrintStreamDemo {
        public static void main(String[] args) {
            testPrintStream();
            //testPrintWriter();
        }
    
        public static void testPrintStream(){
            PrintStream printStream = null;
            try {
                FileOutputStream fileOutputStream = new FileOutputStream("D://e.txt");
                printStream = new PrintStream(fileOutputStream);
                if(printStream != null){
                    System.setOut(printStream);
                }
                for(int c = 0; c <= 60; c++){
                    //上面设置了输出到打印流中,控制台不打印
                    System.out.print(c + ",");
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        public static void testPrintWriter(){
            String s = null;
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            try {
                FileWriter fileWriter = new FileWriter("D://f.txt");
                PrintWriter printWriter = new PrintWriter(fileWriter);
                while((s = bufferedReader.readLine()) != null){
                    if(s.equalsIgnoreCase("exit")){
                        break;
                    }
                    System.out.println(s.toUpperCase());
                    printWriter.println(s.toUpperCase());
                }
                printWriter.println("*************" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
                printWriter.flush();
                printWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    七、对象流

    ObjectOutputStream/ObjectInputStream

    package file;
    
    import java.io.*;
    
    public class ObjectStreamDemo {
        public static void main(String[] args) {
            User user = new User();
            user.age = 12;
            user.name = "wade";
            user.name = "911";
            try {
                FileOutputStream fileOutputStream = new FileOutputStream("D://g.txt");
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
                objectOutputStream.writeObject(user);
                objectOutputStream.flush();
                objectOutputStream.close();
                FileInputStream fileInputStream = new FileInputStream("D://g.txt");
                ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
                User user1 = (User) objectInputStream.readObject();
                System.out.println(user1.toString());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    
        static class User implements Serializable{
            private int age;
            private String name;
            //transient透明的,用来修饰的变量在序列化时不予考虑,当其不存在
            private transient String phone;
    
            @Override
            public String toString() {
                return "User{" +
                        "age=" + age +
                        ", name='" + name + '\'' +
                        ", phone='" + phone + '\'' +
                        '}';
            }
        }
    }
    

    八、文件操作

    1、删除文件或文件夹
    package file;
    
    import java.io.File;
    
    public class FileDemo1 {
        public static boolean deleteFilesOrDir(String fileName){
            File file = new File(fileName);
            if(!file.exists()){
                System.out.println("文件删除失败," + fileName + "不存在");
                return false;
            }else{
                if(file.isFile()){
                    return FileDemo1.deleteFiles(fileName);
                }else{
                    return FileDemo1.deleteDir(fileName);
                }
            }
        }
    
        public static boolean deleteFiles(String fileName){
            File file = new File(fileName);
            if(file.exists() && file.isFile()){
                if(file.delete()){
                    System.out.println("文件删除成功");
                    return true;
                }else{
                    System.out.println("文件删除失败");
                    return false;
                }
            }else{
                System.out.println("文件删除失败," + fileName + " 不存在");
                return false;
            }
        }
    
        public static boolean deleteDir(String dir){
            if(!dir.endsWith(File.separator)){
                dir += File.separator;
            }
            File dirFile = new File(dir);
            if(!dirFile.exists() || !dirFile.isDirectory()){
                System.out.println("文件夹删除失败 !" + dir + " 不存在");
                return false;
            }
            boolean flag = true;
            File[] files = dirFile.listFiles();
            for(int i = 0; i < files.length; i++){
                if(files[i].isFile()){
                    flag = FileDemo1.deleteFiles(files[i].getAbsolutePath());
                    if(!flag){
                        break;
                    }
                }else if(files[i].isDirectory()){
                    flag = FileDemo1.deleteDir(files[i].getAbsolutePath());
                    if(!flag){
                        break;
                    }
                }
            }
            if(!flag){
                System.out.println("删除文件夹失败");
            }
            if(dirFile.delete()){
                System.out.println("文件夹" + dir + "删除成功!");
                return true;
            }else{
                return false;
            }
        }
    }
    
    2、移动文件或文件夹
    package file;
    
    import java.io.File;
    
    public class FileDemo2 {
    
        public static void main(String[] args) {
    //        System.out.println("调用移动文件");
    //        String sourceFileName = "D://b//bbb.txt";
    //        String targetFileName = "D://test2//bbb.txt";
    //        FileDemo2.moveFile(sourceFileName, targetFileName);
    
            System.out.println("调用移动目录");
            String sourceDir = "D://b//b";
            String targetDir = "D://test1";
            FileDemo2.moveDir(sourceDir, targetDir, true);
        }
    
        public static boolean moveFile(String sourceFileName, String targetFileName){
             return FileDemo2.moveFile(sourceFileName, targetFileName, true);
        }
    
        public static boolean moveDir(String sourceDirName, String targetFileName){
            return FileDemo2.moveDir(sourceDirName, targetFileName, false);
        }
    
        public static boolean moveFile(String sourceFileName, String targetFileName, boolean isOverlay){
            File sourceFile = new File(sourceFileName);
            if(!sourceFile.exists()){
                System.out.println("文件" + sourceFileName + "不存在,移动失败");
                return false;
            }else if(!sourceFile.isFile()){
                System.out.println(sourceFileName + "不是文件,移动失败");
                return false;
            }
            File targetFile = new File(targetFileName);
            if(targetFile.exists()){
                if(isOverlay){
                    System.out.println("目标文件已存在,准备删除它");
                    if(!FileDemo1.deleteFilesOrDir(targetFileName)){
                        System.out.println("文件移动失败,文件" + targetFileName + "删除失败");
                        return false;
                    }
                }else{
                    System.out.println("文件移动失败,文件" + targetFileName + "已存在");
                    return false;
                }
            }else{
                if(!targetFile.getParentFile().exists()){
                    System.out.println("文件" + targetFile + "所在目录不存在,正在创建");
                    if(!targetFile.getParentFile().mkdirs()){
                        System.out.println("移动文件失败,创建文件所在的文件夹失败");
                        return false;
                    }
                }
            }
    
            if(sourceFile.renameTo(targetFile)){
                System.out.println("移动源文件" + sourceFileName + "到" + targetFileName + "成功");
                return true;
            }else{
                System.out.println("移动源文件" + sourceFileName + "到" + targetFileName + "失败");
                return false;
            }
        }
    
    
        public static boolean moveDir(String sourceDirName, String targetDirName, boolean isOverlay){
            File sourceDir = new File(sourceDirName);
            if(!sourceDir.exists()){
                System.out.println("源目录" + sourceDirName + "不存在,移动目录失败");
                return false;
            }else if(!sourceDir.isDirectory()){
                System.out.println("移动目录失败," + sourceDirName + "不是目录");
                return false;
            }
            //如果目标文件名不是以文件分隔符结尾,自动添加文件分隔符
            if(!targetDirName.endsWith(File.separator)){
                targetDirName += File.separator;
            }
            File targetDir = new File(targetDirName);
            if(targetDir.exists()){
                if(isOverlay){
                    System.out.println("该目录已存在,准备删除它");
                    if(!FileDemo1.deleteFilesOrDir(targetDirName)){
                        System.out.println("移动目录失败,因目标目录已存在,删除目录" + targetDirName + "失败");
                        return false;
                    }
                }else{
                    System.out.println("移动目录失败," + targetDirName + "已存在!");
                    return false;
                }
            }else{
                System.out.println("该目录不存在,正在创建");
                if(!targetDir.mkdirs()){
                    System.out.println("移动目录失败,创建目标目录失败");
                    return false;
                }
            }
            boolean flag = true;
            File[] files = sourceDir.listFiles();
            for(int i = 0; i < files.length; i++){
                if(files[i].isFile()){
                    flag = FileDemo2.moveFile(files[i].getAbsolutePath(),
                            targetDirName + files[i].getName(), isOverlay);
                    if(!flag){
                        break;
                    }
                }else if(files[i].isDirectory()){
                    flag = FileDemo2.moveDir(files[i].getAbsolutePath(),
                            targetDirName + files[i].getName(), isOverlay);
                    if(!flag){
                        break;
                    }
                }
            }
            if(!flag){
                System.out.println("目录" + sourceDirName + "移动到" + targetDirName + "失败");
                return false;
            }
            //删除原目录
            if(FileDemo1.deleteDir(sourceDirName)){
                System.out.println("目录" + sourceDirName + "移动到" + targetDirName + "成功");
                return true;
            }else{
                System.out.println("目录" + sourceDirName + "移动到" + targetDirName + "失败");
                return false;
            }
        }
    
    }
    
    

    九、总结

    1、InputStream/OutputStream,Reader/Writer 四个最基本的抽象类。
    2、FileInputStream/FileOutputStream,FileReader/FileWriter 以File开头的类都是对文件进行读写操作的。
    3、InputStreamReader/OutputStreamWriter 转化流,将字节流转换为字符流。
    4、DataInputStream/DataOutputStream 通过数据流直接写基本类型数据,两者配合使用,读写顺序必须一致。
    5、PrintStream/PrintWriter 打印流,没有相应的输入和输出。
    6、ObjectInputStream/ObjectOutputStream 对象流,序列化对象需要实现Serializable接口,如果某个字段的安全性较高,不想被序列化,可以使用tranisent修饰,序列化对象时就会忽略此变量。

    相关文章

      网友评论

          本文标题:IO流、File操作

          本文链接:https://www.haomeiwen.com/subject/virczxtx.html