史上第二全的 java 文件操作和数据读写
通过本文,可以详细的了解JDK关于目录和文件API操作。关注公众号,输入“java-summary”即可获得源码。
一、java.io.File
java.io.File类用于描述文件系统中的一个文件或目录
该类可以:
1、访问文件或目录的属性信息
2、访问一个目录中的所有子项
3、操作文件或目录(创建、删除)
该不可以:
访问文件的具体内容
开发时注意不同操作系统的路径的表示方法的差异(如'/'或'\\'),使用File.separator
可以屏蔽不同操作系统的路径的差异。
1.读取文件属性
package com.wuxiaolong.file.directory;import java.io.File;import java.text.SimpleDateFormat;import java.util.Date;/** * Description: * java.io.File * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo1 { public static void main(String[] args) { /* * 尽量不写绝对路径。 * 常用的是相对路径: * 1、相对于项目目录(当前目录) * 2、相对于类加载目录(实际开发更常用) */ File file = new File("." + File.separator + "test.txt"); //分隔符 System.out.println(file); /* * 获取当前文件的属性信息 */ //获取文件名(不包括文件路径) String name = file.getName(); System.out.println("文件名:"+name); //获取文件大小(字节) long length = file.length(); System.out.println("文件大小(字节):"+length); //最后修改时间(毫秒数) long time = file.lastModified(); System.out.println(time); Date date = new Date(); date.setTime(time); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss"); String str = sdf.format(date); System.out.println(str); /* * 可读、可写、可执行、隐藏 */ file.canRead(); file.canWrite(); file.canExecute(); file.isHidden(); System.out.println(file.getPath()); }}
2.创建文件
package com.wuxiaolong.file.directory;import java.io.File;import java.io.IOException;/** * Description: * 使用File创建文件 * * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo2 { public static void main(String[] args) throws IOException { /* * 在当前目录下创建demo.txt文件 ;不能再没有的目录里创建文件,父目录不存在会报错 * 不写“./”就是默认在当前目录 */ File file = new File("." + File.separator + "demo.txt"); /* * 判断file表示的文件或目录是否存在 */ if(! file.exists()){ boolean flag = file.createNewFile(); //没有判断也不会覆盖 System.out.println("创建成功" + flag); }else{ System.out.println("创建失败"); } }}
3.删除文件
package com.wuxiaolong.file.directory;import java.io.File;/** * Description: * 删除文件 * * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo3 { public static void main(String[] args) { /* * 将当前目录中的demo.txt删除; 不会放入回收站 */ File file = new File("demo.txt"); if(file.exists()){ file.delete(); System.out.println("已删除"); }else{ System.out.println("文件不存在"); } }}
4.创建一个目录
package com.wuxiaolong.file.directory;import java.io.File;/** * Description: * 创建一个目录 * * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo4 { public static void main(String[] args) { /* * 在当前目录下创建一级目录demo */ File file = new File("demo"); if(! file.exists()){ file.mkdir(); //mkdir只能创建一级目录,不能创建多级目录,也不报错 System.err.println("创建成功"); }else{ System.out.println("目录已经存在"); } }}
5.创建多级目录
package com.wuxiaolong.file.directory;import java.io.File;/** * Description: * 创建多级目录 * * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo5 { public static void main(String[] args) { /* * 创建多级目录a/b/c/d/e */ File file = new File("a" + File.separator +"b" + File.separator +"c" + File.separator +"d" + File.separator +"e" ); if(! file.exists()){ file.mkdirs(); System.out.println("创建成功"); }else{ System.out.println("目录已存在"); } }}
6.删除目录
package com.wuxiaolong.file.directory;import java.io.File;/** * Description: * 删除目录 * * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo6 { public static void main(String[] args) { File dir = new File("demo"); if(dir.exists()){ dir.delete(); //只能删空目录,非空目录删不掉,也不报错 System.out.println("已删除"); }else{ System.out.println("不存在"); } }}
7.获取一个目录的所有子项
package com.wuxiaolong.file.directory;import java.io.File;/** * Description: * 获取一个目录的所有子项 * * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo7 { public static void main(String[] args) { /* * 获取当前目录中的所有内容 */ File file = new File("."); /* * boolean isDirectory() * 判断是不是目录 */ if(file.isDirectory()){ /* * File[] listFiles() * 查看当前File表示的目录中的所有子项 * 每个子项以一个File对象表示,所有子项存入一个File对象数组,并返回 */ File[] sub = file.listFiles(); for(File f : sub){ System.out.println(f.getName()); } } }}
8.过滤一个目录中的部分子项
package com.wuxiaolong.file.directory;import java.io.File;import java.io.FileFilter;/** * Description: * 获取一个目录中的部分子项 * File支持一个重载的listFile方法,要求传入一个文件过滤器FileFilter(是接口),这样只会返回该目录中满足该过滤器要求的子项 * @author 诸葛小猿 * @date 2020-08-30 */public class FileDemo8 { public static void main(String[] args) { /* * 仅仅获取文件 */ File dir = new File("." ); System.out.println(dir); File[] sub = dir.listFiles( new FileFilter(){//内部类 @Override public boolean accept(File file){ System.out.println("正在过滤:" +file.getName()); //return file1.isFile(); //获取文件,不要目录 //return file1.getName().endsWith(".pom"); //获取以".xml"结尾的文件 return file.getName().startsWith("."); //获取以"."开头的文件 } } ); for(File tmp : sub){ System.out.println(tmp.getName()); } }}
二、java.io.RandomAccessFile
java.io.RandomAccessFile类用于读写文件数据。其基于指针对文件进行读写。由于是基于指针,一个字节一个字节的读写,效率较低,一般用的少。
创建RandomAccessFile有两种方式:
1:“r”,即只读模式,仅仅对文件数据进行读取
2:”rw“,即读写模式,对文件数据可以编辑。
1.一个字节一个字节写文件
package com.wuxiaolong.file.directory;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;/** * Description: * java.io.RandomAccessFile * 用于读写文件数据。一个字节一个字节写文件 * @author 诸葛小猿 * @date 2020-08-30 */public class RandomAccessFileDemo1 { public static void main(String[] args) throws IOException { /* * RandomAccessFile(File f, String mode) * RandomAccessFile(String path, String mode) * 其中mode是操作模式: r rw */ RandomAccessFile raf = new RandomAccessFile("raf.dat", "rw"); /* * void write(int d) * 写出1个字节,写出的是该整数d对应的2进制中的低八位(一个字节8个位) * 00000001 */ int a=1; raf.write(a); //硬盘中存的是二进制 如果文件已经存在,在次运行时 内容从头开始覆盖,后面的不覆盖 a=98; raf.write(a); a=99; raf.write(a); System.out.println("写入硬盘完毕"); /* * 读写完毕后,关闭;防止内存泄漏 */ raf.close(); }}
2.一个字节一个字节读文件
package com.wuxiaolong.file.directory;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;/** * Description: * 一个字节一个字节读文件 * @author 诸葛小猿 * @date 2020-08-30 */public class RandomAccessFileDemo2 { public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile("raf.dat","r"); /* * int read() * 从文件中指针当前的位置读取该字节,并以10进制的数字形式返回 * 若返回值为-1,表示读到了文件的末尾 */ int d = raf.read(); //一次只能读一个字节 System.out.println(d); //1 d = raf.read(); System.out.println(d);//98 d = raf.read(); System.out.println(d);//99 d = raf.read(); System.out.println(d);//-1 存的是-1,返回的是255 存256,返回0 存的是97,98,99,记事本打开abc raf.close(); }}
3.一个字节一个字节复制文件
package com.wuxiaolong.file.directory;import java.io.IOException;import java.io.RandomAccessFile;/** * Description: 复制文件:一次一个字节,慢 * * @author 诸葛小猿 * @date 2020-08-30 */public class CopyDemo1 { public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile("abc.exe","r"); //在硬盘上,一次只能读一个字节,所以慢,40M,三分钟 RandomAccessFile cp = new RandomAccessFile("abc.exe","rw"); long start = System.currentTimeMillis(); int d =-1; //在内存里 while((d=raf.read()) != -1 ){//cpu 判断 速度快 cp.write(d); } long end = System.currentTimeMillis(); System.out.println("复制完成,耗时:" + (end - start) + "ms"); }}
4.自定义缓存复制文件
package com.wuxiaolong.file.directory;import java.io.IOException;import java.io.RandomAccessFile;/** * Description: 若希望提高读写效率们需要提高每一次读写的数据量,从而减少读写次数,最终提高读写效率 * * @author 诸葛小猿 * @date 2020-08-30 */public class CopyDemo2 { public static void main(String[] args) throws IOException { RandomAccessFile src = new RandomAccessFile("firefox.exe","r"); RandomAccessFile aim = new RandomAccessFile("firefox_cp2.exe","rw"); int length = -1;//记录每次读取的实际字节量 byte[] buf = new byte[1024*10]; //一次读取10K long start = System.currentTimeMillis(); /* * int read(byte[] d) * 一次性读取给定长度的字节量,并存入该字节数组中,返回值为实际读取到的字节量。 * 若返回值为-1,则表示读到了文件的末尾(EOF. end of file1) */ while((length=src.read(buf)) != -1){ /* * void write(byte[] d) * 将给定的字节数组一次性复制写入到文件中。 * 这种方式的缺点是:在文件的结尾,最后一次的复制,可能会使文件的大小改变。 */ /* * void write( byte[] d, int offset, int len) * 将给定的字节数组中,从下标为offset处的字节开始连续len个字节一次性复制写到硬盘中。 */ aim.write(buf,0,length); } long end = System.currentTimeMillis(); System.out.println("复制完成。耗时:" +(end - start) + "ms."); }}
5.读写基本类型数据
package com.wuxiaolong.file.file1;import java.io.IOException;import java.io.RandomAccessFile;/** * Description: * RandomAccessFile读写基本类型数据 * * @author 诸葛小猿 * @date 2020-08-30 */public class RandomAccessFileDemo3 { public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile("raf.txt","rw"); raf.writeInt( Integer.MAX_VALUE);//查看源码 一次写四个字节 先无符号右移,在按与运算,掩码运行,截取低八位 raf.writeLong(Long.MAX_VALUE); //一次写八个字节 raf.writeDouble(123.456);//一次写八个字节 System.out.println("完成"); /* * RAF总是在指针当前位置进行读写字节,并且无论进行了读还是写一个字节后,指针都会自动向后移动一个字节的位置。 * 默认创建出来RAF时,指针的位置为0,即:文件第一个字节的位置。 */ long pos = raf.getFilePointer(); //获取文件指针 返回long System.out.println("position:" + pos); //position:20(表示第21个字节所在的位置) 文件开头为0 raf.seek(0); //void seek( long pos) 将文件指针移到固定位置(开头) int value1 = raf.readInt(); //读一个int,并返回 System.out.println(value1); pos = raf.getFilePointer(); System.out.println("pos:" + pos); raf.seek(4); long value2 = raf.readLong();//读一个long,并返回 System.out.println(value2); System.out.println(Long.MAX_VALUE); pos = raf.getFilePointer(); System.out.println("pos:" + pos); raf.seek(12); double value3 = raf.readDouble();//读一个double, 返回 System.out.println(value3); pos = raf.getFilePointer(); System.out.println("pos:" + pos); }}
6.读写字符串
package com.wuxiaolong.file.file1;import java.io.IOException;import java.io.RandomAccessFile;/** * Description: * 读写字符串 * @author 诸葛小猿 * @date 2020-08-30 */public class RandomAccessFileDemo4 { public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile("raf.txt","rw"); String str = "诸葛小猿"; //不同的编码,一个汉字占的字节数不一样 /* * String提供了将当前字符串转化为字节的方法 * byte[] getBytes() 将当前字符串按默认字符集(using the platform's default charset)转换 * byte[] getBytes(String scn) 将当前字符串按指定的字符集转换,字符集不区分大小写 */ byte[] arr = str.getBytes("utf-8"); raf.write(arr); System.out.println("完成"); raf.seek(0); byte[] buf = new byte[40]; int len = raf.read(buf); /* * 将给定字节数组中的指定范围内的字节按照给定的字符集转换为字符串。 */ String s = new String(buf,0,len,"utf-8"); //String s = new String(buf,"GBK"); //字符串结尾有空格, System.out.println(s); raf.close(); }}
三、java.io.FileInputStream和java.io.FileOutputStream
输入流都有一个抽象父类:java.io.InputStream,他是所有字节输入流的父类。FileInputStream是InputStream的子类,是文件字节输入流,是一个低级流(节点流),其使用方式和RandomAccessFile一致。InputStream及其子类只负责读文件,不负责写文件。
输出流都有一个抽象父类:java.io.OutputStream,他是所有字节输出流的父类。FileOutputStream是OutputStream的子类,是文件字节输出流,是一个低级流(节点流),其使用方式和RandomAccessFile一致。OutputStream及其子类只负责写文件,不负责读文件。
1.FileInputStream读文件
package com.wuxiaolong.file.file1;import java.io.FileInputStream;import java.io.IOException;/** * Description: * java.io.FileInputStream * 文件字节输入流,是一个低级流(节点流); * @author 诸葛小猿 * @date 2020-08-30 */public class FISDemo { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("abc.txt"); byte[] data = new byte[100]; int len = fis.read(data); String str = new String(data,0,len,"gbk"); //"gbk"可以不写,这是我的平台默认字符编码集 System.out.println(str); fis.close(); }}
2.FileOutputStream写文件
package com.wuxiaolong.file.file1;import java.io.FileOutputStream;import java.io.IOException;/** * Description: * java.io.FileOutputStream * 文件字节输出流,是一个低级流(数据源明确)(节点流) * @author 诸葛小猿 * @date 2020-08-30 */public class FOSDemo1 { public static void main(String[] args) throws IOException { /* * FileOutputStream fos = new FileOutputStream("abc.txt"); * 默认的构造方法是覆盖写操作,即:如果输出的文件已经存在,会将原文件中的内容清空,然后通过流,从头写入新数据 */ /* * 追加操作,该构造方法需要传入两个参数,后一个参数为boolean值,若该值为true,这追加写操作;该流写入到文件结尾 */ FileOutputStream fos = new FileOutputStream("abc.txt",true); fos.write("诸葛小猿。".getBytes()); System.out.println("完成"); fos.write("你好".getBytes()); //System.out.println("完成"); fos.close(); }}
3.FileInputStream和FileOutputStream拷贝文件
package com.wuxiaolong.file.file1;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;/** * Description: * 使用文件流,完成文件复制操作。方法和RandomAccessFile一致 * @author 诸葛小猿 * @date 2020-08-30 */public class CopyDemo { public static void main(String[] args) throws IOException { /* * 使用FileInputStream读取源文件,使用FileOutputStream向目标文件中写数据。 * 依次从源文件中读取字节,然后写入目标文件,完成复制操作。 */ FileInputStream fis = new FileInputStream("abc.exe"); FileOutputStream fos = new FileOutputStream("abc.exe",true); //再次执行时会追加,文件变大 byte[] b = new byte[1204*10]; int len=-1; while((len=fis.read(b)) != -1){ fos.write(b,0,len); } fis.close(); fos.close(); System.out.println("完成复制"); }}
四、java.io.BufferedInputStream和java.io.BufferedOutputStream
BufferedInputStream和BufferedOutputStream是一对缓冲流,属于高级流(处理流),用于处理低级流(节点流)的数据,使用它们可以提高读写的效率(先将数据写入缓冲区,在写入硬盘,减少了读写次数)。缓冲流单独存在没意义,必须和低级流一起使用。
1.使用缓冲流复制文件
package com.wuxiaolong.file.file1;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;/** * Description: * 缓冲流 BufferedInputStream和BufferedOutputStream是一对缓冲流,属于高级流 * @author 诸葛小猿 * @date 2020-08-30 */public class CopyDemo2 { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("abc.exe"); //低级输入流 BufferedInputStream bis = new BufferedInputStream(fis); //将低级输入流接到高级输入流上 FileOutputStream fos = new FileOutputStream("abc.cp.exe"); //低级输出流 BufferedOutputStream bos = new BufferedOutputStream(fos); //将低级输出流接到高级输出流上 int len = -1; /* * 缓冲流内部维护了一个缓冲区,当我们调用下面的read()方法读取一个字节时, * 实际上缓冲流会让FileInputStream读取一组字节并存入到缓冲流自身内部的字节数组中,然后将第一个字节返回。 * 当我们再次调用read()方法读取一个字节时,缓冲流会直接将数组中的第二个字节返回,以此类推,直到该数组中所有字节都被读取 * 过后才会再次读取一组字节。所以实际上还是通过提高每次读取数据的数量减少读取的次数来提高读取效率。 */ while((len = bis.read()) != -1){ /* * 缓冲输出流也是类似的 */ bos.write(len); } System.out.println("复制完成"); bis.close(); //只需要关高级流(内部会先关闭低级流) bos.close(); }}
2.手动将缓存中的数据刷到磁盘
package com.wuxiaolong.file.file1;import java.io.BufferedOutputStream;import java.io.FileOutputStream;import java.io.IOException;/** * Description: * 缓冲输出流写数据到硬盘中 * @author 诸葛小猿 * @date 2020-08-30 */public class BOSDemo { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("abc.txt"); BufferedOutputStream bos = new BufferedOutputStream(fos); /* * 通过缓冲输出流写出的字节不会立即被写入硬盘,会先存在内存的字节数组,直到该数组满了,才会一次性写出所有的数据。 * 这样做等同于提高了一次性的写入数据量,减少了写的次数,提高效率 */ bos.write("诸葛小猿".getBytes()); //不会及时写入硬盘,在内存中。如果不加colse(),最终被GC干掉,不会写入文件 /* * flush方法强制将缓冲区的数据一次性输出。提高了及时性,但是频繁操作会降低操作效率。 */ bos.flush();//强制及时写入内存,不会等到缓冲区满。 执行次数越多,效率越低 System.out.println("完成"); bos.close();//也调用了flush方法 }}
五、java.io.ObjectInputStream和java.io.ObjectOutputStream
ObjectInputStream和ObjectOutputStream是一对对象流,属于高级流,ObjectInputStream可以读取一组字节转化为java对象,而ObjectOutputStream的作用是可以直接将java中的一个对象转化为一组字节后输出。这其实就是java对象的序列化和反序列化,因此java对象必须要实现序列化接口。
1.定义一个对象
package com.wuxiaolong.file.file1;import lombok.*;import java.io.Serializable;import java.util.List;/** * Description: 该类用于测试作为对象进行对象流的读写操作 * @author 诸葛小猿 * @date 2020-08-30 */@Setter@Getter@ToString@AllArgsConstructor@NoArgsConstructorpublic class Person implements Serializable{ /** * 1.实现Serializable接口。该接口中没有方法,不需要重写方法,这样的接口也叫签名接口。 * * 2.需要有serialVersionUID,序列化的版本号(影响反序列化能否成功)。 * 当一个类实现了Serializable接口后,该类会有一个常量表示这个类的版本号,版本号影响这个对象反序列化的结果。 * 不显式定义serialVersionUID,会默认随机产生一个(根据类的结构由算法产生的,类只要改过,随机产生的就会变化) * 建议自行维护版本号(自定义该常量并给定值)。若不指定,编译器会根据当前类的结构产生一个版本号,类的结构不变则版本号不变,但是结构变了(属性类型、名字变化等),都会导致版本号改变。 * 序列化时,这个版本号会存入到文件中。 * 反序列化对象时,会检查该对象的版本号与当前类现在的版本号是否一致,一致则可以还原,不一致则反序列化失败。 * 版本号一致时,就算反序列化的对象与当前类的结构有出入,也会采取兼容模式,即:任然有的属性就进行还原,没有的属性则被忽略。 */ private static final long serialVersionUID = 1L; private String name; private int age; /* * 3.transient关键字的作用是修饰一个属性。那么当这个类的实例进行序列化时,该属性不会被包含在序列化后的字节中,从而达到了“瘦身”的目的 * 反序列化后是该类型的默认值。引用类型默认是null,其他类型默认是0。如果是静态变量,则映射为内存中的该变量的值。 */ private transient List<String> otherInfo; }
2.对象序列化
package com.wuxiaolong.file.file1;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.util.ArrayList;import java.util.List;/** * Description: * java.io.ObjectOutputStream * 对象输出流,是一个高级流,作用是可以直接将java中的一个对象转化为一组字节后输出,这组字节的输出有OOS维护 * @author 诸葛小猿 * @date 2020-08-30 */public class OOSDemo { public static void main(String[] args) throws IOException { Person p = new Person(); p.setName("诸葛小猿"); p.setAge(30); List<String> otherInfo = new ArrayList<String>(); otherInfo.add("一个彷徨中奋斗的互联网民工"); otherInfo.add("爱好打篮球"); p.setOtherInfo(otherInfo); System.out.println(p); FileOutputStream fos = new FileOutputStream("wuxl.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); /* * ObjectOutputStream的writeObject方法的作用:将给定的java对象转换为一组字节后写到硬盘上, * 这里由于ObjectOutputStream是装在FileOutputStream上的,所以转换的这组字节最终通过FOS写入到文件中。 * * 若希望该对象可以被写出,那么前提是该对象所属的类必须实现Serializable接口 * 实际数据写入文件的信息比对象本身信息多,因为保存了对象的结构信息 * * 该方法涉及到两个操作: * 1:将对象转换为一组字节(称为:对象序列化(编码)) * 2:将该字节写入到文件中(硬盘上)(称为:数据持久化) */ oos.writeObject(p); System.out.println("成功"); oos.close(); }}
3.对象反序列化
package com.wuxiaolong.file.file1;import java.io.FileInputStream;import java.io.IOException;import java.io.ObjectInputStream;/** * Description: * java.io.ObjectInputStream * 对象输入流,是一个高级流,作用:读取一组字节,然后将其还原为描述的对象。 * 需要注意:读取这些字节必须是由ObjectOutputStream将一个对象转换的字节 * @author 诸葛小猿 * @date 2020-08-30 */public class OISDemo { public static void main(String[] args) throws ClassNotFoundException, IOException { FileInputStream fis = new FileInputStream("wuxl.obj"); ObjectInputStream ois = new ObjectInputStream(fis); /* * 将一组字节还原为对象的过程称为:对象的反序列化 * * 反序列化时,wuxl.obj文件中的serialVersionUID要和Person类中的serialVersionUID一致才能成功 * * readObject() 返回的是Object */ Person p = (Person) ois.readObject(); System.out.println(p); ois.close(); }}
六、java.io.InputStreamReader和java.io.OutputStreamWriter
java根据读写数据单位不同,将流分为:字节流与字符流。
字节流的最小读写单位是一个字节,前面介绍的InputStream和OutputStream都属于这一类。
字符流的最小读写单位是一个字符,字符流虽然是以字符为单位读写数据,其底层实际上还是要以字节的形式读写,所以字符流天生就具备将字节转换为字符或字符转换成字节的能力,所以所有的字符流都是高级流,方便我们读写字符数据,无需再关心字符与字节的相互转换。字符流的父类java.io.Reader和java.io.Writer,他们是以char为单位读写,转换为Unicode,他们规定了字符流的基本方法。这里介绍两个常用的字符流java.io.InputStreamReader和java.io.OutputStreamWriter的使用。字符是高级流,也需要和低级流联合使用。
除了InputStreamReader与OutputStreamWriter之外的字符流,大部分都只处理其他字符流。但是低级流都是字节流,这时若希望用一个字符流来处理一个字节流就会产生冲突。所以可以通过创建InputStreamReader与OutputStreamWriter来处理字节流,而InputStreamReader与OutputStreamWriter本身是字符流,所以可以使得其他字符流得以处理该流。这样,InputStreamReader与OutputStreamWriter相当于联系字节流和字符流的纽带,类似转化器的效果,因此这两个流也叫转换流。
1.OutputStreamWriter写文件
package com.wuxiaolong.file.file2;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;/** * Description: * java.io.OutputStreamWriter 字节输出流 * java根据读写数据单位不同,将流分为,字节流与字符流 * 字节流的最小读写单位是一个字节;字符流的最小读写单位是一个字符 * 字符流的父类Reader和Writer.以char为单位读写,转换为Unicode。 * @author 诸葛小猿 * @date 2020-08-30 */public class OSWDeom { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("aa.txt"); /* * OutputStreamWriter的常用构造方法: * 1.OutputStreamWriter(OutputStream out) * * 2.OutputStreamWriter(outputStream out, String csn) * 将给定的字节输出流转换为字节流的同时,指定通过当前字符流写出的字符数据以何种字符集转换为字节。 */ OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8"); osw.write("诸葛小猿"); osw.write("程序员"); String str = "攻城狮"; char[] buf = str.toCharArray(); osw.write(buf,0,buf.length); System.out.println("成功"); osw.close(); }}
2.InputStreamReader读文件
package com.wuxiaolong.file.file2;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;/** * Description: * java.io.InputStreamReader java.io.OutputStreamWriter 字节输入流 * @author 诸葛小猿 * @date 2020-08-30 */public class ISRDemo { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("abc.txt"); /* * 也可以使用一个参数的构造,不加字符集 */ InputStreamReader isr = new InputStreamReader(fis,"GBK"); int len = -1; /* * int read(); * 一次读取一个字符,返回一个该字符编码的int值,若返回值为-1则表示读到末尾 */ while((len = isr.read()) != -1){ // 强转,取低16位 char d = (char) len; System.out.print(d); } }}
七、java.io.BufferedReader和java.io.BufferedWriter
BufferedWriter 和BufferedRead是缓冲字节流,属于高级流,按行读取字符串。由于这两个字符流不能直接处理字节流,所以需要InputStreamReader和OutputStreamWriter这两个转换流做纽带,将低级字节流和BufferedReader、BufferedWriter关联起来。
虽然这两个流读写的速度快,但是没有太多的方法可以使用,所有使用的较少。下面只测试一下java.io.BUfferedReader。
package com.wuxiaolong.file.file2;import java.io.BufferedReader;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;/** * Description: * java.io.BUfferedReader * 缓冲字符输入流,按行读取字符串,不能直接处理字节流,要有转换流 * @author 诸葛小猿 * @date 2020-08-30 */public class BRDemo { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("abc.txt"); //转换流InputStreamReader可以指定字符集 BufferedReader br = new BufferedReader(new InputStreamReader(fis, "GBK")); /* * String readLine() * 连续读取若干字符,直到遇到换行符为止,将换行符之前的所有字符以一个字符串返回(不包括换行符) * 若该方法返回值为null,则表示读取到了末尾(如果一行为空,返回空串“”) * 注意,返回的字符串不包含最后的换行符 */ String line = null; while((line = br.readLine()) != null){ //line中不包含\n System.out.println(line); } br.close(); }}
八、java.io.PrintWriter
缓冲字节流 java.io.PrintWriter,是一种比较常用的输出流。其内部维护了一个缓冲区(字节数组),按行写字符串,写字符效率高。内部自动处理BufferedWriter来完成缓冲操作, 并且PrintWriter具有自动行刷新功能。
1.PrintWriter写文件
package com.wuxiaolong.file.file2;import java.io.FileNotFoundException;import java.io.PrintWriter;import java.io.UnsupportedEncodingException;/** * Description: * 缓冲字节流 java.io.PrintWriter(常用) * 内部维护缓冲区(字节数组),按行读写字符串,读写字符效率高 * 常用的缓冲字符输出流,内部自动处理BufferedWriter来完成缓冲操作, * 并且PrintWriter具有自动行刷新功能。 * @author 诸葛小猿 * @date 2020-08-30 */public class PWDemo1 { public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException { /** * PrintWriter提供了丰富的构造方法 * 其中提供了可以针对文件写出操作的构造方法: * PrintWriter(String path) * PrintWriter(File file) * 是个高级流,不用对接低级流,源码已经使用了低级流,可以不加字符集 */ PrintWriter pw = new PrintWriter("abc.txt","utf-8"); pw.println("诸葛小猿"); pw.println("诸葛小小猿"); pw.println("诸葛小小小猿"); System.out.println("finished!"); pw.close(); //最终的文件大小大于所有的字符之和,因为有换行符 }}
2.PrintWriter处理其他流
package com.wuxiaolong.file.file2;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.io.UnsupportedEncodingException;/** * Description: * PrintWriter处理其他流 * @author 诸葛小猿 * @date 2020-08-30 */public class PWDemo2 { public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException { /* * 向文件pw1.txt中写出内容 */ FileOutputStream fos = new FileOutputStream("abc.txt"); /* * PrintWriter构造传入字节流,不能指定字符集: PrintWriter pw = new PrintWriter(fos); * 若希望指定字符集,需要在中间使用转换流OutputStreamWriter */ OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8"); PrintWriter pw = new PrintWriter(osw); pw.println("诸葛小猿"); pw.println("诸葛小小猿"); System.out.println("成功"); pw.close(); }}
3.PrintWriter自动行刷新
package com.wuxiaolong.file.file2;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.util.Scanner;/** * Description: * 自动行刷新 * 当PrintWriter处理的是一个流时,构造方法允许传入第二个参数,该参数为一个boolean值,当该值为true时,则具有自动行刷新功能, * 即:每当使用println(不是print)方法写出一行字符串时,会自动flush * @author 诸葛小猿 * @date 2020-08-30 */public class PWDemo3 { public static void main(String[] args) throws FileNotFoundException { //true是自动刷新,可以只用一个参数 PrintWriter pw = new PrintWriter(new OutputStreamWriter( new FileOutputStream("noteBook.txt")),true); System.out.println("请输入文件内容:"); Scanner sc = new Scanner(System.in); while(true){ String str = sc.nextLine(); if("exit".equals(str)){ System.out.println("结束"); break; } //具有自动行刷新的pw在使用println方法是会自动flush pw.println(str); } pw.close(); }}
总结:
1.RandomAccessFile可以在任意位置读写。默认情况下,每次读写都会从头开始,写时不会覆盖所有内容,只会覆盖当前位置的内容。基于指针操作,可以在任意位置编辑,但是效率较低。
一个Stream流要么读,要么写。要么覆盖所有内容从头重写,要么在原有基础上追加。
Stream可以分为低级流(节点流)和高级流(处理流);也可以分为字节流、字符流。一般字节流是低级流,字符流是高级流。高级流需要依赖低级流完成读写功能。
InputStreamReader和OutputStreamWriter可以看成是高级流中的低级流,也可以看成是联系高级流和低级流的纽带,他介于高级流和低级流之间,可以看成是转换流。
关注公众号,输入“java-summary”即可获得源码。
完成,收工!
【传播知识,共享价值】,感谢小伙伴们的关注和支持,我是【诸葛小猿】,一个彷徨中奋斗的互联网民工。
版权声明: 本文为 InfoQ 作者【诸葛小猿】的原创文章。
原文链接:【http://xie.infoq.cn/article/0882f298f16c3d9fa6133a950】。文章转载请联系作者。
诸葛小猿
我是诸葛小猿,一个彷徨中奋斗的互联网民工 2020.07.08 加入
公众号:foolish_man_xl
评论