• java学习笔记16--I/O流和文件


    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note16.html,转载请注明源地址。

    IO(Input  Output)流

    IO流用来处理设备之间的数据传输,对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中

    输入/输出流可以从以下几个方面进行分类

    从流的方向划分:

    输入流、输出流

    从流的分工划分:

    节点流、处理流

    从流的内容划分:

    面向字符的流、面向字节的流

    字符流和字节流

    字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:

    • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

    • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

    结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

    流按流向分为:输入流、输出流

    IO流常用基类

    字节流的抽象基类:

    •InputStream,OutputStream。

    字符流的抽象基类:

    •Reader, Writer。

    注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

    如:InputStream的子类FileInputStream。

    如:Reader的子类FileReader。

    Java流操作有关的类或接口:

    Java流类图结构:

    读写文本文件

     写文本文件

    例:在C盘根目录创建文本文件Hello.txt,并往里写入若干行文本
    import java.io.*;   
    class Ex1{
      public static void main ( String[] args ) throws IOException {  
         //main方法中声明抛出IO异常
         String fileName = "C:\Hello.txt"; 
         FileWriter writer = new FileWriter( fileName );   
         writer.write( "Hello!
    "); 
         writer.write( "This is my first text file,
    "  );  
         writer.write( "You can see how this is done.
    " ); 
         writer.write("输入一行中文也可以
    ");
         writer.close(); 
      }
    }

    说明:

    每次运行这个程序,都将删除已经存在的”Hello.txt”文件,创建一个新的同名文件。FileWriter的构造方法有五个,本例是通过一个字符串指定文件名来创建。FileWriter类的write方法向文件中写入字符

    Writer类的流可实现内部格式到外部磁盘文件格式的转换

    “Hello.txt”是一个普通的ASCII码文本文件,每个英文字符占一个字节,中文字符占两个字节

    Java程序中的字符串则是每个字符占两个字节的,采用Unicode编码

    close方法清空流里的内容并关闭它。如果不调用该方法,可能系统还没有完成所有数据的写操作,程序就结束了

    在看一个例子:处理IO异常

    import java.io.*;   
    class Ex2 {
      public static void main ( String[] args ) {
        String fileName = "c:\Hello.txt" ;
        try {  //将所有IO操作放入try块中
             FileWriter writer = new FileWriter( fileName ,true );
             writer.write( "Hello!
    ");  
             writer.write( "This is my first text file,
    "  );  
             writer.write( "You can see how this is done. 
    " );
             writer.write("输入一行中文也可以
    ");
             writer.close();
        }
        catch ( IOException iox) { 
             System.out.println("Problem writing" + fileName ); }
        }
    }

    说明:

    运行此程序,会发现在原文件内容后面又追加了重复的内容,这就是将构造方法的第二个参数设为true的效果

    如果将文件属性改为只读属性,再运行本程序,就会出现IO错误,程序将转入catch块中,给出出错信息

    BufferedWriter类

    如果需要写入的内容很多,就应该使用更为高效的缓冲器流类BufferedWriter

    FileWriter和BufferedWriter类都用于输出字符流,包含的方法几乎完全一样,但BufferedWriter多提供了一个newLine()方法用于换行

    使用BufferedWriter完成上面的功能:

    import java.io.*; 
    class Ex3 {
        public static void main ( String[] args ) throws IOException    {
            String fileName = "C:/newHello.txt" ;
            BufferedWriter out = new BufferedWriter( 
                                      new  FileWriter( fileName ) );
           out.write( "Hello!"  );
                 out.newLine() ; 
              out.write( "This is another text file using BufferedWriter,"  );   
            out.newLine(); ;
            out.write( "So I can use a common way to start a newline" ); 
            out.close();
        }
    }

    读文本文件

    FileReader类

    从文本文件中读取字符

    继承自Reader抽象类的子类InputStreamReader

    BufferedReader

    读文本文件的缓冲器类

    具有readLine()方法,可以对换行符进行鉴别,一行一行地读取输入流中的内容

    继承自Reader

    文件输入方法:

    BufferedReader in = new BufferedReader(new FileReader( fileName) );

    从Hello.txt中读取文本并显示在屏幕上

    import java.io.*;
    class Ex4 {
        public static void main ( String[] args ) {
            String fileName = "C:/Hello.txt" , line;
            try {
                 BufferedReader in = new BufferedReader(
                                                  new FileReader( fileName  ) );
                 line = in.readLine();   //读取一行内容
                 while ( line != null ) { 
                    System.out.println( line );
                    line = in.readLine();
                 }
                 in.close(); 
            }
           catch ( IOException iox ) { 
                 System.out.println("Problem reading " + fileName );  
            }
        }
    }

    运行该程序,屏幕上将逐行显示出Hello.txt文件中的内容

    FileReader对象:创建后将打开文件,如果文件不存在,会抛出一个IOException

    BufferedReader类的readLine()方法:从一个面向字符的输入流中读取一行文本。如果其中不再有数据,返回null

    Reader类的read()方法:也可用来判别文件结束。该方法返回的一个表示某个字符的int型整数,如果读到文件末尾,返回 -1。据此,可修改本例中的读文件部分:

    int c;
    while((c=in.read())!= -1)  System.out.print((char)c);

    close()方法:为了操作系统可以更为有效地利用有限的资源,应该在读取完毕后,调用该方法

    指定源文件和目标文件名,将源文件的内容拷贝至目标文件。调用方式为:

    java copy sourceFile  destinationFile

    class CopyMaker {
        String sourceName, destName;
        BufferedReader source;
        BufferedWriter dest;
        String line;
        private boolean openFiles() {
            try {
                source = new BufferedReader(new FileReader(sourceName));
            } catch (IOException ex) {
                System.out.println("Problem opening " + sourceName);
                return false;
            }
            try {
                dest = new BufferedWriter(new FileWriter(destName));
            } catch (IOException ex) {
                System.out.println("Problem opening " + destName);
                return false;
            }
            return true;
        }
        private boolean copyFiles() {
            try {
                line = source.readLine();
                while(line != null) {
                    dest.write(line);
                    dest.newLine();
                    line = source.readLine();
                }
            } catch (IOException ex) {
                System.out.println("Problem reading or writing");
                return false;
            }
            return true;
        }
        private boolean closeFiles() {
            boolean retVal = true;
            try {
                source.close();
            } catch (IOException ex) {
                System.out.println("Prolem closing " + sourceName);
                retVal = false;
            }
            try {
                dest.close();
            } catch (IOException ex) {
                System.out.println("Problem closing " + destName);
                retVal = false;
            }
            return retVal;
        }
        public boolean copy(String src, String dst) {
            sourceName= src;
            destName = dst;
            return openFiles() && copyFiles() && closeFiles();
        }
    }
    
    public class CopyFile {
        public static void main(String[] args) {
            if(args.length == 2)
                new CopyMaker().copy(args[0], args[1]);
            else
                System.out.println("Please Enter names");
        }
    }

    读写二进制文件

    二进制文件

    原则上讲,所有文件都是由8位的字节组成的

    如果文件字节中的内容应被解释为字符,则文件被称为文本文件;如果被解释为其它含义,则文件被称为二进制文件

    例如文字处理程序,例如字处理软件Word产生的doc文件中,数据要被解释为字体、格式、图形和其他非字符信息。因此,这样的文件是二进制文件,不能用Reader流正确读取

    为什么需要二进制文件?

    输入输出更快

    比文本文件小很多

    有些数据不容易被表示为字符

    抽象类OutputStream

    派生类FileOutputStream

    用于一般目的输出(非字符输出)

    用于成组字节输出

    派生类DataOutputStream

    具有写各种基本数据类型的方法

    将数据写到另一个输出流

    它在所有的计算机平台上使用同样的数据格式

    其常用的一些方法见下表

    例:将三个int型数字255/0/-1写入数据文件data1.dat

    public class ext6_7 {
        public static void main(String[] args) {
            String fileName = "c:/data1.dat";
            int value0 = 255, value1 = 0, value2 = -1;
            try {
                DataOutputStream out = new DataOutputStream(
                        new FileOutputStream(fileName));
                out.writeInt(value0);
                out.writeInt(value1);
                out.writeInt(value2);
                out.close();
            } catch (IOException ex) {
                System.out.println("Problem writing " + fileName);
            }
        }
    }

    说明:

    FileOutputStream类的构造方法负责打开文件“data1.dat”用于写数据

    FileOutputStream类的对象与DataOutputStream对象连接,写基本类型的数据

    BufferedOutputStream

    写二进制文件的缓冲流类

    类似于文本文件中的BufferedWriter

    对于大量数据的写入,可提高效率

    用法示例:

    DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream( fileName ) ) ); 

    例:向文件中写入各种数据类型的数,并统计写入的字节数

    public class ex6_8 {
        public static void main(String[] args) throws IOException {
            String fileName = "c:/mixedTypes.dat";
            DataOutputStream dataOut = new DataOutputStream(
                    new BufferedOutputStream(
                            new FileOutputStream(fileName)));
            dataOut.writeInt(0);
            System.out.println(dataOut.size() + "bytes have been written.");
            dataOut.writeDouble(31.2);
            System.out.println(dataOut.size() + "bytes have been written.");
            dataOut.writeBytes("java");
            System.out.println(dataOut.size() + "bytes have been written.");
            dataOut.close();
        }
    }

    读二进制文件

    过滤流

    读或写的同时对数据进行处理

    通过另外一个流来构造一个过滤流

    大部分java.io 包所提供过滤流都是FilterInputStream和FilterOutputStream的子类:

    DataInputStream 和 DataOutputStream

    BufferedInputStream 和 BufferedOutputStream

    LineNumberInputStream

    PushbackInputStream

    PrintStream

    读取上面的例子创建的数据文件中的3个int型数字,显示相加结果

    public class ex6_10 {
        public static void main(String[] args) {
            String fileName = "C:\data1.dat";
            int sum = 0;
            try {
                DataInputStream instr = new DataInputStream(
                        new BufferedInputStream(new FileInputStream(fileName)));
                sum += instr.readInt();
                sum += instr.readInt();
                sum += instr.readInt();
                System.out.println("The sum is: " + sum);
                instr.close();
            } catch (IOException ex) {
                System.out.println("Problem reading " + fileName);
            }
        }
    }

    分析:

    readInt方法可以从输入流中读入4个字节并将其当作int型数据

    由于知道文件中存储的是3个int型数据,所以使用了3个读入语句

    如果不知道数据的个数该怎么办呢?因为DataInputStream的读入操作如遇到文件结尾就会抛出EOFException异常,所以我们可以将读操作放入try块中

    将读操作放入try块中,使遇到文件结尾就会抛出EOFException异常,进入到相应的catch块中

    try
    {
       while(true) sum += instr.readInt();
    }
    catch ( EOFException  eof )
    {
       System.out.println("The sum is: " + sum);
       instr.close();
    }

    File类

    表示磁盘文件信息

    定义了一些与平台无关的方法来操纵文件

    –创建、删除文件

    –重命名文件

    –判断文件的读写权限及是否存在

    –设置和查询文件的最近修改时间等

    构造文件流可以使用File类的对象作为参数

    File类常用方法:

    例:在C盘创建文件Hello.txt,如果存在则删除旧文件,不存在则直接创建新的

    public class ex6_13 {
        public static void main(String[] args) {
            File f = new  File("C:" + File.separator + "hello.txt");
            if(f.exists()) {
                f.delete();
            } else {
                try {
                    f.createNewFile();
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        }
    }

    处理压缩文件

    压缩流类

    –java.util.zip包中提供了一些类,使我们可以以压缩格式对流进行读写

    –它们都继承自字节流类OutputStream和InputStream

    –其中GZIPOutputStream和ZipOutputStream可分别把数据压缩成GZIP格式和Zip格式

    –GZIPInputStream和ZipInputStream可以分别把压缩成GZIP格式或Zip的数据解压缩恢复原状

    public class ex6_14 {
        public static void main(String[] args) throws IOException{
            FileInputStream in = new FileInputStream("c:/Hello.txt");
            GZIPOutputStream out = new GZIPOutputStream(    //生成压缩文件test.gz
                    new FileOutputStream("c:/test.gz"));
            System.out.println("Writing compressing file from" +
                    "c:/Hello.txt to c:/test.gz");
            int c;
            while((c = in.read()) != -1) {
                out.write(c);
            }
            in.close();
            out.close();
            System.out.println("Reading file form c:/test.gz to monitor");
            BufferedReader in2 = new BufferedReader(
                    new InputStreamReader(
                            new GZIPInputStream(
                                    new FileInputStream("c:/test.gz"))));
            String s;
            while((s = in2.readLine()) != null) System.out.println(s);
            in2.close();
            System.out.println("Writing decompression to c:/newHello.txt");
            GZIPInputStream in3 = new GZIPInputStream(    //读取test.gz中的内容
                    new FileInputStream("c:/test.gz"));
            FileOutputStream out2 = new FileOutputStream("c:/newHello.txt");
            while((c = in3.read()) != -1) out2.write(c);
            in3.close();
            out2.close();
        }
    }

    Zip文件

    –可能含有多个文件,所以有多个入口(Entry)

    –每个入口用一个ZipEntity对象表示,该对象的getName()方法返回文件的最初名称

    ZipOutputStream

    –父类是DeflaterOutputStream

    –可以把数据压缩成ZIP格式

    ZipInputStream

    –父类是InflaterInputStream

    –可以把压缩成ZIP格式的数据解压缩

    例:指定若干文件名,将所有文件压缩为"c:/test.zip",再从此压缩文件中解压缩并显示

    public class ex6_15 {
        public static void main(String[] args) throws IOException {
            ZipOutputStream out = new ZipOutputStream(
                    new BufferedOutputStream(
                            new FileOutputStream("c:/test.zip")));
            String[] s = {"c:/t1.txt", "c:/t2.txt", "c:/t3.txt"};  //文件路径
            for(int i = 0; i < s.length; i++) {
                System.out.println("Writing file" + s[i]);
                BufferedInputStream in = new BufferedInputStream(
                        new FileInputStream(s[i]));
                out.putNextEntry(new ZipEntry(s[i]));
                int c;
                while((c = in.read()) != -1) out.write(c);
                in.close();
            }
            out.close();
            System.out.println("Reading file");
            ZipInputStream in2 = new ZipInputStream(
                    new BufferedInputStream(
                            new FileInputStream("c:/test.zip")));
            ZipEntry ze;
            while((ze = in2.getNextEntry()) != null) {
                System.out.println("Reading file " + ze.getName());
                int x;
                while((x = in2.read()) != -1) System.out.write(x);
                System.out.println();
            }
            in2.close();
        }
    }

    再看一个例子:解压缩Zip文件,并恢复其原来路径

    class Unzip {
        byte[] doc = null;    ;   //存储解压缩数据的缓冲字节数组
        String FileName = null;    //压缩文件名字符串
        String UnZipPath = null;    //解压缩路径字符串 
        public Unzip(String filename, String unZipPath) {
            this.FileName = filename;
            this.UnZipPath = unZipPath;
            this.setUnZipPath(this.UnZipPath);
        }
        public Unzip(String filename) {
            this.FileName = new String(filename);
            this.UnZipPath = null;
            this.setUnZipPath(this.UnZipPath);
        }
        private void setUnZipPath(String unZipPath) {
            if(unZipPath.endsWith("\"))
                this.UnZipPath = new String(unZipPath);
            else
                this.UnZipPath = new String(unZipPath + "\");
        }
        public void doUnZip() {
            try {
                ZipInputStream zipis = new ZipInputStream(
                        new FileInputStream(FileName));
                ZipEntry fEntry = null;
                while((fEntry = zipis.getNextEntry()) != null) {
                    if(fEntry.isDirectory()) {
                        checkFilePath(UnZipPath + fEntry.getName());
                    } else {    //是文件则解压缩文件
                        String fname = new String(UnZipPath + fEntry.getName());
                        try {
                            FileOutputStream out = new FileOutputStream(fname);
                            doc = new byte[512];
                            int n;
                            while((n = zipis.read(doc, 0, 512)) != -1) {
                                out.write(doc, 0, n);
                            }
                            out.close();
                            out = null;
                            doc = null;
                        } catch (Exception ex) {
                            
                        }    
                    }
                }
                zipis.close();    //关闭输入流
            } catch (IOException ioe) {
                System.out.println(ioe);
            }
        }
        private void checkFilePath(String dirName) {
            File dir = new File(dirName);
            if(!dir.exists())
                dir.mkdirs();
        }
    }
    public class ex6_16 {
        public static void main(String[] args) {
            String zipFile = "c:/test.zip";
            String unZipPath = "";
            Unzip myZip = new Unzip(zipFile, unZipPath);
            myZip.doUnZip();
        }
    }

    对象序列化

    保存对象的信息,在需要的时候,再读取这个对象

    内存中的对象在程序结束时就会被垃圾回收机制清除

    用于对象信息存储和读取的输入输出流类:

    ObjectInputStream、ObjectOutputStream

    实现对象的读写

    通过ObjectOutputStream把对象写入磁盘文件

    通过ObjectInputStream把对象读入程序

    –不保存对象的transient和static类型的变量

    –对象要想实现序列化,其所属的类必须实现Serializable接口

    必须通过另一个流构造ObjectOutputStream:

    FileOutputStream out = new FileOutputStream("theTime");                   
    ObjectOutputStream s =  new ObjectOutputStream(out);
    s.writeObject("Today");
    s.writeObject(new Date());
    s.flush();

    必须通过另一个流构造ObjectInputStream:

    FileInputStream in = new FileInputStream("theTime");
    ObjectInputStream s = new ObjectInputStream(in);
    String today = (String)s.readObject();
    Date date = (Date)s.readObject();

    空接口,使类的对象可实现序列化

    Serializable 接口的定义:

    package java.io;
    public interface Serializable {
        // there's nothing in here!
    };

    实现Serializable接口的语句

    public class MyClass implements Serializable {
        ...
    }

    使用关键字transient可以阻止对象的某些成员被自动写入文件

    看一个例子:

    创建一个书籍对象,并把它输出到一个文件book.dat中,然后再把该对象读出来,在屏幕上显示对象信息

    class Book implements Serializable {
        int id;
        String name;
        String author;
        float price;
        public Book(int id, String name, String author, float price) {
            this.id = id;
            this.name = name;
            this.author = author;
            this.price = price;
        }
    }
    public class ex6_17 {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            Book book = new Book(100000, "java programming", "Wu", 23);
            ObjectOutputStream oos = new ObjectOutputStream(
                    new FileOutputStream("c:/book.dat"));
            oos.writeObject(book);
            oos.close();
            System.out.println("ID is: " + book.id);
            System.out.println("name is: " + book.name);
            System.out.println("author is: " + book.author);
            System.out.println("price is: " + book.price);
        }
    }

    Externalizable 接口

    –实现该接口可以控制对象的读写

    –API中的说明为

    public interface Externalizable extends Serializable

    –其中有两个方法writeExternal()和readExternal(),因此实现该接口的类必须实现这两个方法

    –ObjectOutputStream的writeObject()方法只写入对象的标识,然后调用对象所属类的writeExternal()

    –ObjectInputStream的readObject()方法调用对象所属类的readExternal()

    随机文件读写

    RandomAccessFile类

    –可跳转到文件的任意位置读/写数据

    –可在随机文件中插入数据,而不破坏该文件的其他数据

    –实现了DataInput 和 DataOutput 接口,可使用普通的读写方法

    –有个位置指示器,指向当前读写处的位置。刚打开文件时,文件指示器指向文件的开头处。对文件指针显式操作的方法有:

    int skipBytes(int n):把文件指针向前移动指定的n个字节

    void seek(long):移动文件指针到指定的位置。

    long getFilePointer():得到当前的文件指针。

    –在等长记录格式文件的随机读取时有很大的优势,但仅限于操作文件,不能访问其它IO设备,如网络、内存映像等

    –可用来实现读和写,构造方法包括
    public RandomAccessFile(File file,String mode) throws FileNotFoundException
    public RandomAccessFile(String name, String mode) throws FileNotFoundException
    –建立一个RandomAccessFile时,要指出你要执行的操作:仅从文件读,还是同时读写
    new RandomAccessFile("farrago.txt", "r");
    new RandomAccessFile("farrago.txt", "rw");

    RandomAccessFile类常用API

    例:创建一个雇员类,包括姓名、年龄。姓名不超过8个字符,年龄是int类型。每条记录固定为20个字节。使用RandomAccessFile向文件添加、修改、读取雇员信息

    class Employee {
        char name[] = {'u0000', 'u0000','u0000', 'u0000',
                'u0000', 'u0000', 'u0000', 'u0000'};
        int age;
        public Employee(String name, int age) throws Exception {
            if(name.toCharArray().length > 8)
                System.arraycopy(name.toCharArray(), 0, this.name, 0, 8);
            else
                System.arraycopy(name.toCharArray(), 0, this.name, 0, name.toCharArray().length);
            this.age = age;
        }
    }
    
    public class ex6_18 {
        String FileName;
        public ex6_18(String FileName) {
            this.FileName = FileName;
        }
        public void writeEmployee(Employee e, int n) throws Exception {
            RandomAccessFile ra = new RandomAccessFile(FileName, "rw");
            ra.seek(n * 20);    //将位置指示器移到指定位置上
            for(int i = 0; i < 8; i++) ra.writeChar(e.name[i]);
            ra.writeInt(e.age);
            ra.close();
        }
        public void readEmployee(int n) throws Exception {
            char buf[] = new char[8];
            RandomAccessFile ra = new RandomAccessFile(FileName, "r");
            ra.seek(n * 20);
            for(int i = 0; i < 8; i++) buf[i] = ra.readChar();
            System.out.print("name: ");
            System.out.println(buf);
            System.out.println("age: " + ra.readInt());
            ra.close();
        }
        public static void main(String[] args) throws Exception {
            ex6_18 t = new ex6_18("c:/temp.txt");
            Employee e1 = new Employee("zhangsan", 22);
            Employee e2 = new Employee("lisi", 20);
            Employee e3 = new Employee("wangwu", 25);
            t.writeEmployee(e1, 0);
            t.writeEmployee(e3, 2);
            System.out.println("第1个雇员的信息");
            t.readEmployee(0);
            System.out.println("第3个雇员的信息");
            t.readEmployee(2);
            System.out.println("第2个雇员的信息");
            t.readEmployee(1);
        }
    }

    您还可能感兴趣:

    java学习笔记系列:

    java学习笔记15--多线程编程基础2

    java学习笔记14--多线程编程基础1 

    java学习笔记13--反射机制与动态代理

    java学习笔记12--异常处理

    java学习笔记11--集合总结

    java学习笔记10--泛型总结 

    java学习笔记9--内部类总结

    java学习笔记8--接口总结

    java学习笔记7--抽象类与抽象方法

    java学习笔记6--类的继承、Object类

    java学习笔记5--类的方法 

    java学习笔记4--对象的初始化与回收

    java学习笔记3--类与对象的基础 

    java学习笔记2--数据类型、数组

    java学习笔记1--开发环境平台总结

  • 相关阅读:
    Spring Boot 2.4 手工和 SDKMAN! 安装 Spring Boot 命令行
    Spring Boot 2.4 安装
    Java Web 开发中的中文乱码与解决方式
    项目文件与 SVN 资源库同步提示错误 Attempted to lock an already-locked dir
    JavaScript中的日期时间函数
    jmeter测试 常用数据库的性能
    jmeter 运行多个sql
    Linux 进程管理
    Linux vi 文件编辑
    Linux 常用文件管理命令
  • 原文地址:https://www.cnblogs.com/wuyudong/p/java-study-note16.html
Copyright © 2020-2023  润新知