• Java语言基础IO流(输入输出流) 字符流


    IO(Input Output)流

    Java对数据的操作时通过流的方式;
    Java用于操作流的对象都在IO包中;
    流按操作数据分为两种:字节流与字符流;
    流按流向分为:输入流,输出流。

    输入输出流是相对于内存设备而言;
    将外设中的数据读取到内存中--输入;
    将内存中的数据写入到外设中--输出。

    字符流:就是用于读取文字字节数据的字节流与编码表相结合,封装成字符流。
    (字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字,再对这个文字进行操作。)

    字节流的抽象基类(顶层父类):
    InputStream,OutputStream

    字符流的抽象基类(顶层父类):
    Reader,Writer

    这些体系的子类都以父类名作为后缀;
    子类名的前缀就是该对象的功能。

    需求:将一些文字存储到硬盘的文件中
    如果要操作文字数据,建议优先考虑字符流;
    而且要将数据从内存写到硬盘上,需要使用字符流中的输出流Writer。
    硬盘的数据的基本体现是文件,希望找到一个可以操作文件的Writer。

    private static final String LINE_SEPARATOR = System.getProperty("line.separator");//定义行分隔符常量
    基本方法示例:

    package cn.itcast.p2.io.filewriter;
    
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class FileWriterDemo {
    
        private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    
        /**
         * @param args
         * @throws IOException
         */
        public static void main(String[] args) throws IOException {
    
            /*
             * 创建一个可以往文件中写入字符的字符输出流对象 。 往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件
             * 
             * 如果文件不存在,则会自动创建。 如果文件存在,则源文件被覆盖。
             * 
             * 如果构造函数中加入true,可以实现对文件的续写
             */
            FileWriter fw = new FileWriter("demo.txt",true);
    
            /*
             * 调用Writer对象中的write(String)方法,写入数据。 数据写入到了临时存储缓冲区中。
             */
    //        fw.write("abcde"+LINE_SEPARATOR+"hahaha");
            fw.write("xixi");
    
            /*
             * 进行刷新,将数据直接写入到目的地
             */
            // fw.flush();
    
            /*
             * 关闭资源。在关闭前,会调用fluse()方法刷新缓冲中的数据到目的地。
             */
            fw.close();
        }
    
    }
    
    


    FileWriter的IO异常处理示例:

    package cn.itcast.p2.io.filewriter;
    
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class IOExceptionDemo {
    
        private static final String LINE_SEPARATOR = System
                .getProperty("line.separator");
        public static void main(String[] args) {
    
            FileWriter fw = null;// 在try外进行定义,内部进行初始化
            try {
                fw = new FileWriter("k:\\demo.txt");
    
                fw.write("abcde" + LINE_SEPARATOR + "hahaha");
            } catch (IOException e) {
                System.out.println(e.toString());
            } finally {
                if (fw != null)//如果fw创建文件失败时,关闭时会出现空指针异常
                    try {
                        fw.close();
                    } catch (IOException e) {
                        throw new RuntimeException("关闭失败");
                    }
            }
        }
    
    }
    
    

    需求:读取一个文本文件,将读取到的字符打印到控制台。
    同上,找到了FileReader。
    read()方法
    public int read() throws IOException
    读取单个字符。
    返回:作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1。※

    package cn.itcast.p3.io.filereader;
    
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileReaderDemo {
        public static void main(String[] args) throws IOException {
            // 创建读取字符数据的流对象
            /*
             * 在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件时存在的。
             * 
             * 用一个读取流关联一个已存在文件
             */
            FileReader fr = new FileReader("demo.txt");
    
            // 用Reader中的read方法读取字符
            int ch = 0;
            while ((ch = fr.read()) != -1) {
                System.out.println((char) ch);
            }
    
            fr.close();
        }
    
    }
    
    


    public int read(char[] cbuf) throws IOException
    将字符读入数组。
    参数:cbuf - 目标缓冲区
    返回:读取的字符数,如果已到达流的末尾,则返回 -1 ※

    package cn.itcast.p3.io.filereader;
    
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileReaderDemo2 {
        public static void main(String[] args) throws IOException {
            FileReader fr = new FileReader("demo.txt");
    
            /*
             * 使用read(char())读取文本文件数据 先创建字符数组
             */
            char[] buf = new char[1024];//长度最好为1024的整数倍
            int len=0;
            while((len=fr.read(buf))!=-1){
                System.out.println(new String(buf,0,len));
            }
    
            /*int num = fr.read(buf);// 将读到的字符存储到数组中
            System.out.println(num + ":" + new String(buf));
            int num1 = fr.read(buf);
            System.out.println(num1 + ":" + new String(buf));
            int num2 = fr.read(buf);
            System.out.println(num2 + ":" + new String(buf));*/
            /*
             * abcde# 
             * 结果: 
             * 3:abc 
             * 2:dec 
             * -1:dec 
             * 第一次读:a b c (从a处开始,读取到3个字符,返回3) 
             * 第二次读:d e c (c是此一次读取的c,从d处开始,读取到2个字符,返回2) 
             * 第三次读:d e c (c是此一次读取的c,开始处即是流的末尾,未读取到字符,返回-1)
             */
            fr.close();
        }
    
    }
    
    


    将C盘的一个文本文件复制到D盘
    分析:
    复制的原理:
    读取C盘文件中的数据,将这些数据写入到D盘中。
    读&写。

    方法一:

    package cn.itcast.p3.io.charstream.test;
    
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    /*
     * 需求:将C盘的一个文本文件复制到D盘
     * 思路:
     * 1.需要读取源;
     * 2.将读到的源数据写入到目的地
     * 3.操作文本数据,使用字符流
     */
    public class CopyTextTest {
    
        public static void main(String[] args) throws IOException {
    
            //1.读取一个已有的文本文件,用字符读取流和文件相关联
            FileReader fr=new FileReader("IO流_2.txt");
            //2.创建一个目的,用于存储读到的数据
            FileWriter fw=new FileWriter("copytest_1.txt");
            //3.频繁的读写操作
            int ch=0;
            while((ch=fr.read())!=-1){
                fw.write((char)ch);
            }
            //4.关闭流资源
            fw.close();
            fr.close();
            
        }
    }
    
    

    方法二(效率较高):

    package cn.itcast.p3.io.charstream.test;
    
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class CopyTextTest_2 {
    
        private static final int BUFFER_SIZE = 1024;
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            FileReader fr = null;
            FileWriter fw = null;
    
            try {
                fr = new FileReader("IO流_2.txt");
                fw = new FileWriter("copytest_2.txt");
                // 创建一个临时容器,用于缓存读取到的字符
                char[] buf = new char[BUFFER_SIZE];//自定义缓冲区
    
                // 定义一个变量,记录读取到的字符数(其实就是网数组里装的字符数)
                int len = 0;
                while ((len = fr.read(buf)) != -1) {//循环次数少,效率较高
                    fw.write(buf, 0, len);
                }
            } catch (Exception e) {
    
            } finally {
                if (fw != null)
                    try {
                        fw.close();
                    } catch (IOException e) {
                        // System.out.println("读写失败");
                        throw new RuntimeException("读写失败");
                    }
                if (fr != null)
                    try {
                        fr.close();
                    } catch (IOException e) {
                        throw new RuntimeException("读写失败");
                    }
            }
        }
    
    }
    
    

    字符流的缓冲区(提高性能)
    public class BufferedReader extends Reader
    从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

    public class BufferedWriter extends Writer
    将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

    BufferedWriter示例:

    package cn.itcast.p3.io.charstream.buffer;
    
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class BufferedWriterDemo {
    
        private static final String LINE_SEPEARTOR = System
                .getProperty("line.separator");
    
        /**
         * @param args
         * @throws IOException
         */
        public static void main(String[] args) throws IOException {
            FileWriter fw = new FileWriter("buf.txt");
    
            // 为了提高写入的效率,使用了字符流的缓冲区
            // 创建了一个字符写入流的缓冲对象,并和指定要被换种的对象相关联
            BufferedWriter bufw = new BufferedWriter(fw);
    
            // 使用缓冲区的写入方法,将数据写入到缓冲区中
            // bufw.write("abcde"+LINE_SEPEARTOR+"haha");
    
            // bufw.write("abcde");
            // bufw.newLine();
            // bufw.write("heheh");
    
            for (int i = 1; i <= 4; i++) {
                bufw.write("abcde" + i);
                bufw.newLine();//BufferedReader特有的方法newLine()
                bufw.flush();
            }
    
            // 使用缓冲区的刷新方法将数据刷入目的地中
            bufw.flush();
    
            bufw.close();// 缓冲区关闭时,底层关闭的是缓冲的流对象
        }
    
    }
    
    

    BufferedReader使用示例:

    package cn.itcast.p3.io.charstream.buffer;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class BufferedReaderDemo {
    
        /**
         * @param args
         * @throws IOException
         */
        public static void main(String[] args) throws IOException {
            // demo();
    
            FileReader fr = new FileReader("buf.txt");
    
            BufferedReader bufr = new BufferedReader(fr);
    
            String line = null;
            while ((line = bufr.readLine()) != null) {//BufferedReader特有方法readLine()
                System.out.println(line);
            }
        }
    
        /**
         * @throws FileNotFoundException
         * @throws IOException
         */
        private static void demo() throws FileNotFoundException, IOException {
            FileReader fr = new FileReader("buf.txt");
    
            char[] buf = new char[1024];
    
            int len = 0;
            while ((len = fr.read(buf)) != -1) {
                System.out.println(new String(buf, 0, len));
            }
    
            fr.close();
        }
    
    }
    
    

    ※底层流对象的read方法读的是硬盘中的数据,缓冲区流对象的read读的是缓冲区中的数据。
    readLine()方法的临时容器中存储的是一行的数据(这个容器可以使StringBuilder,以为最终返回的是字符串),不包含换行符。返回该行内容的字符串。
    readLine()原理:使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记,将标记前的缓冲数据变成字符串返回。

    缓冲区-复制文本文件示例:

    package cn.itcast.p3.io.charstream.buffer.test;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class CopyTextBufferTest {
    
        /**
         * @param args
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
            FileReader fr=new FileReader("buf.txt");
            BufferedReader bufr=new BufferedReader(fr);
            
            FileWriter fw=new FileWriter("buf_copy.txt");
            BufferedWriter bufw=new BufferedWriter(fw);
            
            String line=null;
            while((line=bufr.readLine())!=null){
                bufw.write(line);
                bufw.newLine();//不要忘记换行
            }
            bufw.close();
            bufr.close();
            
            /*
            int ch=0;
            while((ch=bufr.read())!=-1){
                bufw.write(ch);
            }
            bufw.close();
            bufr.close();
            */
        }
    
    }
    
    


    自定义MyBufferedReader

    package cn.itcast.p4.io.charstream.mybuffer;
    
    import java.io.FileReader;
    import java.io.IOException;
    
    /**
     * 自定义的读取缓冲区,其实就是模拟一个BufferedReader
     * 
     * 分析: 
     * 缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。
     * 其实这些方法最终操作的都是数组的指针。
     * 
     * 缓冲的原理:
     * 从源中获取一批数据装入缓冲区中,然后从缓冲区中不断取出一个一个的数据。
     * 当此次取完后,再从源中继续取一批数据进入缓冲区。
     * 当源中的数据取光时,用-1作为结束标记。
     * @author chenchong
     * 
     */
    public class MyBufferedReader {
        private FileReader r;
    
        //定义一个数组作为缓冲区
        private char[] buf=new char[1024];
        //定义一个指针,用于操作数组中的元素,当操作到最后一个元素后,指针归零
        private int pos=0;
        
        //定义一个计数器,用于记录缓冲区中的数据个数,当该数据减到0,
        //就从源中继续获取数据到缓冲区中
        private int count=0;
        
        public MyBufferedReader(FileReader r) {
            this.r = r;
        }
    
        /**
         * 该方法从缓冲区中一次取一个字符
         * @return
         * @throws IOException
         */
        public int myRead() throws IOException {
    
            if(count==0){
                count=r.read(buf);//从源中获取数据
                pos=0;
            }
            if(count<0)
                return -1;
            
            char ch=buf[pos++];
            
            count--;
            return ch;
            
            /*//1.从源中取出一批数据到缓冲区中,要先做判断,
            //只有计数器为0时才 需要从源中获取数据
            if(count==0){
                count=r.read(buf);
                
                if(count<0)
                    return -1;
                
                //每次获取数据到缓冲区后,角标归零
                pos=0;
                char ch=buf[pos];
                
                pos++;
                count--;
                return ch;
            }else if(count>0){
                char ch=buf[pos];
                
                pos++;
                count--;
                return ch;
            }*/
            
        }
    
        public String myReadLine() throws IOException {
    
            StringBuilder sb=new StringBuilder();
            int ch=0;
            while((ch=myRead())!=-1){
                if(ch=='\r')
                    continue;
                if(ch=='\n')
                    return sb.toString();
                
                //将从缓冲区中读到的字符存储到缓存行数据的缓冲区中
                sb.append((char)ch);
            }
            if(sb.length()!=0)
                return sb.toString();
            return null;
        }
    
        public void myClose() throws IOException {
            r.close();
        }
    
    }
    
    

    装饰设计模式
        对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。装饰和继承都能实现一样的特点:进行功能的扩展增强
    区别:
    举例:
    所有线有一个继承体系,
    Writer
        |--TextWriter        用于操作文本
        |--MediaWriter    用于操作媒体

    想要对操作的动作进行效率的提高,
    按照面向对象,可以通过继承对具体对象进行功能的扩展。
    效率提高需要加入缓冲技术
    Writer
        |--TextWriter        用于操作文本
            |--BufferTextWriter        加入了缓冲技术的操作文本的对象
        |--MediaWriter    用于操作媒体
            |--BufferMediaWriter        加入了缓冲技术的操作媒体的对象

    但是这样做并不理想。
    如果这个体系再次进行功能扩展,又多了流对象,这个流要提高效率,也要产生子类。
    这时,就会发现只为提高功能进行的继承,导致继承体系越来越臃肿,不够灵活。

    重新思考这个问题。
    既然加入的都是同一种技术——缓冲,
    前一种是让缓冲和具体的对象相结合。
    考虑将缓冲进行单独的封装:哪个对象需要缓冲,就将哪个对象和缓冲关联。

    class Buffer extends Writer{
        Buffer(TestWriter w)
        {}
        Buffer(MediaWriter m)
        {}
    }
                                        ↓
    class BufferWriter extends Writer{
        BufferWriter(Writer w)
        {}
    }

    Writer
        |--TextWriter        用于操作文本
        |--MediaWriter    用于操作媒体
        |--BufferWriter    用于提高效率
    发现装饰比继承更为灵活。
    特点:装饰类和被装饰类都必须所属于同一个接口或父类。

    LineNumberReader示例:

    package cn.itcast.p6.io.charstream.linenumber;
    
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.LineNumberReader;
    
    public class LineNumberReaderDemo {
    
        /**
         * @param args
         * @throws IOException 
         */
        public static void main(String[] args) throws IOException {
            
            FileReader fr=new FileReader("IO流_2.txt");
            LineNumberReader lnr=new LineNumberReader(fr);
            
            String line=null;
            lnr.setLineNumber(100);//设置行号从100开始
            while((line=lnr.readLine())!=null){
                System.out.println(line+":"+lnr.getLineNumber());
            }
            lnr.close();
        }
    
    }
    
  • 相关阅读:
    mysql 索引失效的几种情况
    MYSQL行转列-动态修改
    有A B C三列,用SQL语句实现:当A列大于B列时选择A列否则选择B列
    C++ 的关键字(保留字)完整介绍
    element-ui组件el-upload自定义上传时界面抖动
    React Hooks 组件卸载时触发的方法
    JS中的事件冒泡和事件捕获
    关于tranform: translate 后,元素被覆盖的问题
    Spring Boot使用@Scheduled时需要注意的坑
    redis的Lua脚本事务注意
  • 原文地址:https://www.cnblogs.com/chenchong/p/2631179.html
Copyright © 2020-2023  润新知