Java中的IO操作涉及到的概念及相关类很多,很容易弄混,今天特来整理总结一下,并附上一份完整的文件操作的代码。
概念解析
读和写
流就是管道,向管道里面写数据用输出流:write
从管道里面读数据,用输入流:read
流的分类
流,按照不同的维度有不同的分类,按照从流中每次读取数据单位不同,划分为字节流字符流。
按照流-对应操作的角色的不同(是IO设备,还是其他流)是否能够直接向特定的IO设备如文件、网络、磁盘等,能够直接与这些进行读写的称之为节点流,
对节点流进一步封装的流,通过封装后的流来实现读写功能,处理流也被称为高级流。
注意事项
1、流和数组不一样,不能通过索引读写数据。在流中,你也不能像数组那样前后移动读取数据,除非使用RandomAccessFile 处理文件。
2、流仅仅只是一个连续的数据流。流中的数据只能够顺序访问。当达到流末尾时,返回-1。
3、InputStream的read()方法返回一个字节大小,返回值的范围在0到255之间。
4、Reader的read()方法返回一个字符,会根据文本的编码,一次读取一个或者多个字节,返回值的范围在0到65535之间。
5、read(byte[])会尝试读取与给定字节数组容量一样大的字节数,返回值int表示已经读取过的字节数。如果InputStream内可读的数据不足以填满字节数组,那么数组剩余的部分将包含本次读取之前的数据。记得检查有多少数据实际被写入到了字节数组中。
6、read(byte, int offset, int length)同样将数据读取到字节数组中,不同的是,该方法从数组的offset位置开始,并且最多将length个字节写入到数组中。同样地,read(byte, int offset, int length)方法返回一个int变量,表示有多少字节已经被写入到字节数组中,所以请记得在读取数据前检查上一次调用read(byte, int offset, int length)的返回值。
示例代码
package com.lingyejun.io; import java.io.*; /** * Created by Lingye on 2018/9/27 20:15 */ public class FileStreamOperate { public static final String READ_UTF8_FILE_PATH = "D:\input-utf8.txt"; public static final String READ_UNICODE_FILE_PATH = "D:\input-unicode.txt"; public static final String WRITE_BYTES_FILE_PATH = "D:\output-bytes.txt"; public static final String WRITE_CHARS_FILE_PATH = "D:\output-char.txt"; /** * 按照字节流的方式读取文件内容 * * Step 1.根据文件路径,构建文件对象 * Step 2.创建输入流用来读取文件内容 * Step 3.创建字节数组来存放读取内容 * Step 4.关闭读取文件的输入流 * * @return */ public void readFileByFileInputStream() { System.out.println("=== readFileByFileInputStream Start ==="); // 构建文件对象 File inputFile = new File(READ_UTF8_FILE_PATH); // 初始化输入流 InputStream inputStream = null; try { // 创建字节输入流 inputStream = new FileInputStream(inputFile); // 读取到1KB字节数组中 byte[] buffer = new byte[100]; // 每次读取的字节数 int readLength; // 读取数据并放到buffer数组中 while ((readLength = inputStream.read(buffer)) != -1) { // UTF-8为变长编码,一个汉字占3个字节 System.out.println("本次读取" + readLength + "个字节数据内容为:" + new String(buffer)); } } catch (FileNotFoundException e) { // 文件未找到时异常处理 e.printStackTrace(); } catch (IOException e) { // 读取过程中,删除文件会出此异常 e.printStackTrace(); } finally { if (inputStream != null) { try { // 关闭流过程,也有可能出现异常 inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== readFileByFileInputStream End ==="); } /** * 按照字符流的方式读取文件内容 * * Step 1.根据文件路径,构建文件对象 * Step 2.创建字符输入流读取文件内容 * Step 3.创建字符数组来存放读取内容 * Step 4.关闭读取文件的字符输入流 * * @return */ public void readFileByFileReader(){ System.out.println("=== readFileByFileReader Start ==="); // 根据路径拿到文件对象 File file = new File(READ_UTF8_FILE_PATH); // 初始化字符输入流 Reader fileReader = null; // 初始化存放读取内容的字符数组 char[] charArray = new char[100]; // 初始化一个字符 char once; try { fileReader = new FileReader(file); // 一次读取一个数组长度的字符串 fileReader.read(charArray); System.out.println(charArray); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileReader != null) { try { // 关闭流过程,也有可能出现异常 fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== readFileByFileReader End ==="); } /** * 通过字节流的方式写入信息到文件 * * Step 1.根据文件路径,构建文件对象 * Step 2.创建字节输出流写出信息到文件 * Step 3.构造待写出的内容,并转为字节数组 * Step 4.关闭读取文件的字符输出流 */ public void writeFileByFileOutputStream() { System.out.println("=== writeFileByFileOutputStream Start ==="); // 创建写出文件 File file = new File(WRITE_BYTES_FILE_PATH); // 初始化字节输出流 OutputStream outputStream = null; // 写出内容 String outInfo = "写出测试"; // 转成字节数组 byte[] byteArray = outInfo.getBytes(); try { // 创建输出字节流 outputStream = new FileOutputStream(file); outputStream.write(byteArray); System.out.println("按照字节流成功写出内容:"+outInfo); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { try { // 关闭写出流时,注意抓异常 outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== writeFileByFileOutputStream End ==="); } /** * 通过字符流的方式写入信息到文件 * * Step 1.根据文件路径,构建文件对象 * Step 2.创建字符输出流写出信息到文件 * Step 3.构造待写出的内容,并转为字符数组 * Step 4.关闭读取文件的字符输出流 */ public void writeFileByFileWriter(){ System.out.println("=== writeFileByFileWriter Start ==="); // 创建写出文件 File file = new File(WRITE_CHARS_FILE_PATH); // 初始化字符输出流 Writer fileWriter = null; String strInfo = "字符写出数据"; try { // 创建输出字符流 fileWriter = new FileWriter(file); // 写出内容 fileWriter.write(strInfo); System.out.println("按照字符流成功写出内容:"+strInfo); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if(fileWriter != null){ try { // 关闭写出流时,注意抓异常 fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== writeFileByFileWriter End ==="); } /** * 任意读取文件内容 */ public void randomAccessFile(){ System.out.println("=== randomAccessFile Start ==="); File file = new File(READ_UTF8_FILE_PATH); try { RandomAccessFile randomAccessFile = new RandomAccessFile(file,"r"); // 获取文件当前的指针位置 System.out.println("file now pointer is "+randomAccessFile.getFilePointer()); // 将文件指针设置到指定位置(绝对位置) randomAccessFile.seek(3); // 相对位置,相对于当前的位置, randomAccessFile.skipBytes(3); System.out.println("file now pointer is "+randomAccessFile.getFilePointer()); // 字节数组 byte[] buffer = new byte[17]; // off是指的写到buffer的数组的起始位置 randomAccessFile.read(buffer,0,buffer.length); System.out.println(new String(buffer)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println("=== randomAccessFile End ==="); } /** * 通过字节流的方式将文件内容拷贝到另一个文件中 * * Step 1.根据文件路径,构建源文件对象 * Step 2.根据文件路径,构造目的文件对象 * Step 3.创建字节输入流从源文件中读取信息 * Step 4.将读入到内存的信息再写出到目的文件中 * Step 5.拷贝完成后关闭输入输出流 */ public void copyFile() { System.out.println("=== copyFile Start ==="); // 输入文件对象 File inFile = new File(READ_UTF8_FILE_PATH); // 输出文件对象 File outFile = new File(WRITE_BYTES_FILE_PATH); // 初始化输入流 InputStream inputStream = null; // 初始化输出流 OutputStream outputStream = null; try { // 将输入流怼到输入文件,使程序内存与磁盘建立联系 inputStream = new FileInputStream(inFile); // 将输出流怼到输出文件,使程序内存与磁盘建立联系 outputStream = new FileOutputStream(outFile); while (true) { // 读取信息到内存 int temp = inputStream.read(); // 拷贝完成 if (temp == -1) { break; } // 将内容拷贝到输出文件中 outputStream.write(temp); } System.out.println("拷贝文件成功完成"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { // 关闭输入流异常后,也要保证输出流关闭 inputStream.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } } System.out.println("=== copyFile End ==="); } public static void main(String[] args) { FileStreamOperate fileStreamOperate = new FileStreamOperate(); // 按照字节流读取的话,一个汉字两个字节 fileStreamOperate.readFileByFileInputStream(); // 按照字符读取,java Unicode编码一个字符(不论字母还是汉字)两个字节 fileStreamOperate.readFileByFileReader(); // 采用任意读取的方式读取文件信息 fileStreamOperate.randomAccessFile(); // 按照字节流的方式写信息到文件 fileStreamOperate.writeFileByFileOutputStream(); // 按照字符流的方式写信息到文件 fileStreamOperate.writeFileByFileWriter(); // 拷贝文件 fileStreamOperate.copyFile(); } }
输入输出文件和执行结果
文件展示
执行结果
小结
通过本篇,我们学会了文件IO的一些常规操作方式,但是每次读取完文件后都需要在最后进行关闭流资源的close操作,这个操作不可省略,但是每次都写又显得十分啰嗦,有没有更加简便的方式呢,请看我的下一篇文章《try with resources简洁的异常捕获机制》。