Java语言的输入与输出与文件处理

一、流的概念

流是指计算机各部件之间的数据流动。按照数据的传输方法,流可以分为输入流与输出流。从流的内容上划分,流分为字节流和字符流。

  1. 输入输出流
    在Java语言中,把不同类型的输入输出源(键盘,屏幕等)抽象为流,其中输入或输出的数据被称为数据流。数据流分为输入流和输出流,从外设或外存传递到应用程序的流称为输入流,将数据从应用程序传递到外设或外存的流称为输出流。输入流只可以从其中读取数据,输出流只可以向其中写入数据。
  2. 缓冲流
    对数据流的每次操作若都是以字节为单位进行操作,那么数据的传输效率显然很低,所以为了提高数据的传输效率,通常使用缓冲流(buffered stream),即为一个流配有一个缓冲区(buffer),这个缓冲区就是专门用于传送数据的一块内存。
    当向一个缓冲流写入数据时,系统将数据发送到缓冲区,而不是直接发送到外部设备。缓冲区自动记录数据,当缓冲区满时,系统会将数据全部发送到相应的外部设备。
    当从一个缓冲流读取数据时,系统实际是从缓冲区中读取数据,当缓冲区空时,系统就会从相关外部设备自动读取数据,并读取尽可能多的数据填满缓冲区,由此可见,缓冲区提高了内存与外部设备之间的数据传输效率。

二、输入输出流类库

为了方便流的处理,Java语言的流类都封装在java.io包之中,所以要是用流类,必须导入java.io包。
按照处理的数据单位分为字节流和字符流。

  • 字节流:操作的数据单元是8位字节,InputStreamOutputStream作为抽象基类。
  • 字符流:操作的数据单元是字符,WriterReader作为抽象基类。
  • 字节流可以处理所有的数据文件,但是若处理纯文本数据,建议使用字符流。

三、使用InputStreamOutputStream流类

InputStreamOutputStream流类是Java语言中用来处理以位(bit)为单位的流,它除了可以用来处理二进制文件(binary file)的数据以外,也可以用来处理文本文件。
**注意:虽然说字节流可以操作文本文件,但是并不提倡这样做,因为字节流操作文本文件,若是文件中有汉字,可能会出现乱码,这是因为字节流不能够直接操作Unicode字符所导致,因此Java语言不提倡使用字节流读取文本文件,而是建议字符流操作文本文件。

  1. InputStream流类
    常用方法:

    方法 功能
    public int read() 从输入流中的当前位置读入一个字节的二进制数据,然后以此数据位地位字节,配上8个全0的高位字节合成一个16位的整型量(0-255)返回给调用此方法的语句,若输入流中的当前位置没有数据,则返回-1。
    public int read(byte[] b) 从输入流中的当前位置连续读入多个字节保存在数组b中,同时返回所读到的字节数。
    public int read(byte[] b, int off, int len) 从输入流中的当前位置连续读入len个字节,从数组b的第off+1个元素位置处开始存放,同时返回所读到的字节数。
    public int available() 返回输入流中可以读取的字节数
    public long skip(long n) 使位置指针从当前位置向后跳过n个字节
    public void mark(int readlimit) 在当前位置处做一个标记,并且在输入流中读取readlimit个字节数后该标记失效
    public void reset() 将位置指针返回到标记的位置
    public void close() 关闭输入流与外设的链接并且释放所占用的系统资源

当Java程序需要从外设如键盘、磁盘文件等读入数据时,应该创建一个适当类型的输入流对象来完成与该设备的连接,由于InputStream是抽象类,所以程序中创建的输入流对象一般是InputStream某个子类的对象,通过调用该对象继承的read()方法就可以实现相应设备的输入操作。
流中的方法都声明抛出异常,所以程序中调用流方法时必须处理异常,否则编译不能通过,由于所有的I/O流都实现了AutoCloseable接口,而只是实现该接口的资源才可以使用try-with-resources语句自动关闭打开的资源。因此,在使用I/O流进行输入输出操作的时候,可以使用try-with-resources语句处理异常。

  1. OutputStream流类
    OutputStream流类中包含一套所有字节输出都需要的方法,可以完成最基本的向输出流写入数据的功能。

    方法 功能
    public void write(int b) 将参数b的低位字节写入到输出流
    public void write(byte[] b) 将字节数组b的全部字节按顺序写入到输出流
    public void write(byte[] b, int off, int len) 将字节数组b中的第off+1个元素开始的len个数据,顺序地写入到输出流
    public void flush() 强制清空缓冲区并执行向外设写操作
    public void close() 关闭输出流与外设的连接并且释放所占用的系统资源

    例如:写入到文件中

    import java.io.*;
    
    class demo {
        public static void main(String[] args) throws IOException {
            char ch;
            FileInputStream fin = new FileInputStream(FileDescriptor.in);
            FileOutputStream fout = new FileOutputStream("text.txt");
            System.out.println("请输入一串字符!(以#号结束)");
            while ((ch = (char) fin.read()) != '#')
                fout.write(ch);
        }
    
    }
    

    读取文件的内容

     import java.io.*;
     
     class demo {
         public static void main(String[] args) throws IOException {
             char ch;
             int data;
     //        FileInputStream fin = new FileInputStream(FileDescriptor.in);
     //        FileOutputStream fout = new FileOutputStream("text.txt");
     //        System.out.println("请输入一串字符!(以#号结束)");
     //        while ((ch = (char) fin.read()) != '#')
     //            fout.write(ch);
             FileInputStream fin = new FileInputStream("text.txt");
             FileOutputStream fout = new FileOutputStream(FileDescriptor.out);
             while (fin.available() > 0) {
                 data = fin.read();
                 fout.write(data);
             }
         }
     }
    

    FileInputStreamFileOutputStream主要用来处理二进制图像文件,例如:

    import java.io.*;
    
    class demo {
        public static void main(String[] args) throws IOException {
            FileInputStream fin = new FileInputStream("1.png");
            FileOutputStream fout = new FileOutputStream("2.png");
            System.out.println("文件大小"+fin.available());//利用available()方法获取文件的大小,并输出,以字节(B)为单位。
            byte[] b = new byte[fin.available()];//新建一个byte类型的数组
            fin.read(b);//将文件读入数组
            fout.write(b);//将数组中的数据写入新文件"2.png"中。
            System.out.println("文件复制更名成功!");
        }
    }
    
  2. 顺序输入流
    顺序输入流类SequenceInputStreamInputStream的直接子类,其功能是将多个输入流顺序连接在一起,形成单一的输入数据流,没有对应的输出数据流存在。再进行输入时,顺序输入流依次打开每个输入流并读取数据,也在读取完毕后将该流关闭,然后自动切换到下一个输入流。也就是说,由多个输入流构成的顺序输入流,当从一个流中中读取数据遇到EOF时,SequenceInputStream将会自动转向下一个输入流,直到构成SequenceInputStream类的最后一个输入流读取到EOF时为止。
    SequenceInputStream类的构造方法、

    构造方法 功能
    public SequenceInputStream(Enumeration e) 创建一个串行输入流,连接枚举对象e中的所有输入流
    public SequenceInputStream(InputStream s1, InputStream s2) 创建一个串行输入流,连接输入流s1和s2

    SequenceInputStream类的常用方法

    常用方法 功能
    public int available() 返回流中可读取的字节数
    public void close() 关闭输入流
    public int read() 从输入流中读取字节,遇到EOF就转向下一输入流
    public int read(byte[] b, int off, int len) 将len个数据读到一个字节数组从off开始的位置
  3. 管道输入输出流
    Java之中管道字节输入流PipedInputSteam和管道字节输出流PipedOutputStream类提供了利用管道方式进行数据输入输出管理的类。管道流用来将一个程序或县城的输出连接到另外一个程序或线程作为输入,是的相互连接的线程能够通过PipedInputStreamPipedOutputStream流进行数据交换,从而可以实现程序内部线程间的通信或不同程序之间的通信。
    jdk1.8文档中明确说明了,不建议从单个线程中使用这两个对象,因为它可能会使线程死锁。
    具体方法参考jdk文档

  4. 过滤输入输出流
    过滤字节输入流类FilterInputStream和过滤字节输出流类FilterOuputStream,分别实现了在数据的读写操作的同时进行数据处理,他们是InputStramOutputStream类的直接子类。过滤字节输入输出流类的主要特点是,顾虑字节输入输出流时间里在基本输入输出流之上,并在输入输出数据的同时能对所传输的数据做指定类型或格式的转换,即可实现对二进制字节数据的理解和编码转换。
    具体使用方法见jdk文档。
    整理如下:

    DataInputStream类的常用方法

    常用方法 说明
    public boolean readBoolean() 从流中读1字节,但是字节值非0返回true,否则返回false
    public byte readByte() 从流中读1自字节,返回该字节值
    public char readChar() 从流中读取a,b2字节,形成Unicode字符(char)((a<<8)|(b&0xff))
    public short readShort() 从六中读入2字节的short值并返回
    public int readInt() 从流中读入4字节的int值并返回
    public float readFloat() 从流中读入4字节的float值并返回
    public long readLong() 从流中读入8字节的long值并返回
    public double readDouble() 从流中读入8字节的double值并返回

    DataOutputStream类的常用方法

    常用方法 说明
    public void writeBoolean(boolean v) 将boolean写入底层输出流作为1字节值,值true被写为(byte)1,值false作为值(byte)0
    public void writeByte(int v) 向流中写入1字节,最低的1字节,其他的字节丢弃
    public writeChar(int v) 向流中写入参数v的高2字节,其他的字节丢弃
    public writeShort(int v) 向流中写入参数v的高2字节,其他的字节丢弃
    public writeInt(int v) 向流中写入参数v的4字节
    public writeFloat(float v) 向流中写入参数v的4字节
    public writeLong(long v) 向流中写入参数v的8字节
    public writeDouble(double v) 向流中写入参数v的8字节
    package sample;
    
    import java.io.*;
    
    class demo {
    
        public static void main(String[] args) throws IOException {
            FileOutputStream fout = new FileOutputStream("text");
            DataOutputStream dout = new DataOutputStream(fout);
    
            dout.writeInt(10);
            dout.writeLong(12345);
            dout.writeFloat(3.1415626f);
            dout.writeDouble(987654321.123);
            dout.writeBoolean(true);
            dout.writeChars("GoodBye!\0");//此处不加上\0后面会抛出IO异常EOFException;
            FileInputStream fin = new FileInputStream("text");
            DataInputStream din = new DataInputStream(fin);
    
            System.out.println(din.readInt());
            System.out.println(din.readLong());
            System.out.println(din.readFloat());
            System.out.println(din.readDouble());
            System.out.println(din.readBoolean());
    
            char ch;
            while ((ch = din.readChar()) != '\0')
                System.out.print(ch);
        }
    }
    

    writeChars()里面需要加上一个\0,不然的话会抛出异常,亲测,EOFException3表示输入过程中意外地到达文件尾或流尾的信号,导致异常。

  5. 标准输入输出流
    当程序对标准输入输出设备进行操作的时候,则不需要创建输入或输出流类的对象。
    对于一般的系统来说,标准输入设备通常指键盘,标准输出设备通常指屏幕显示器。为了方便程序对键盘输入和屏幕输出进行操作。Java系统事先在System类中定义静态流对象System.inSystem.outSystem.errSystem.in对应着输入流,通常指键盘输入设备。System.out对应着输出流,指显示器等信息输出设备,System.err对应着标准错误输出设备,使得程序的运行错误,可以由固定的输出位置,通常来说该对象对应着显示器。
    1.标准输入
    Java语言的标准输入是System.inBufferedInputStream类的对象,当程序需要从键盘上读入数据时,只需要调用System.inread()方法即可,该方法从键盘缓冲区读入一个字节的二进制数据,并将它们存储到缓冲区b,实际读取的字节数作为整数返回。其方法:public int read (byte[] b) throws IOException。第一个字节存入b[0],以此类推。
    2.标准输出
    Java语言的标准输出,是打印输出流PrintStream类的对象。详见jdk手册。

    import java.io.*;
    
    class demo {
    
        public static void main(String[] args) throws IOException {
            byte[] b = new byte[128];
            System.out.println("请输入字符:");
            int count = System.in.read(b);
            System.out.println("输入的是:");
            for(int i = 0; i < count; ++i){
                System.out.println(b[i] + " ");//输出的是ASCII值
            }
            for(int i = 0; i < count; ++i)
                System.out.println((char)b[i] + " ");//以字符的方式输出数组b元素
        }
    
    

四、使用ReaderWriter流类

ReaderWriter类则是用来处理“字符流”的,也就是文本文件,与字节输入输出流的功能一样,字符输入输出流类知识建立了一条通往文本文件的通道。
Reader类的常用方法

常用方法 说明
public int read() 从输入流中读一个字符
publici int read(char[] cbuf) 从输入流中读最多cbuf.length个字符,存入字符数组cbuf
public int read(char[] cbuffer, int off, int len) 从输入流中读最多len个字符,存入字符数组cbuffer中从off开始的位置
public long skip(long n) 从输入流中最多向后跳n个字符
public boolean ready() 判断流是否最好读的准备
public void mark(int readAheadLimit) 标记输入流的当前位置
public boolean markSupported() 测试输入流是否支持mark
public void reset() 重定位输入流
public void close() 关闭输入流

Writer类的常用方法

常用方法 说明
public void write(int c) 将单一字符c输出到流中
public void write(String str) 将字符串输出到流中
public void write(char[] cbuf) 将字符数组cbuf输出到流
public void write(char[] cbuf, int off, int len) 将字符数组按指定格式输出
public void flush() 将缓冲区的数组写到文件
public void close() 关闭输出流
  1. 使用FileReader类读取文件
    文件字符输入流类FileReader是继承自InputStreamReader类,而InputStreamReader类又是继承自Reader类,因此,Reader类与InputStreamReader类所提供的方法均可供FileReader类所创建对象使用。
    使用FileReader类读取文件时,必须先调用FileReader()构造方法创建FileReader类的对象,再利用它来调用read()方法。
    其构造方法:public FileReader(String name),根据文件名称创建一个可以读取的输入流对象。

    import java.io.*;
    
    class demo {
    
        public static void main(String[] args) throws IOException {
            char[] ch = new char[500];//创建可以容纳500字符的数组
            FileReader file = new FileReader("text.txt");
            int num = file.read(ch);//将数据读取到数组之中,并且返回读取的字符数量
            String str = new String(ch, 0, num);//将字符数组转换成为字符串
            System.out.println("读取的字符个数有:" + num + "个。内容如下:");
            System.out.println(str);
        }
    }
    

    这样子会将文件中的内容读出来,输出。但是可能会有人遇到乱码问题,记住,windows默认创建的txt文件时ANSI编码格式,所以,你需要保存为UTF-8格式。

  2. 使用FileWriter类写入文件
    文件字符输出流类FileWriter继承自OutputStreamWriter类,但是OutputStreamWriter类又是从Writer类中继承得到的,所以说,Writer类与OutputStreamWriter类suotigong7的方法均可提供给FileWriter类所创建的对象使用。
    FileWriter类的构造方法

    构造方法 功能说明
    public FileWriter(String filename) 根据所给的文件名创建一个可以供写入字符数据的输出流对象,原先的文件会被覆盖
    public FileWriter(String filename, boolean a) 同上,如果说a设置为true,则会被数据追加在原先文件之后
    import java.io.*;
    
     class demo {
         public static void main(String[] args) throws IOException {
             FileWriter file = new FileWriter("text.txt");
             String str = "Java牛皮!";
             file.write(str);
             file.close();
         }
     }
    
  3. 利用BufferedReader类读取文件
    缓冲字符输入流类BufferedReader继承自Reader类,BufferedReader类是用来读取缓冲区李的数据,使用该类读取缓冲区中的数据之前,必须创建FileReader类对象,再将以该对象来创建BufferedReader类的对象,然后才可以利用该对象来读取缓冲区中的数据。
    构造方法

    构造方法 功能说明
    public BufferedReader(Reader in) 创建缓冲区字符输入流
    public BufferedReader(Reader in, int size) 创建缓冲区字符输入流,并设置缓冲区大小

    常用方法

    常用方法 功能说明
    public int read() 读取单一字符
    public int read(char[] ch) 从流中读取字符并且写入到字符数组之中
    public int read(char[] ch, int off, int len) 从流中读取字符存放到字符数组之中,off代表数组下标,len代表的是数组长度
    public long skip(long n) 跳过n个字符不读取
    public String readLine() 读取一行字符串
    public void close() 关闭流
    import java.io.*;
    
    class demo {
        public static void main(String[] args) throws IOException {
            FileReader file = new FileReader("text.txt");
            BufferedReader bfile = new BufferedReader(file);
    
            String tmpString;
            while((tmpString = bfile.readLine())!=null){
                System.out.println(tmpString);
            }
        }
    }
    
  4. 使用BufferedWriter类写入文件
    BufferedReader类类似,BufferedWriter继承自Writer类,BufferedWriter类,是用来将数据写入缓冲区中,首先来说,我必须创建FileWriter类对象,再以该对象为参数创建BufferedWriter类的对象,然后就可以利用此对象来讲数据写入缓冲区中,所以不同的是,缓冲区中的数据最后必须都要用flush()方法将缓冲区清空,也就是将缓冲区的数据全部写到文件之内。
    缓冲字符输出流类BuffereWriter的构造方法:

    构造方法 功能说明
    public BufferedWriter(Writer out) 创建缓冲区字符输出流
    public BufferedWriter(Writer out, int size) 创建缓冲区字符输出流

    BufferedWriter类的常用方法

    常用方法 功能说明
    public void writer(int c) 将单一字符写入缓冲区
    public void write(char[] ch. int off, int len) 将字符数组ch按照指定格式写入到输出缓冲区中(off表示数组下标,len表示写入的字符数)
    pulic void write(String str, int off, int len) 写入字符串(off表示下标,len表示写入的字符数)
    public void newLine() 写入回车换行字符
    public void close() 关闭流
    import java.io.*;
    
    class demo {
        public static void main(String[] args) throws IOException {
            BufferedWriter out = new BufferedWriter(new FileWriter("text.txt"));
            String str = new String("Hello!Java!我是新一代程序员!");
            out.write(str);
            out.newLine();
            out.write("我必定会登顶巅峰!");
            out.close();
        }
    } 
    

五、文件的处理与随机访问

  1. 对于文件以及文件夹的管理

    • 创建一个File类的对象,每个File类的对象对应着系统的一个磁盘文件或者文件夹,所以说创建File类对象需要给出它所对应的文件名或者文件夹名。
      File类的构造方法

      构造方法 功能说明
      public File(String path) 利用path参数创建对象
      public File(String path, String name) 以path为路径,创建name的文件或文件夹
      public File(File dir. String name) 用一个已经存在代表某磁盘的文件夹的File对象dir作为文件夹,以name作为文件或文件夹名来创建File对象

      因为在不同的操作系统中使用的文件夹的分隔符不一样,如Windows操作系统使用的是反斜线,但是在UNIX操作系统上使用的是正斜线,但是为了保证Java程序能够在不同的平台上运行,可以使用File类的一个静态变量File.separator。该属性中保存了当前系统规定的文件夹分隔符,使用它会组合成在不同操作系统上都能够通用的路径。

    • 文件或文件夹属性
      常用方法

    用方法 功能说明
    public boolean exists() 判断文件或文件夹是否存在
    public boolean isFile() 判断对象是否代表有效文件
    public boolean isDirectory() 判断对象是否代表有效文件夹
    public String getName() 返回文件名或文件夹名
    public String getPath() 返回文件或者文件夹的路径
    public long length() 返回文件的字节数
    public boolean canRead() 判断文件是否可读
    public boolean canWriter() 判断文件是否可写
    public String[] list() 将文件夹中所有文件名保存在字符串数组中返回
    public boolean equals(File file) 比较两个文件或文件夹是否相同
    1. 文件或文件夹操作
      管理操作方法
    常用方法 功能说明
    public boolean renameTo(File newFile) 将文件重命名成newFile对应的文件名
    public boolean delete() 将当前文件删除,若删除成功返回true,否则返回false
    public boolean mkdir() 创建当前文件夹的子文件夹,若成功返回true,否则返回false
  2. 对文件的随机访问
    RandomAccessFile类的构造方法

    构造方法 功能说明
    public RandomAccessFile(String name, String mode) 以name来指定随机文件流对象所对应的文件名,以mode表示对文件的访问模式
    public RandomAccessFile(File file, String mode) 以file来指定随机文件流对象所对应的文件名,以mode表示对文件的访问模式

    访问模式:r:表示只读方式打开文件,rw:表示以读写方式打开文件。

    RandomAccessFile类的常用方法

    常用方法 功能说明
    public void close() 关闭随机访问文件并释放系统资源
    public final FIleDescriptorgetFD() 获取文件描述符
    public long getFilePointer() 返回文件指针的当前位置
    public long length() 返回文件长度
    public int skipBytes(int n) 跳过输入流中n个字符,并返回跳过实际的字节数
    public int read() 从文件输入流中读取一个字节的数据
    public int read(byte[] b, int off, int len) 从文件输入流的当前指针位置开始读取长度为len字节的数据存放到字节数组b中,存放的便宜位置为off,若遇到文件结束符,则返回值-1
    public final void readFully(byte[] b) 从文件输入流的当前指针位置开始读取b.length字节的数据存放到字节数组b中,若遇文件结束符,则抛出EOFException类异常
    public final void readFully(byte[] b, int off, int len) 从文件输入流的当前指针位置开始读取长度为len字节的数据,放到b数组中,存放的位置开始为off,遇到文件结束符,则抛出EOFException类异常
    public final boolean readBoolean() 读取文件的逻辑值
    public final byte readByte() 从文件中读取带符号的字节值
    public final char readChar() 从文件中读取一个Unicode字符
    public final String readLine() 从文本文件中读取一行
    public void seek(long pos) 设置文件指针位置

    RandomAccessFile类用于写入操作的常用方法

    用方法 功能说明
    public void write(int b) 在文件指针的当前位置写入一个int型数据b
    public void writeBoolean(boolean v) 在文件指针的当前位置写入一个boolean型数据v
    public void writeByte(int v) 在文件指针的当前位置写入一个字节值
    public void writeBytes(String s) 以字节形式写入一个字符串到文件
    public void writeChar(int v) 在文件指针的当前位置写入v的两字节,高字节优先
    public void writeChars(String s) 以字符形式写一个字符串到文件
    public void writeDouble(double v) 在文件当前指针位置写入8字节数据v
    public void writeFloat(float v) 在文件当前指针位置写入4字节数据v
    public void writeInt(int v) 在整形数作为4字节写入文件
    public void writeLong(long v) 将长整型数作为8字节写入文件
    public void writeShort(int v) 在文件指针当前位置写入2字节,高位字节
    public void writeUTF(String str) 作为UTF格式向文件之中写入一个字符串
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章