Java开发笔记(八十六)通过缓冲区读写文件
前面介绍了利用文件写入器和文件读取器来读写文件,因为FileWriter与FileReader读写的数据以字符为单位,所以这种读写文件的方式被称作“字符流I/O”,其中字母I代表输入Input,字母O代表输出Output。可是FileWriter的读操作并不高效,缘由在于FileWriter每次调用write方法都会直接写入文件,假如某项业务需要多次调用write方法,那么程序就会写入文件同样次数。因为写文件本质是写磁盘,磁盘的速度远不如内存,所以频繁地写文件必然严重降低程序的运行效率。为此Java又设计了缓存写入器BufferedWriter,它的write方法并不直接写入文件,而是先写入一块缓存,等到缓存写满了再将缓存上的数据写入文件。由于缓存空间位于内存之中,写入缓存等同访问内存,这样相当于把写磁盘动作替换成写内存动作,因此BufferedWriter的整体写文件性能要大大优于FileWriter。除此之外,BufferedWriter还新增了下列几个方法:
newLine:往文件末尾添加换行标记(Window系统是回车加换行)。当然实际上是先往缓存添加换行标记,并非直接往磁盘写入换行标记。
flush:立即将缓冲区中的数据写入磁盘。默认情况要等缓冲区满了才会写入磁盘,或者调用close方法关闭文件之时也会写入磁盘,但是有时程序猴急,一定要立即写入磁盘,此时就需调用flush方法强行写磁盘。
使用缓存写入器之前要先创建文件读取器对象,并获得父类Writer的实例,然后再据此创建缓存写入器对象。下面是通过缓存写入器把多行字符串写入文件的代码例子:
private static String mSrcName = "D:/test/aad.txt";// 使用缓存字符流写入文件private static void writeBuffer() {String str1 = "白日依山尽,黄河入海流。";String str2 = "欲穷千里目,更上一层楼。";File file = new File(mSrcName); // 创建一个指定路径的文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 先创建文件写入器,再根据文件读取器创建缓存写入器try (Writer writer = new FileWriter(file);BufferedWriter bwriter = new BufferedWriter(writer);) {// FileWriter的每次write调用都会直接写入磁盘,不但效率低,性能也差。// BufferedWriter的每次write调用会先写入缓冲区,直到缓冲区满了才写入磁盘,// 缓冲区大小默认是8K,查看源码defaultCharBufferSize = 8192;// 资源释放的close方法再把缓冲区的剩余数据写入磁盘,// 或者中途调用flush方法也可提前将缓冲区的数据写入磁盘。bwriter.write(str1); // 往文件写入字符串bwriter.newLine(); // 另起一行,也就是在文件末尾添加换行标记(Window系统是回车加换行)bwriter.write(str2); // 往文件写入字符串//bwriter.flush(); // 把缓冲区中的数据写入磁盘} catch (Exception e) {e.printStackTrace();}}
既然文件写入器有对应的缓存写入器,那么文件读取器也有对应的缓存读取器BufferedReader。BufferedReader的实现原理与它的兄弟BufferedWriter类似,另外BufferedReader比起文件读取器新增了如下方法:
readLine:从文件中读取一行数据。
mark:在当前位置做个标记。
reset:重置文件指针,令其回到上次标记的位置。也就是回到上次mark方法标记的文件位置。
lines:读取文件内容的所有行,返回的是Stream<String>流对象,之后便可按照流式处理来加工该字符串流。
若想使用缓存读取器,依然要先创建文件读取器,再根据其父类的读取器实例创建缓存读取器。下面是通过缓存读取器从文件中读取多行字符串的代码例子:
// 使用缓存字符流读取文件private static void readBuffer() {File file = new File(mSrcName); // 创建一个指定路径的文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 先创建文件读取器,再根据文件读取器创建缓存读取器try (Reader reader = new FileReader(file);BufferedReader breader = new BufferedReader(reader);) {breader.mark((int) file.length()); // 做个标记for (int i=1; ; i++) {// FileReader只能一个字符一个字符地读,或者一次性读进字符数组。// BufferedReader还支持一行一行地读。String line = breader.readLine(); // 从文件中读出一行文字if (line == null) { // 读到了空指针,表示已经到了文件末尾break;}System.out.println("第"+i+"行的文字为:"+line);}breader.reset(); // 重置文件指针,令其回到上次标记的位置for (int i=1; ; i++) {String line = breader.readLine(); // 从文件中读出一行文字if (line == null) { // 读到了空指针,表示已经到了文件末尾break;}System.out.println("又读了一遍 第"+i+"行的文字为:"+line);}//breader.lines(); // 返回Stream<String>对象,之后可按照流式处理来加工该字符串流} catch (Exception e) {e.printStackTrace();}}
注意到以上代码BufferedWriter和BufferedReader的创建语句都位于try后面的圆括号之中,这是因为Writer与Reader两大家族统统实现了AutoCloseable接口,所以由它们繁衍而来的所有子类都具备自动释放资源的功能。另外,try语句支持同时管理多个资源类,只要它们的对象创建语句以冒号隔开,程序在运行时即可自动回收相关的资源。
结合运用读操作和写操作,可以实现文件复制的功能,无非是一边从源文件中读出数据,另一边紧接着往目标文件写入数据。采用缓存读取器和缓存写入器逐行复制的话,具体的文件复制代码示例如下:
private static String mSrcName = "D:/test/aad.txt";private static String mDestName = "D:/test/aad_copy.txt";// 通过缓存字符流逐行复制文件private static void copyFile() {File src = new File(mSrcName); // 创建一个指定路径的源文件对象File dest = new File(mDestName); // 创建一个指定路径的目标文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 分别创建源文件的缓存读取器,以及目标文件的缓存写入器try (BufferedReader breader = new BufferedReader(new FileReader(src));BufferedWriter bwriter = new BufferedWriter(new FileWriter(dest));) {for (int i=0; ; i++) {String line = breader.readLine(); // 从文件中读出一行文字if (line == null) { // 读到了空指针,表示已经到了文件末尾break;}if (i != 0) { // 第一行开头不用换行bwriter.newLine(); // 另起一行,也就是在文件末尾添加换行标记}bwriter.write(line); // 往文件写入字符串}} catch (Exception e) {e.printStackTrace();}System.out.println("文件复制完成,源文件大小="+src.length()+",新文件大小="+dest.length());}
或者也可逐个字符来复制文件,此时BufferedReader每次调用的read方法只返回整型数表示一个字符,并且BufferedWriter每次调用的write方法也只写入该字符对应的整型数。通过依次遍历源文件的所有字符,同时往目标文件依次写入这些字符,从而完成逐个字符复制文件的操作流程。下面是采取逐字符复制文件的代码例子:
// 通过缓存字符流逐个字符复制文件private static void copyFileByInt() {File src = new File(mSrcName); // 创建一个指定路径的源文件对象File dest = new File(mDestName); // 创建一个指定路径的目标文件对象// try(...)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔// 分别创建源文件的缓存读取器,以及目标文件的缓存写入器try (BufferedReader breader = new BufferedReader(new FileReader(src));BufferedWriter bwriter = new BufferedWriter(new FileWriter(dest));) {while (true) { // 开始遍历文件中的所有字符int temp = breader.read(); // 从源文件中读出一个字符if (temp == -1) { // read方法返回-1表示已经读到了文件末尾break;}bwriter.write(temp); // 往目标文件写入一个字符}} catch (Exception e) {e.printStackTrace();}System.out.println("文件复制完成,源文件大小="+src.length()+",新文件大小="+dest.length());}
需要注意的是,使用字符流复制文件只有逐行复制和逐字符复制两种方式,不可采取整个读到字符数组再整个写入字符数组的方式。之所以不能通过字符数组复制文件,是因为中文跟英文不一样,一个汉字会占用多个字节(GBK编码的每个汉字占用两个字节,UTF8编码的每个汉字占用三个字节)。若要把文件内容读到字符数组,势必先得知晓该数组的长度,可是调用文件对象的length方法只能得到该文件的字节长度,并非字符长度。譬如“白日依山尽”这个字符串在内存中的字符数组长度为5,写到UTF8编码的文件之后,文件大小是5*3=15字节;接着想把文件内容读到字符数组,然而15字节的文件天晓得它有几个字符,可能有5个UTF8编码的中文字符,也可能有15个英文字符,也可能有5个GBK编码的中文字符加5个英文字符共10个字符,总之你根本想不到该分配多大的字符数组。既然确定不了待读取的字符数组长度,就无法一字不差地复制文件内容了。
更多Java技术文章参见《Java开发笔记(序)章节目录》
转载于:https://www.cnblogs.com/pinlantu/p/10726494.html
Java开发笔记(八十六)通过缓冲区读写文件相关推荐
- 【Visual C++】游戏开发笔记四十六 浅墨DirectX教程十四 模板测试与镜面特效专场
本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8632184 作者:毛星云( ...
- 【Visual C++】游戏开发笔记三十六 浅墨DirectX提高班之四 顶点缓存的逆袭
本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8276363 作者:毛星云(浅墨 ...
- Android开发笔记(十六)秋千摇摆动画SwingAnimation
上节博主介绍了AlphaAnimation和淡入淡出动画的使用,其实AlphaAnimation只是四种补间动画中的一种.那么为了加深对其他补间动画的理解,我想说说旋转动画RotateAnimatio ...
- 【Visual C++】游戏开发笔记二十六 DirectX 11各组件的介绍 第一个DirectX 11 Demo的创建
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...
- 【Visual C++】游戏开发笔记二十六 DirectX 11各组件的介绍第一个DirectX 11 Demo的创建
本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://blog.csdn.net/zhmxy555/article/details/7688515 作者:毛星云 邮箱: h ...
- 【Visual C++】游戏开发笔记二十六 DirectX 11各组件的介绍第一个DirectX 11 Demo的创建...
本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://blog.csdn.net/zhmxy555/article/details/7688515 作者:毛星云 邮箱: happ ...
- 易课寄在线购课系统开发笔记(十六)--完成内容服务系统的工程搭建
首页动态展示分析 内容信息要从数据库中获得. 动态展示分析 内容需要进行分类. 分类下有子分类,需要动态管理. 分类下有内容列表. 单点的内容信息: 有图片 有链接 有标题 有价格 包含大文本类型,可 ...
- Java学习笔记(十六)—— 开发个小项目(GoBang4.0)
接十二,今天重点搞定简单基础版本的AI下棋. 画个大纲(跟随慢慢开发过程不断完善) 1.用户 两个用户对战 一黑一白 用户可以是人,也可以是AI.对战模式支持人人,人机,机机. 属性 本次比赛执棋颜 ...
- Java学习笔记二十六:Java多态中的引用类型转换
Java多态中的引用类型转换 引用类型转换: 1.向上类型转换(隐式/自动类型转换),是小类型到大类型的转换: 2.向下类型转换(强制类型转换),是大类型到小类型的转换: 3.instanceof运算 ...
- java中的多态与继承_【Java学习笔记之十六】浅谈Java中的继承与多态
1. 什么是继承,继承的特点? 子类继承父类的特征和行为,使得子类具有父类的各种属性和方法.或子类从父类继承方法,使得子类具有父类相同的行为. 特点:在继承关系中,父类更通用.子类更具体.父类具有更 ...
最新文章
- Actionscript3.0动画编程中的几种特效举例
- android 的命令行工具(dos命令)
- POJ - 4045 Power Station(树形dp/树的重心)
- 在java中使用关键字导入包_java中import关键字的使用方法
- SSL 多线程通信 linux openSSL C API编程
- 太极发送卡片软件_QQ卡片式消息一键发送app
- ElasticSearch配置说明
- Linux_CentOS-服务器搭建 五 补充
- aop springboot 传入参数_springboot用aop做参数校验
- backtrack5 oracle,BackTrack5(BT5)硬盘安装
- 关于EFS加密原理及破解浅谈
- LaTex多张子图并排排列方法
- 三星Q950T全景声回音壁测试心得
- 在控制面板找不到程序的情况下,卸载流氓软件
- 猿人学web端爬虫攻防大赛赛题解析_第九题:js混淆-动态cookie2
- java BigDecimal比较大小
- 谴责那些没有良知的人
- 计算机专业大创要求,计算机学院举行2018年度“大创项目”研究工作推进会
- 新机器通过U盘安装WIN7原版时提示缺少驱动的解决方法
- jfinal hutool工具 excelUtil ZipUtil实现导出excel并压缩文件
热门文章
- intellij idea rearrange code
- 数据库外连接和内连接详解
- 一个程序员的创业失败教训【转自CSDN】
- Swift 语言快速入门
- python进阶03UnboundLocalError和NameError错误
- java 怎么接收xml_Java如何提取完整的XML块
- LinuxCentos7 防火墙开放端口,查看状态,查看开放端口
- oracle update范例,oracle 12c单范例数据库打12.1.0.2.4补丁记录
- 两个单片机iic通讯程序_PIC单片机之DS1302时钟
- 并注册烧写钩子 获取启动介质类型_PyTorch中对张量登记注册反向传播的钩子函数,并展示调用顺序...