zoukankan      html  css  js  c++  java
  • 第27章 java I/O输入输出流

    java I/O输入输出流

    1.编码问题

    
    import java.io.UnsupportedEncodingException;
    
    /**
     * java涉及的编码
     */
    public class EncodeDemo {
        public static void main(String[] args) throws UnsupportedEncodingException {
            String s = "百度ABC";
            //字符串转化为byte,使用getBytes方法
            //转换字节序列用的是项目默认的编码utf-8
            byte[] byte1 = s.getBytes();
            for (byte b : byte1) {
                //把字节以16进制的方式显示
                //utf-8编码中文占用3个字节,英文占用1个字节
                System.out.print(Integer.toHexString(b & 0xff)+" ");//e7 99 be e5 ba a6 41 42 43
            }
    
            System.out.println();
            //显式的重新设置编码
            byte[] byte2 = s.getBytes("gbk");
            for (byte b : byte2){
                //gbk编码中文占用2个字节,英文占用1个字节
                System.out.print(Integer.toHexString(b & 0xff)+" ");//b0 d9 b6 c8 41 42 43
            }
    
    
            //java是双字节编码:utf-16be
            System.out.println();
            //显式的重新设置编码
            byte[] byte3 = s.getBytes("utf-16be");
            for (byte b : byte3){
                //utf-16be编码中文占用2个字节,英文占用2个字节
                System.out.print(Integer.toHexString(b & 0xff)+" ");//b0 d9 b6 c8 41 42 43
            }
    
            /**
             * 当你的字节序列是用某种编码的,这个时候想把字节序列变成字符串
             * 也需要用这种编码方式,否则会出现乱码
             */
            String str1 = new String(byte3);
            System.out.println(str1);//v~^� A B C
            //String可以指定编码格式
            String str2 = new String(byte3,"utf-16be");
            System.out.println(str2);//百度ABC
    
            /**
             * 文本文件 就是字节序列
             * 可以是任意编码的字节序列
             * 如果我们在中文机器上直接创建文本文件,那么该文本文件只认识ansi编码
             */
        }
    }
    

    2.File类的使用

    java.io.File类用于表示文件(目录)
    File类只用于表示文件(目录)的信息(名称,大小等),但不不能用于文件内容的访问

    2.1.File类常用API介绍

    import java.io.File;
    import java.io.IOException;
    
    /**
     * 测试File类的方法
     */
    public class FileDemo {
        public static void main(String[] args) throws IOException {
            //了解构造函数的情况
            //创建一个File实例,构造方法
            File  file = new File("/home/cenyu/test");
            File file1 = new File("/home/cenyu/note");
            File file2 = new File("/home/cenyu","note");
            //exists()检查文件是否存在
            System.out.println(file.exists());
            //mkdir()创建指定目录
            if (!file.exists()) {
                file.mkdir();
            }else {
                //delete删除路径目录
                file.delete();
            }
    
    
            //文件
            if (!file1.exists()){
                //创建一个文件
                file1.createNewFile();
            }else {
                //也可删除文件
                file1.delete();
            }
            //是否是一个目录
            System.out.println(file.isDirectory());
            //是否是一个文件
            System.out.println(file.isFile());
    
    
            //常用的File对象的API
            System.out.println(file);//file.toString()的内容,这里显示的是目录名
            //显示抽象文件路径名
            System.out.println(file.getAbsolutePath());
            //获取文件名字
            System.out.println(file.getName());
            //返回父目录路径
            System.out.println(file.getParent());
    
        }
    }
    
    

    2.1.File类方法

    遍历

    import java.io.File;
    
    /**
     * 列出File类的一些常用操作
     * 过滤,遍历等
     */
    public class FileUtils {
        /**
         * 列出指定目录下(包括其子目录)的所有文件
         */
        public static void listDirectory(File dir) throws IllegalAccessException {
            if (!dir.exists()){
                throw new IllegalAccessException("目录:"+dir+"不存在");
            }
            if (!dir.isDirectory()){
                throw  new IllegalAccessException(dir+"不是目录");
            }
            //此处只能遍历指定目录下的一层目录
        /*    String[] filnames = dir.list();//返回的是字符串数组,直接是子目录的名称,没有子目录下的内容
            for (String string : filnames){
                System.out.println(string);
            }
        */
    
        //如果需要遍历目录下的内容,就需要构造成File对象做递归操作,File提供了直接返回File对象的API
            //此处遍历指定目录下的所有层级的文件
            File[] files = dir.listFiles();//返回的是直接子目录(文件)的抽象
            if (files!=null && files.length>0){
                for (File file : files){
                    if (file.isDirectory()){
                        //递归
                        listDirectory(file);
                    }else {
                        System.out.println(file);
                    }
                }
            }
    
        }
    
        
        //测试
        public static void main(String[] args) throws IllegalAccessException {
            FileUtils.listDirectory(new File("/home/cenyu/"));
        }
    }
    
    

    3.RandomAccessFile的使用

    RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件

    RandomAccessFile支持随机访问文件,可以访问文件的任意位置
    java文件的一些知识
    1.java文件模型:

    在硬盘上的文件是byte byte byte存储的,是数据的集合

    2.打开文件:

    有两种模式"rw"(读写),“人”(只读),在创建RandomAccessFile实例的时候,需要指定使用什么方法来打开文件
    RandomAccessFile raf = new RandomAccessFile(file, "rw")
    文件中存在文件指针,在打开文件时指针在开头,pointer=0;在读和写的过程中,这个指针会乡下移动

    3.写方法

    raf.write(int)--->只写一个字节(后8位),这个字节是最后一个字节,所以如果要写好几个字节的字符,需要分别写进去,可以使用移位的方法,同时指针指向下一个位置,准备再次写入

    4.读方法

    int b = raf.read()--->读一个字节

    5.文件写完以后一定要关闭

    如果不关闭,会产生不可预料的问题

    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.util.Arrays;
    
    /**
     * RandomAccessFile类对文件的读写操作
     */
    public class RafDemo {
    
        public static void main(String[] args) throws IOException {
            //创建一个目录,如果目录不存在,就创建这个目录
            File demo = new File("demo");//如果不指定位置,就默认是当前目录下
            if (!demo.exists()){
                demo.mkdir();
            }
    
            //创在demo下的一个文件,如果文件不存在,就创建这个文件
            File file = new File(demo,"raf.dat");
            if (!file.exists()){
                file.createNewFile();
            }
    
            //
            RandomAccessFile raf = new RandomAccessFile(file,"rw");
            //指针位置
            System.out.println(raf.getFilePointer());//0
    
            raf.write('A');//只写了一个字节
            System.out.println(raf.getFilePointer());//1,已经写了一个字节
            raf.write('B');
            System.out.println(raf.getFilePointer());//2,已经写了两个字节
    
            int i = 0x7fffffff;
            //用write方法每次只能写一个字节,如果把i写进去就得写4次
            raf.write(i>>>24);//高8位
            raf.write(i>>>14);
            raf.write(i>>>8);
            raf.write(i);//低8位
            System.out.println(raf.getFilePointer());//6
    
    
            //可以直接使用writInt方法,一次性把找个int写进去
            raf.writeInt(i);//其实底层调用的还是上面的移位方法
    
            //写入一个字符
            String s = "中";
            byte[] gbk = s.getBytes("gbk");
            raf.write(gbk);
            System.out.println(raf.getFilePointer());//12
            System.out.println(raf.length());//12
    
    
            //读文件,必须把指针移到头部
            raf.seek(0);//设置文件指针位置
            //一次性读取,把文件中的内容都读到字节数组中
            byte[] buf = new byte[(int)raf.length()];
            raf.read(buf);//不加buf参数的话,一次只能读取一个
            System.out.println(Arrays.toString(buf));//[65, 66, 127, -1, -1, -1, 127, -1, -1, -1, -42, -48]
    
            String s1 = new String(buf);
            //此处乱码的原因是我们的s使用的是gdk编码格式,跟其他内容的格式是不一样。
            System.out.println(s1);//AB��������
        }
    }
    
    

    4.字节流的使用

    IO流作为内容的传输作用,分为输入流和输出流,输入流就是程序获取了信息,输出流就是程序向外传送了信息
    传输方式一般有字节流和字符流
    1.字节流
    1.有两个抽象的父类,分别是:
    InputStream:抽象了应用程序读取(获得)数据的方法
    OutputStream:抽象了应用程序写出(传出)数据的方法
    2.数据读取到结尾的时候就是EOF=End,也是读到-1.这两个就代表读到了结尾
    3.输入流基本方法:
    int b = in.read();读取一个字节(byte类型),无符号填充到int低八位,-1是EOF
    in.read(byte[] buf)读取数据填充到字节数组buf
    in.read(byte[] buf, int start, int size)读取数据到字节数组buf,从buf的start位置开始存放size长度的数据
    4.输出流基本方法
    out.read(int b)写出一个byte到流,写的是b的低8位,而int是4个字节的
    out.read(byte[] buf)将buf字节数组都写入到流
    out.read(byte[] buf, int start, int size)字节数组buf从start位置开始写size长度的字节到流
    5.FileInputStream--->集成InputStream,具体实现了文件上的读取数据

    注意:读取的时候单字节读取不适合读取大文件,大文件可以使用批量读取,效率更高
    使用输入流读取数据实例:

    import java.io.FileInputStream;
    
    import java.io.IOException;
    
    /**
     * 读取文件内容的两种不同方法
     */
    public class IOUtil {
        /**
         * 读取指定文件内容,按照16进制输出到控制台
         * 并且每输出10个byte换行
         * @param fileName
         */
        //单字节读取
        public static void printHex(String fileName) throws IOException {
            //把文件作为字节流操作
            FileInputStream in = new FileInputStream(fileName);
            int b;
            int i =1;
            while ((b=in.read())!=-1){
                if (b<0xf){
                    //如果是一位,单位数前面补0
                    System.out.print("0");
                }
                System.out.print(Integer.toHexString(b)+"  ");
                if (i++%10==0){
                    System.out.println();
                }
            }
            in.close();
        }
    
        //批量读取
        public static void printHexByByteArray(String fileName) throws IOException {
            FileInputStream in = new FileInputStream(fileName);
            byte[] buf = new byte[20*1024];
            /**
             * 从in中批量读取字节,放入到buf这个字节数组中,
             * 从第0个位置开始放,最多放buf.length个
             * 返回的是多的字节的个数
             */
    /*        int bytes = in.read(buf, 0, buf.length);//一次性读完,此要求字节数组足够大
            int j=1;
            for (int i = 0; i < bytes; i++) {
                if (buf[i]<=0xf){
                    System.out.print("0");
                }
                System.out.print(Integer.toHexString(buf[i])+"  ");
                if (j++%10==0){
                    System.out.println();
                }
            }
            in.close();
    */
    //上面的方法需要字节数组足够大,如果文件草果字节数组比较麻烦,所以一般更推荐使用下面的方法
            int bytes = 0;
            int j = 1;
    
            while ((bytes = in.read(buf, 0, buf.length))!=-1){
                for (int i = 0; i < bytes; i++) {
                    if (buf[i]<=0xf){
                    System.out.print("0");
                    }
                    //byte类型8位,int类型32位,为了避免数据转换错误,通过&0xff将高24位清零
                    System.out.print(Integer.toHexString(buf[i] & 0xff)+"  ");
                    if (j++%10==0){
                        System.out.println();
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            try {
                IOUtil.printHex("/home/cenyu/aa");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    
            try {
                IOUtil.printHexByByteArray("/home/cenyu/aa");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    6.FileOutStream--->继承OutputStream 实现了向文件中写入byte数据的方法

    mport java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * 测试FileOutputStream类写入文件
     */
    public class FileOutDemo {
        public static void main(String[] args) throws IOException {
            //如果该文件不存在,则直接创建该文件。如果文件存在,要看后面有没有true参数,
            //如果没有true参数,则删除文件后再重新创建,如果有true参数,则在文件后追加内容
            FileOutputStream out = new FileOutputStream("demo/out.dat",true);
            out.write('A');//写出了'A'的低八位
            out.write('B');//写出了'B'的低八位
            int a =10;//write只能写八位,那么写一个int整数,需要写4此,每次8位
            out.write(a>>>24);
            out.write(a>>>16);
            out.write(a>>>8);
            out.write(a);
            //写字符串
            byte[] gdk = "中国".getBytes("gbk");
            out.write(gdk);
            out.close();
    
    
            //测试copyFile
            FileOutDemo.copyFile(new File("/home/cenyu/aa"), new File("/home/cenyu/bb"));
        }
    
    
        //copy源文件
        public static void copyFile(File srcFile, File destFile) throws IOException{
            if (!srcFile.exists()){
                throw new IllegalArgumentException("文件:"+srcFile+"不存在");
            }
            if (!srcFile.isFile()){
                throw new IllegalArgumentException(srcFile+"不是文件");
            }
            FileInputStream in = new FileInputStream(srcFile);
            FileOutputStream out = new FileOutputStream(destFile);
            byte[] buf = new byte[8*1024];
            int b;
            while ((b=in.read(buf, 0, buf.length))!=-1){
                out.write(buf, 0, b);
                out.flush();
            }
            in.close();
            out.close();
        }
    }
    
    

    7.DataOutputStream/DataInputStream类是对“流”功能的扩展,可以更加方便读取int,long,字符等类型数据
    DataOutputStream:
    writeInt()/writeDouble()/writeUTF()

    写入代码示例:

    public class DosDemo {
        public static void main(String[] args) throws IOException {
            String file = "demo/dos.dat";
            DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
            dos.writeInt(10);
            dos.writeInt(-10);
            dos.writeLong(10L);
            dos.writeDouble(10.5);
            //采用utf-8编码写出
            dos.writeUTF("中国");
            //采用utf-16编码写出
            dos.writeChars("中国");
            dos.close();
            IOUtil.printHex(file);
        }
    }
    
    

    读取代码示例:

    public class DisDemo {
        public static void main(String[] args) throws IOException {
            String file = "demo/dos.dat";
            DataInputStream dis = new DataInputStream(new FileInputStream(file));
            int i = dis.readInt();
            System.out.println(i);
            i = dis.readInt();
            System.out.println(i);//10
            long l = dis.readLong();
            System.out.println(l);//-10
            double d = dis.readDouble();//10
            System.out.println(d);//10.5
            String  s = dis.readUTF();
            System.out.println(s);//中国
    
            dis.close();
        }
    }
    

    8.BufferedInputStream / BufferedOutputStream
    这两个流类为IO提供了缓冲区的操作,一般打开文件进行写入或者读取操作,都会加上缓冲,这种流模式提高了IO的性能
    从应用程序中把输入放入文件,就相当于将一缸水倒入另一个缸中:
    FileOutputStream--->write()方法相当于一滴一滴把水“转移”过去
    DataOutputStream--->writeXXX()方法会更方便一些,相当于一瓢一瓢把水“转移”过去
    BufferedOutputStream--->write()方法更方便,相当于一瓢一瓢把水先放入桶中,再从桶中倒入到另一个缸里。

        //进行文件的拷贝,利用带缓冲的字节流
        public static void copyFileByBuffer(File srcFile, File destFile) throws IOException {
            if (!srcFile.exists()){
                throw new IllegalArgumentException("文件:"+srcFile+"不存在");
            }
            if (!srcFile.isFile()){
                throw new IllegalArgumentException(srcFile+"不是文件");
            }
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
            int c;
            while ((c = bis.read())!=-1){
                bos.write(c);
                bos.flush();//刷新缓冲区
            }
            bis.close();
            bos.close();
    
        }
    

    5.字符流的使用

    1.编码的问题上面讲过了
    2.认识文本和文本文件

    java中的文本(Char)是一个16位无符号整数,是字符unicode(双字节)编码。就是说java中的文本就是char,而这里的char表示unicode中的所有能表示的单个字符,注意必须是单个的。而在java的语法中,定义一个char的时候必须用单引号来引起来,不能用双引号
    文件是byte byte byte ……的数字序列
    文本文件是文本(char)序列按照某种编码方法(utf-8, utf-16be, gbk)序列化为byte的存储结果。

    3.字符流也分为输出流(Write)抽象类和输入流(Read)抽象类,都是操作文本文件,文本文件是把文本(char)按照一定的编码序列化成byte

    字符的处理,一次处理一个字符
    字符的底层依然是基本的字节序列

    1.字符流的基本实现:
    InputStreamReader 完成byte流解析为char流,按照编码解析
    OutputStreamReader 提供char流到byte流,按照编码处理
    此类主要对文件内的文本进行操作

    
    /**
     * 字符流的读(InputStreamReader)和写操作(OutputStreamReader)
     */
    public class IsrAndOswDemo {
        public static void main(String[] args) throws IOException {
            FileInputStream in = new FileInputStream("demo/dos.dat");
            InputStreamReader isr = new InputStreamReader(in, "utf-8");//utf-8是当前调用文件的默认编码,如果以后遇到不是这个编码的,要在这里修改
    
            //单个读取
            int c;
            while ((c=isr.read())!=-1){
                //char类型是一个16位的无符号整数,代表一个单个的字符
                System.out.print((char) c);//强制转换为char来打印,结果就是字符
            }
    
    
            //按照char[]数组大小批量读取
            char[] buffer = new char[8*1024];
            int c1;
            //批量读取,放入buffer这个字符数组,从第0个位置开始放置,最多放buffer.length个字符
            while ((c1 = isr.read(buffer,0,buffer.length))!=-1){
                String s = new String(buffer,0,c1);
                System.out.print(s);
            }
    
            //写操作
            FileOutputStream out = new FileOutputStream("demo/osw.dat");
            OutputStreamWriter osw = new OutputStreamWriter(out, "utf-8");//调用文件的编码方式
            osw.write("aaa11");
            osw.flush();
    
            isr.close();
            osw.close();
        }
    }
    

    字符流的第二种操作方式FileReader和FileWriter
    这一类重要是对文件进行操作

    /**
     * 字符流的第二种操作方式FileReader和FileWriter
     */
    public class FrAndFwDemo {
        public static void main(String[] args) throws IOException {
            //实例化,不可指定编码格式
            FileReader fr = new FileReader("demo/dos.dat");
            //有true是对文件追加内容,没有true,则直接覆盖
            FileWriter fw = new FileWriter("demo/osw.dat",true);
            char[] buffer = new char[2056];
            int c;
            while ((c = fr.read(buffer, 0, buffer.length))!=-1) {
                fw.write(buffer, 0, c);
                fw.flush();
            }
            fr.close();
            fw.close();
        }
    }
    

    字符流的过滤器
    BufferReader --->readLine 一次读一行
    BufferWriter/PrintWriter --->写一行

    /**
     * 字符流的过滤器
     */
    public class BrAndBwPwDemo {
        public static void main(String[] args) throws IOException {
            //对文件进行读写操作
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("demo/dos.dat")));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demo/out.dat")));
    
            String line;
            while ((line=br.readLine())!=null){
                System.out.println(line);//一次读一行,并不能识别换行符
                bw.write(line);//一次写一行,但是不会自动换行
                bw.newLine();//换行操作
                bw.flush();
            }
            br.close();
            bw.close();
            
        }
    }
    
    

    6.对象的序列化和反序列化

    对象序列化:就是将Object转换成byte序列,反之叫对象的反序列化
    序列化流(ObjectOutputStream),是过滤流---witeObject
    反序列化流(ObjectInputStream)--->read Object

    对象要进行序列化有一个接口(Serializable)
    对象必须实现序列化接口,才能进行序列化,否则将出现异常,这个接口,没有任何方法,只是一个标准
    序列号化和反序列号的操作
    创建一个Student类,并实现Serializable接口

    transient关键字
    transient关键字限定这个元素不会进行JVM的序列号操作。但是并不是不能再对这个元素进行序列化,而是可以通过自己来做序列化

    public class Student implements Serializable{
        private String stuno;
        private String stuname;
        private transient int stuage;//transient关键字限定这个元素不会进行JVM的序列号操作
    
        public Student(String stuno, String stuname, int stuage) {
            super();
            this.stuno = stuno;
            this.stuname = stuname;
            this.stuage = stuage;
        }
    
        public void setStuno(String stuno) {
            this.stuno = stuno;
        }
    
        public String getStuno() {
            return stuno;
        }
    
        public void setStuname(String stuname) {
            this.stuname = stuname;
        }
    
        public String getStuname() {
            return stuname;
        }
    
        public void setStuage(int stuage) {
            this.stuage = stuage;
        }
    
        public int getStuage() {
            return stuage;
        }
    
        @Override
        public String toString() {
            return "Student[stuno="+stuno+", stuname="+stuname+",stuage="+stuage+"]";
        }
    }
    
    
    

    序列化和反序列化操作

    
    public class ObjectSeriaDemo1 {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            String file = "demo/obj11.dat";
            //1.对象序列化
            /*
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
            Student stu = new Student("10001","张三", 20);
            oos.writeObject(stu);
            oos.flush();
            oos.close();
            */
    
            //2.反序列化操作
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            Student stu = (Student)ois.readObject();
            System.out.println(stu);
            ois.close();
        }
    }
    
  • 相关阅读:
    【bzoj 1143】[CTSC2008]祭祀river
    【SRM-09 B】撕书II
    【刷题记录】BZOJ-USACO
    【noip 2014】提高组Day2T3.华容道
    【noip 2012】提高组Day2T3.疫情控制
    【noip 2012】提高组Day1T3.开车旅行
    点分治
    2—SAT问题
    生成树
    莫比乌斯反演
  • 原文地址:https://www.cnblogs.com/cenyu/p/6149889.html
Copyright © 2011-2022 走看看