流与文件

Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。

Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。

一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。

Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。

标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流等等,java中将输入输出抽象称为流,就好像水管,将两个容器连接起来。将数据从外存中读取到内存中的称为输入流,将数据从内存写入外存中的称为输出流。

  • 数据流:一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。
  • 输入流(Input Stream):程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道
  • 输出流(Output Stream):程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。

采用数据流的目的就是使得输出输入独立于设备。

  • Input Stream不关心数据源来自何种设备(键盘,文件,网络)
  • Output Stream不关心数据的目的是何种设备(显示器、打印机、文件、网络…)

数据流分类

流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种:

  • 字节流:数据流中最小的数据单元是字节
  • 字符流:数据流中最小的数据单元是字符,Java中的字符是Unicode编码,一个字符占用两个字节。

流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。

Java.io包

在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。

Java I/O主要包括如下几个层次,包含三个部分:

  1. 流式部分 ―― IO的主体部分;
  2. 非流式部分 ―― 主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;
  3. 其他类 ―― 文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

主要的类如下:

  1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。
  2. InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
  3. OutputStream(二进制格式操作):抽象类,基于字节的输出操作,是所有输出流的父类。定义了所有输出流都具有的共同特征。
  4. Reader(文件格式操作):抽象类,基于字符的输入操作。
  5. Writer(文件格式操作):抽象类,基于字符的输出操作。
  6. RandomAccessFile(随机文件操作):它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。

Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。

Java中IO流的体系结构如图:

输入/输出 字节流 字符流
输入流 Inputstream Reader
输出流 OutputStream Writer

标准I/O

Java程序可通过命令行参数与外界进行简短的信息交换,同时,也规定了与标准输入、输出设备,如键盘、显示器进行信息交换的方式。而通过文件可以与外界进行任意数据形式的信息交换。

标准输出流 System.out

System.out 向标准输出设备输出数据,其数据类型为PrintStream。方法:

  • Void print()
  • Void println()

标准输入流 System.in

System.in 读取标准输入设备数据(从标准输入获取数据,一般是键盘),其数据类型为InputStream。方法:

  • int read() // 返回ASCII码。若返回值为-1,说明没有读取到任何字节,读取工作结束。
  • int read(byte[] b) // 读入多个字节到缓冲区b中,返回值是读入的字节数。

标准错误流

System.err 输出标准错误,其数据类型为 PrintStream

非流式文件类 File

在Java语言的java.io包中,由File类提供了描述文件和目录的操作与管理方法。但File类不是InputStream、OutputStream或Reader、Writer的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件与目录。

作用:File类主要用于命名文件、查询文件属性和处理文件目录。

构造函数:

public class File extends Object implements Serializable,Comparable {
  public File(String pathname)
  public File(String parent, String child)
  public File(File parent, String child)
  public File(URI uri)
}

常用方法:

public boolean mkdirs() // 创建目录
public String[] list() // 列出文件及文件夹
public File[] listFiles() // 列出文件
public static File[] listRoots()
public boolean exists() // 文件(夹)是否存在
public boolean renameTo(File dest) // 重命名
public boolean delete() // 删除
public boolean isDirectory() // 是否是目录
public boolean isFile() // 是否是文件
public boolean isHidden() // 是否隐藏
public boolean canExecute() // 是否可执行
public boolean setReadOnly() // 设为只读
public boolean setWritable(boolean writable) // 设置可写性
public boolean setReadable(boolean readable) // 设置可读性
public boolean setExecutable(boolean executable) // 设置是否可执行
public String getPath()
public String getName()
public String getParent()
public long getTotalSpace()
public long getFreeSpace()
public long getUsableSpace()

字节流 InputStream/OutputStream

InputStream抽象类

InputStream 为字节输入流,它本身为一个抽象类,必须依靠其子类实现各种功能,此抽象类是表示字节输入流的所有类的超类。 继承自InputStream的流都是向程序中输入数据的,且数据单位为字节(8bit);

InputStream是输入字节数据用的类,所以InputStream类提供了3种重载的read方法

常用的方法:

  • public int read() 读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。
  • public int read(byte b[]) 读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的。
  • public int read(byte b[], int off, int len) 从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。
  • public int available() 返回输入流中可以读取的字节数。注意:若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用。
  • public long skip(long n) 忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取 。
  • public int close() 我们在使用完后,必须对我们打开的流进行关闭。

主要的子类:

  • FileInputStream 把一个文件作为InputStream,实现对文件的读取操作;
  • ByteArrayInputStream 把内存中的一个缓冲区作为InputStream使用;
  • PipedInputStream 实现了pipe的概念,主要在线程中使用;
  • SequenceInputStream 把多个InputStream合并为一个InputStream 。

OutputStream抽象类

OutputStream提供了3个write方法来做数据的输出,这个是和InputStream是相对应的。

常用的方法:

  • public void write(byte b[]) 将参数b中的字节写到输出流。
  • public void write(byte b[], int off, int len) 将参数b的从偏移量off开始的len个字节写到输出流。
  • public void write(int b) 先将int转换为byte类型,把低字节写入到输出流中。
  • public void flush() 将数据缓冲区中数据全部输出,并清空缓冲区。
  • public void close() 关闭输出流并释放与流相关的系统资源。

主要的子类:

  • FileOutputStream 把信息存入文件中
  • ByteArrayOutputStream 把信息存入内存中的一个缓冲区中
  • PipedOutputStream 实现了pipe的概念,主要在线程中使用

流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。

字符流 Writer/Reader

Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。

Reader抽象类

用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int)close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。其子类如下:

常用方法:

  • public int read() throws IOException 读取一个字符,返回值为读取的字符
  • public int read(char cbuf[]) throws IOException 读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量
  • public int read(char cbuf[],int off,int len) throws IOException 读取len个字符,从数组cbuf[]的下标off处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现

主要的子类:

  • FileReader 与FileInputStream对应
  • CharArrayReader 与ByteArrayInputStream对应
  • InputStreamReader 从输入流读取字节,在将它们转换成字符
  • FilterReader 允许过滤字符流
  • StringReader 与StringBufferInputStream对应, 读取字符串

FileReader

与FileInputStream对应,主要用来读取字符文件,使用缺省的字符编码,有三种构造函数:

  1. 将文件名作为字符串
FileReader f = new FileReader("c:/temp.txt");
  1. 构造函数将File对象作为其参数。
File f = new file("c:/temp.txt");
FileReader f1 = new FileReader(f);
  1. 构造函数将FileDescriptor对象作为参数
FileDescriptor() fd = new FileDescriptor();
FileReader f2=new FileReader(fd);

CharArrayReader

与ByteArrayInputStream对应, 构造函数有:

  1. 用指定字符数组作为参数
CharArrayReader(char[])
  1. 将字符数组作为输入流
CharArrayReader(char[], int, int)

Writer抽象类

写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)flush()close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。 其子类如下:

常用方法:

  • public void write(int c) throws IOException 将整型值c的低16位写入输出流
  • public void write(char cbuf[]) throws IOException 将字符数组cbuf[]写入输出流
  • public abstract void write(char cbuf[],int off,int len) throws IOException 将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流
  • public void write(String str) throws IOException 将字符串str中的字符写入输出流
  • public void write(String str,int off,int len) throws IOException 将字符串str 中从索引off开始处的len个字符写入输出流
  • public void flush() throws IOException 刷空输出流,并输出所有被缓存的字节
  • public abstract void close() throws IOException 关闭流

主要的子类:

  • FileWriter 与FileOutputStream对应, 将字符类型数据写入文件,使用缺省字符编码和缓冲器大小。
  • CharArrayWriter 与ByteArrayOutputStream对应, 将字符缓冲器用作输出。
  • PrintWriter 生成格式化输出
  • FilterWriter 用于写入过滤字符流
  • PipedWriter 与PipedOutputStream对应
  • StringWriter 无与之对应的以字节为导向的stream

缓冲输入输出流 BufferedInputStream/BufferedOutputStream

计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样可以减少访问硬盘的次数,提高传输效率。

  • BufferedInputStream 当向缓冲流写入数据时候,数据先写到缓冲区,待缓冲区写满后,系统一次性将数据发送给输出设备。
  • BufferedOutputStream 当从向缓冲流读取数据时候,系统先从缓冲区读出数据,待缓冲区为空时,系统再从输入设备读取数据到缓冲区。

1)将文件读入内存

将BufferedInputStream与FileInputStream相接

FileInputStream in = new FileInputStream("file1.txt");
BufferedInputStream bin = new BufferedInputStream(in);

2)将内存写入文件

将BufferedOutputStream与FileOutputStream相接

FileOutputStream out = new FileOutputStream("file1.txt");
BufferedOutputStream bout = new BufferedOutputStream(out);

3)键盘输入流读到内存

将BufferedReader与标准的数据流相接

InputStreamReader sin = new InputStreamReader(System.in)BufferedReader bin = new BufferedReader(sin);

实例

从控制台获取字符输入

public static void main(String[] args) throws IOException {
    char c;
    // 使用 System.in 创建 BufferedReader
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    System.out.println("输入字符, 按下 'q' 键退出。");
    // 读取字符
    do {
        c = (char) br.read();
        System.out.println(c);
    } while (c != 'q');
}

从控制台获取整行输入

public static void main(String[] args) throws IOException {
    // 使用 System.in 创建 BufferedReader
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String str;
    System.out.println("Enter lines of text.");
    System.out.println("Enter 'end' to quit.");
    do {
        str = br.readLine();
        System.out.println(str);
    } while (!str.equals("end"));
}

读写文件

public static void main(String[] args) throws IOException {
    // 构建FileOutputStream对象,文件不存在会自动新建
    File f = new File("a.txt");
    FileOutputStream fop = new FileOutputStream(f);
    // 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
    OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
    // 写入到缓冲区
    writer.append("中文输入");
    writer.append("\r\n");
    writer.append("English");
    // 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
    writer.flush();
    // 关闭写入流,同时会把缓冲区内容写入文件,所以上面的flush可以注释掉
    writer.close();
    // 关闭输出流,释放系统资源
    fop.close();
    // 构建FileInputStream对象
    FileInputStream fip = new FileInputStream(f);
    // 构建InputStreamReader对象, 编码与写入相同
    InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
    StringBuffer sb = new StringBuffer();
    while (reader.ready()) {
        // 转成char加到StringBuffer对象中
        sb.append((char)reader.read());
    }
    System.out.println(sb.toString());
    // 关闭读取流
    reader.close();
    // 关闭输入流,释放系统资源
    fip.close();
}

列出文件及文件夹

public static void main(String args[]) {
    String dirname = "d:/";
    File f1 = new File(dirname);
    if (f1.isDirectory()) {
        System.out.println("目录 " + dirname);
        String s[] = f1.list();
        for (int i = 0; i < s.length; i++) {
            File f = new File(dirname + "/" + s[i]);
            if (f.isDirectory()) {
                System.out.println(s[i] + " 是一个目录");
            } else {
                System.out.println(s[i] + " 是一个文件");
            }
        }
    } else {
        System.out.println(dirname + " 不是一个目录");
    }
}

创建文件夹

String dirname = "d:/tmp/log";
File d = new File(dirname);
d.mkdirs();

递归删除文件(夹)

public static void main(String args[]) {
    File folder = new File("d:/tmp");
    deleteFolder(folder);
}
// 删除文件及目录
public static void deleteFolder(File folder) {
    File[] files = folder.listFiles();
    if (files != null) {
        for (File f : files) {
            if (f.isDirectory()) {
                deleteFolder(f);
            } else {
                f.delete();
            }
        }
    }
    folder.delete();
}

MIT Licensed | Copyright © 2018-present 滇ICP备16006294号

Design by Quanzaiyu | Power by VuePress