1.输入输出
- 输入流和输出流相对于内存设备而言.
将外设中的数据读取到内存中:输入
将内存的数写入到外设中:输出。
- 字符流的由来:
其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。在对这个文字进行操作。简单说:字节流+编码表
2.字节流和字符流的基类
- 字节流的两个顶层父类:
- InputStream
- OutputStream.
- 字符流的两个顶层父类:
- Reader
- Writer
这些体系的子类都以父类名作为后缀。 而且子类名的前缀就是该对象的功能。
3.字符流
3.1 FilterWriter的使用
- 需求:将一些文字存储到硬盘一个文件中。
public class FileWriterDemo {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
//创建一个可以往文件中写入字符数据的字符输出流对象。
/*
* 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)。
*
* 如果文件不存在,则会自动创建。
* 如果文件存在,则会被覆盖。
*
* 如果构造函数中加入true,可以实现对文件进行续写!
*/
FileWriter fw = new FileWriter("demo.txt",true);
/*
* 调用Writer对象中的write(string)方法,写入数据。
*
* 其实数据写入到临时存储缓冲区中。
*
*/
fw.write("abcde"+LINE_SEPARATOR+"hahaha");
// fw.write("xixi");
/*
* 进行刷新,将数据直接写到目的地中。
*/
// fw.flush();
/*
* 关闭流,关闭资源。在关闭前会先调用flush刷新缓冲中的数据到目的地。
*/
fw.close();
// fw.write("haha");// java.io.IOException: Stream closed
}
}
- 字符输出的 I/O 异常
public class IOExceptionDemo {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("k:\\demo.txt");
fw.write("abcde" + LINE_SEPARATOR + "hahaha");
} catch (IOException e) {
System.out.println(e.toString());
} finally {
if (fw != null)
try {
fw.close();
} catch (IOException e) {
// code....
throw new RuntimeException("¹Ø±Õʧ°Ü");
}
}
}
}
3.2 FilterReader的使用
- 需求:读取一个文本文件。将读取到的字符打印到控制台.
一次读取一个字节
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
//1,创建读取字符数据的流对象。
/*
* 在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。
*
* 用一个读取流关联一个已存在文件。
*/
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch=fr.read())!=-1){
System.out.println((char)ch);
}
fr.close();
}
}
一次读取一个字节数组
public class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据。
*
* 先创建字符数组。
*/
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
-
文本复制案例:
/**
* 文本文件复制:
* 1.一次读取一个字节
* 2.一次读取一个数组
*/
public class CopyTextTest1 {
private static final int BUF_SIZE = 1024;
//文本文件复制:1一次读取一个字节
@Test
public void demo1() throws IOException {
//输入源
FileReader fr=new FileReader("IO_字符文件.txt");
//输出目的
FileWriter fw=new FileWriter("copy_text1.txt");
int ch=0;
while((ch=fr.read())!=-1) {
fw.write(ch);
}
//关流
fr.close();
fw.close();
}
// 2.一次读取一个数组(带异常处理的)
@Test
public void demo2() {
//输入源
FileReader fr=null;
//输出目的
FileWriter fw=null;
try {
fr=new FileReader("IO_字符文件.txt");
fw=new FileWriter("copy_text2.txt");
char[]cbuf=new char[BUF_SIZE];
int len=0;
while((len=fr.read(cbuf))!=-1) {
fw.write(cbuf, 0, len);
}
} catch (Exception e) {
throw new RuntimeException("读写错误");
}finally {
if (fr!=null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw!=null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.3 字符流缓冲区
BufferedWriter |
:newLine(); |
BufferedReader |
: readLine(); |
3.3.1 BufferedWriter :实例
public class BufferedWriterDemo {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("buf.txt");
//为了提高写入的效率。使用了字符流的缓冲区。
//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
BufferedWriter bufw = new BufferedWriter(fw);
//使用缓冲区的写入方法将数据先写入到缓冲区中。
// bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
// bufw.write("xixiixii");
// bufw.newLine();
// bufw.write("heheheheh");
for(int x=1; x<=4; x++){
bufw.write("abcdef"+x);
bufw.newLine();
bufw.flush();
}
//使用缓冲区的刷新方法将数据刷目的地中。
// bufw.flush();
//关闭缓冲区。其实关闭的就是被缓冲的流对象。
bufw.close();
}
}
3.3.2 BufferedReader :实例
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){
System.out.println(line);
}
bufr.close();
}
-
使用缓冲区进行文本复制
/**
* 文本复制:使用缓冲区
*/
public class CopyTestByBuffer {
@Test
public void demo1() throws IOException {
//输入
BufferedReader br=new BufferedReader(new FileReader("IO_字符文件.txt"));
//输出
BufferedWriter bw=new BufferedWriter(new FileWriter("copy_text3.txt"));
String line=null;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();//换行
bw.flush();//刷新
}
//关流
bw.close();
br.close();
}
}
自定义的读取缓冲区
其实就是模拟一个BufferedReader.
* 分析:
* 缓冲区中无非就是封装了一个数组,
* 并对外提供了更多的方法对数组进行访问。
* 其实这些方法最终操作的都是数组的角标。
*
* 缓冲的原理:
* 其实就是从源中获取一批数据装进缓冲区中。
* 在从缓冲区中不断的取出一个一个数据。
*
* 在此次取完后,在从源中继续取一批数据进缓冲区。
* 当源中的数据取光时,用-1作为结束标记。
public class MyBufferedReader extends Reader {
private Reader r;
//定义一个数组作为缓冲区。
private char[] buf = new char[1024];
//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。
private int pos = 0;
//定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
private int count = 0;
MyBufferedReader(Reader r){
this.r = r;
}
/**
* 该方法从缓冲区中一次取一个字符。
* @return
* @throws IOException
*/
public int myRead() throws IOException{
if(count==0){
count = r.read(buf);
pos = 0;
}
if(count<0)
return -1;
char ch = buf[pos++];
count--;
return ch;
}
public String myReadLine() throws IOException{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = myRead())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
//将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void myClose() throws IOException {
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
}
}
4. 字节流
4.1 InputStream
- 使用缓冲区 BufferedInputStream
4.2 OutputStream
- 使用缓冲 BufferedOutputStream
4.3 文件复制案例
4.4 数据回显
需求:从键盘读取数据,转换成大写,打印到控制台,当遇到 over 时停止打印
4.4.1 仅仅使用缓冲区
4.4.2 加上转换流
- InputStreamReader:字节到字符的转换桥梁,解码
- OutputStreamWriter:字符到字节的桥梁,编码。
- 转换流详解图:
5. 流的操作规律
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时用到哪些对象。只要通过四个明确即可。
1,明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer
2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3,明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流
4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。
实例分析:
需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer
3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
=============================================================
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
======================================================================
需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
======================================================================
需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
- 使用指定的码表将文本写入到磁盘中(指定了编码表,一定需要用到转换流)
public static void writeText_2() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_3.txt"),"GBK");
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_3.txt"),"GBK");
// FileWriter fw = new FileWriter("gbk_1.txt");
/*
* 这两句代码的功能是等同的。
* FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
* 简单说:操作文件的字节流+本机默认的编码表。
* 这是按照默认码表来操作文件的便捷类。
*
* 如果操作文本文件需要明确具体的编码。FileWriter就不行了。必须用转换流。
*
*/
osw.write("你好");
osw.close();
}
- 根据指定的编码从文件中读取文本(指定了编码表,一定需要用到转换流)
public static void readText_2() throws IOException, FileNotFoundException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_1.txt"),"utf-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);
需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
什么时候使用转换流呢?
1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。