java io 学习笔记(三) 字符流读写
1、字符流读取
字符流读取的所有类都是从Reader这个超类继承的,都是用于读取字符的,这些类分别是InputSteamReader(从字符流读取)、FileReader(继承与InputStreamReader,读取文件流)StringReader(读取字符串)、PipedReader(读取管道,管道的上端来自于一个PipedWriter)、CharArrayReader(读取字符数组),还有两个比较特殊的类,一个是FileterReader,这是个抽象类,目前只有PushbackReader类实现了它,从名字就可以看出PushbackReader类允许把读出来的数据推回流中(但能不能推到源中,还有待于验证);一个是BufferedReader,这个是一个为了提高字符流读取的性能而提供的带有缓存功能的类,是一个代理类,能代理前面提到的所有类,也就是说,它可以读取所有来源的数据。
InputStreamReader和FileReader
这个类的字符流是从字节流读取字节数据,并且把字节数据转化为字符的,也就是说字符流的读操作其实是基于字节流读操作之上的,内部调取了字节流的读操作。所以很重要的两个传入参数就是字节流和字符编码集。因为文件操作很重用,所有从InputStreamReader里面有专门派生出了一个FileReader,FileReader因为读取的是文件,字符流可以用文件或者文件名代替,这样源就是且只能是一个文件了。
这个类与其他读字符流读取类最大的区别,就是它没有mark、skip、reset等和读取位置有关的方法,这是因为他是从流中读取字节数据的,因此没有办法对流进行标记,只能老老实实重头到尾按顺序取字节流。所以他只有close,read,ready,getEncoding四个函数
PushbackReader
这个类主要是提供了unread()操作,可以把字符写回。
PipedReader
这个类从和它相连(snc.connect(src)的PipedWriter中读取字节流。
BufferedReader
这个类主要是提供一个其它类的代理功能,并且提供一个缓存,尤其是用在InputStream时,更能提高效率,因为他可以提前把字节流读取到缓存中,所以其他类一般都不会直接用,而是用BufferedReader包装一下再用。特别需要注意的是,这个类没有提供直接读取字节流的构造函数,也就是说它必须从字符流中读取数据。与它对应的Buffer额度Writer也是这样,只能把字符写到字符流中,而不能直接写到字节流中去。也就是说,它没有提供字符到字节的转码功能。
注意:为了优化性能,所有的数据字符流读取类从字节流中读取数据时不是一个一个字符的数据量读的,而是一下读取很多数据,然后把这些数据放在缓存里面,再根据需求从缓存里面把字符读出来。所以,当有两个字符流读取对象对同一个block字节流进行读取时,虽然前一个字符流只读取了一个字符出来,但是因为字节的预读取,所以后一个对象可能就无法从字节流中读取数据了,如果此时后面这个对象的缓存里面也没有数据,它就会一直卡在那里,等待字节流的数据。
1 public class InputStreamIO {
2 public static void main(String args[]) {
3 try {
4 int count = 0;
5 InputStreamReader inputStreamReader = new InputStreamReader(System.in);
6 PushbackReader pushbackReader = new PushbackReader(new InputStreamReader(System.in));
7 System.out.println("the ecoding of inputStreamReader is :" + inputStreamReader.getEncoding());
8 char mychar;
9 do {
10 mychar = (char)inputStreamReader.read();
11 // pushbackReader.unread(mychar);
12 System.out.println("char from inputStreadReader:"+mychar);
13 System.out.println("char from pushbackReader:"+(char)pushbackReader.read());
14 } while (mychar!='q');
15 }
16 catch (IOException e){
17 System.out.println(e.getMessage());
18 e.printStackTrace();
19 }
20 }
21 }
上面的代码是从标准输入流中读取数据的,结果如下:
the ecoding of inputStreamReader is :GBK char from inputStreadReader:1 char from pushbackReader:1
char from inputStreadReader:2
char from pushbackReader:2
char from inputStreadReader:3
char from pushbackReader: char from inputStreadReader:4
上面的结果中,当输入123456之后,inputStreadReader读取了一个字符到mychar中,虽然只读取了一个字符,但是它把标准输入流中的所有数据都读完了,存在它的缓存里面,后面的pushbackReader因为自己的缓存里面没有数据,所以它试图从标准输入中获取字节,但此时标准输入中所有的字节都被前面的inputStreadReader读取完了,标准输入流里已经没有数据了,所以后面的pushbackReader就只能一直等在那里。
第二次输入12之后,标准输入流里面就有了“12\n“(注意,因为标准输入流以换行符提交刷新,即flush,所以最后肯定会有一个换行符),这是pushbackReader便有了字节可以读取,它一下子把“12\n”读取完了,存在缓存里,并且从缓存里读取出1,被打印了出来
之后循环继续,到InputStreamReader时,它的缓存里面还有"23456\n",所以它不需硬要从标准输入流中读如字节数据,所以它不会卡在那里,他顺利的读取了2;同样,后面的pushbackReader它也还有“2\n”,它把2读了出来了。这样顺利的循环下去,知道pushbackReader读取我它缓存里的最后一个数据“\n”之后,它又卡在了那里。
2、字符流写
字符流写就是把字符写到相应的目的地,并且和字符流读是对应的,基本上就是把原来的xxxReader变成xxxWriter,然后功能就由读变成了写,源也就变成了对应的目的地。如果目的地是字符流(这种情况只有OutputStreamWriter以及它的子类FileWriter),则会把字符自动编码成字节后再写入流当中。
PrintWriter这个类比较特殊,下面重点说说它。
PrintWriter
PrintWriter其实是OutputStreamWriter和FileWriter的增强版,它包括了格式化输出,按行输出以及自动添加换行符等功能(println)。另外有一点需要注意,它添加的换行符是和底层操作系统有关的,在Windows下,它添加的是回车换行符”\r\n“,在GNU下,他添加的是换行符“\n“。PrintWriter还能自动刷新缓存,每当调用了println, printf, 或者format中的任何一个函数之后,就自动flush。
我们还将马上看到,在字节流输出中,也有一个和PrintWriter比较像的叫PrintStream的对象,它也实现了把字符输出到字节流并提供编码功能的方法(所以说它比较特殊),但是它的换行符总是“\n”,且遇到“\n”就调用flush。
3、重点分析分析PipedReader和PipedWriter
PipedReader和PipedWriter是连起来用的,用于线程间的IO通信。PipedWriter是PipedReader的src(源),PipedReader是PipedWriter的snk(目标),他们通过二者任何一个connect函数连接,也可以在通过构造函数参数建立连接,并且src和snk是一对一的关系,否则会抛出IOException("Already connected")
异常。
其实src和snk通信挺简单的,就是src的writer
向snk的buffer里面写数据,具体的方式这是调用snk的receive
函数把字符传给snk的buffer。下面重点看看PipedReader的receive
函数的源码:
/**
* Receives a char of data. This method will block if no input is
* available.
*/
synchronized void receive(int c) throws IOException {
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByWriter || closedByReader) {
throw new IOException("Pipe closed");
} else if (readSide != null && !readSide.isAlive()) {
throw new IOException("Read end dead");
} writeSide = Thread.currentThread();
while (in == out) {
if ((readSide != null) && !readSide.isAlive()) {
throw new IOException("Pipe broken");
}
/* full: kick any waiting readers */
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
/* 说明此PipedReader空了,此时把in和out都归位到0*/
if (in < 0) {
in = 0;
out = 0;
}
buffer[in++] = (char) c;
if (in >= buffer.length) {
in = 0;
}
}
还有PipedReader的的read
函数的源码:
public synchronized int read() throws IOException {
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByReader) {
throw new IOException("Pipe closed");
} else if (writeSide != null && !writeSide.isAlive()
&& !closedByWriter && (in < 0)) {
throw new IOException("Write end dead");
} readSide = Thread.currentThread();
int trials = 2;
while (in < 0) {
if (closedByWriter) {
/* closed by writer, return EOF */
return -1;
}
if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
throw new IOException("Pipe broken");
}
/* might be a writer waiting */
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
int ret = buffer[out++];
if (out >= buffer.length) {
out = 0;
}
if (in == out) {
/* now empty */
in = -1;
}
return ret;
}
可以看到,PipedReader是通过in和out两个指针来确定写入和读出的,如果有数据且不满,那么in!=out;当in==out且不等于-1时,表示buffer满了;当in=-1时,表示buffer空了。buffer是一个首尾相接的数组,从out到in是缓存的数据。从receive函数的源码中可以很容易地看出,当buffer满了(in==out且in>0),receive函数会唤醒其它线程,并等待1000ms让其它线程(读线程)有机会把数据读走。并且注意到,receive函数是同步的,也就是说只能有有个线程在写数据,并且可以肯定的是,这个线程是个写数据线程(snk的receive函数一般只由src的write函数调用,因为receive函数的访问权限是默认访问权限,只有同一个包小面的类才能访问它)。
另外,从PipedReader的read函数也可以看出,当buffer空了的时候(in<0),他也会唤醒其它线程,并且等待1000ms以期待其它线程写入数据到buffer。
java io 学习笔记(三) 字符流读写的更多相关文章
- Java IO学习笔记三
Java IO学习笔记三 在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在了一组字节流-字符流的转换类. OutputStreamWriter:是Writer的子类,将输出的 ...
- Java IO学习笔记三:MMAP与RandomAccessFile
作者:Grey 原文地址:Java IO学习笔记三:MMAP与RandomAccessFile 关于RandomAccessFile 相较于前面提到的BufferedReader/Writer和Fil ...
- Java IO学习笔记:概念与原理
Java IO学习笔记:概念与原理 一.概念 Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...
- Java IO学习笔记二
Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...
- Java IO学习笔记总结
Java IO学习笔记总结 前言 前面的八篇文章详细的讲述了Java IO的操作方法,文章列表如下 基本的文件操作 字符流和字节流的操作 InputStreamReader和OutputStreamW ...
- Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer
作者:Grey 原文地址:Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer ByteBuffer.allocate()与ByteBuffer.allocateD ...
- Java IO学习笔记五:BIO到NIO
作者:Grey 原文地址: Java IO学习笔记五:BIO到NIO 准备环境 准备一个CentOS7的Linux实例: 实例的IP: 192.168.205.138 我们这次实验的目的就是直观感受一 ...
- Java IO学习笔记四:Socket基础
作者:Grey 原文地址:Java IO学习笔记四:Socket基础 准备两个Linux实例(安装好jdk1.8),我准备的两个实例的ip地址分别为: io1实例:192.168.205.138 io ...
- Java IO学习笔记六:NIO到多路复用
作者:Grey 原文地址:Java IO学习笔记六:NIO到多路复用 虽然NIO性能上比BIO要好,参考:Java IO学习笔记五:BIO到NIO 但是NIO也有问题,NIO服务端的示例代码中往往会包 ...
- Java IO学习笔记七:多路复用从单线程到多线程
作者:Grey 原文地址:Java IO学习笔记七:多路复用从单线程到多线程 在前面提到的多路复用的服务端代码中, 我们在处理读数据的同时,也处理了写事件: public void readHandl ...
随机推荐
- 【转】开源视频录制库LandscapeVideoCamera
非常强大的android 视频录制库,可以选择视频尺寸以及视频质量,只允许横屏录制. 使用Android自带的Camera应用可以录制视频,只需发送MediaStore.ACTION_VIDEO_CA ...
- Vue axios发送Http请求
axios 1.cnpm install axios --save 2.在vue文件中引入,import Axios from 'axios' 3.使用,Axios.get(url).then((re ...
- 严选 Android 路由框架优化(下篇)
3 router 框架优化 3.1 apt 生成代码量过大问题优化 思考框架本身,其实可以发现仅有 router 映射表是需要根据注解编译生成的,其他的全部代码都是固定代码,完全可以 sdk 中直接编 ...
- .properties文件的使用
在很多项目中我们都会使用到.properties文件对我们的项目进行配置,今天就介绍一下.properties文件在项目中的使用: 如下图,我们项目中有一个名为project.properties的p ...
- ubuntu 16.4安装toolsbelt heroku
https://devcenter.heroku.com/articles/getting-started-with-python#set-up # Run this from your termin ...
- 老男孩Day9作业:高级FTP
一.作业需求 1. 用户加密认证(已完成) 2. 多用户同时登陆(已完成) 3. 每个用户有自己的家目录且只能访问自己的家目录(已完成) 4. 对用户进行磁盘配额.不同用户配额可不同(已完成) 5. ...
- SDUT OJ 数据结构实验之串一:KMP简单应用 && 浅谈对看毛片算法的理解
数据结构实验之串一:KMP简单应用 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Descr ...
- Domoticz 中添加彩云天气
前言 用过一段时间的彩云天气 APP,最吸引我的地方是精确到局部区域的天气预测,虽然准确度并不算高,但是对于预测下雨还是不错的选择.在 Domoticz 中添加彩云天气的数据,利用的是彩云天气提供的 ...
- tornado 03 请求与响应
tornado 03 请求与响应 一.请求与响应 浏览器与服务器之间沟通的到底是什么信息 #服务器在后台一直保持运行着 #浏览器通过URL(路由.地址)发送请求 #服务器接收请求了通过tornado处 ...
- git 下载 安装
1.下载Git,官网地址:https://git-scm.com/,进入官网首页 在右下方的显示器中找到最新的版本下载,点击下载,跳转到下载页面 下载完成 2.安装Git 双击刚刚下载完成的安装文件, ...