IO之字符流,字节流,转换流

一、概述

把不同类型的输入、输出都抽象为流(Stream)。按流的方向,可分为输入流与输出流。从JDK1.4起,加了 java.nio 包, JDK1.7 作了改进,称nio2
  

字节流的抽象基类:InputStream,OutputStream。
  字符流的抽象基类:Reader,Writer。
  由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。如:InputStream的子类FileInputStream。Reader的子类FileReader。
  如果要操作文字数据,优先考虑字符流。

二、字符流

字符流集成体系简图
这里写图片描述

Reader类

Reader的重要方法是read()
  public int read(); //需要将int转成char
  ppublic int read(char b[]);
  ppublic int read(char[] b, int off, int len);

import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo {
	public static void main(String[] args) throws IOException{
		FileReader fr = new FileReader("demo.txt");
		//方法一:用reader()方法读取字符
		int ch = 0;
		while((ch=fr.read()) != -1){
			System.out.println((char)ch);
		}
		fr.close();
		//方法二:使用read(char[])读取文本文件数据
		//先创建字符数组
		char[] buf = new char[3];
		int len = 0;
		while((len = fr.read(buf)) != -1){
			System.out.println(new String(buf, 0, len));
		}
		fr.close();
	}
}

Writer类

Writer的方法有:
  public void write (int b);// 将参数b的低两字节写入到输出流
  public void write (char b[]);// 将字符数组b[]中的全部字节顺序写入到输出流
  public void write(char[] b, int off, int len);// 将字节数组b[]中从off开始的len个字节写入到流中
  public void write( String s);// 将字符串写入流中
  public void write( String s, int off, int len);// 将字符串写入流中, off为位置,len为长度
  public void flush ();// 刷新流
  public void close();// 关闭流
  IO流的异常处理方式:为防止代码异常导致流无法关闭,因此在finally中对流进行关闭。

import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
	public static void main(String[] args) throws IOException{
		//创建一个可以往文件中写入字符数据的字符输出流对象
		FileWriter fw = null;
		try{
			fw = new FileWriter("demo.txt");
			//调用writer对象中的write(string)方法,将数据写入到临时存储缓冲区中
			fw.write("abcde");
			//刷新,将数据直接写入到目的地中
			fw.flush();
		}catch(IOException e){
			System.out.println(e.toString());
		}finally{
			try{
				//关闭流,关闭资源,在关闭前会先调用flush刷新缓冲中的数据到目的地
				fw.close();
			}catch(IOException e){
				throw new RuntimeException("Failed close");
			}
		}
	}
}

复制文本文件的两种方式:

  1. 使用read()读取文本文件数据。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyTextTest {
	public static void main(String[] args) throws IOException{
		//读取一个已有的文本文件,使用字符读取流和文件相关联
		FileReader fr = new FileReader("demo.txt");
		//创建一个目的,用于存储读到数据
		FileWriter fw = new FileWriter("copyText_1.txt");
		//频繁的读写操作
		int ch = 0;
		while((ch = fr.read())!=-1){
			fw.write(ch);
		}
		//关闭字符流
		fw.close();
		fr.close();
	}
}
  1. 使用read(char[])读取文本文件数据。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyTextTest2 {
	private static final int BUFFER_SIZE = 1024;
	public static void main(String[] args){
		FileReader fr = null;
		FileWriter fw = null;
		try{
			fr = new FileReader("demo.txt");
			fw = new FileWriter("copytest_2.txt");
			//创建一个临时容器,用于缓存读取到的字符
			char[] buf = new char[BUFFER_SIZE];
			//定义一个变量记录读取到的字符数(其实就是往数组里装的字符个数)
			int len = 0;
			while((len = fr.read(buf))!=-1){
				fw.write(buf,0,len);
			}
		}catch(Exception e){
			throw new RuntimeException("读写失败");
		}finally{
			if(fw != null){
				try{
					fw.close();
				}catch(IOException e){
					System.out.println(e.toString());
				}
			}
			if(fr != null){
				try{
					fw.close();
				}catch(IOException e){
					System.out.println(e.toString());
				}
			}
		}
	}
}

字符流的缓冲区
  缓冲区的出现提高了对数据的读写效率。
  作用:在流的基础上对流的功能进行了增强。
  BufferedWriter类,BufferedReader类

/*
BufferWriter示例
*/
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterDemo {
	public static void main(String[] args) throws IOException{
		FileWriter fw = new FileWriter("buf.txt");
		//为了提高写入的效率,使用了字符流的缓冲区
		//创建了一个字符写入流的缓冲区对象,并且指定与要被缓冲的流对象相关联
		BufferedWriter bufw = new BufferedWriter(fw);
		for(int x = 1; x <= 4; x++){
			//使用缓冲区的写入方法将数据先写入到缓冲区中
			bufw.write("abcdef"+x);
			//写入内容换行方法:newLine();
			bufw.newLine();
			bufw.flush();
		}
		//使用缓冲区的刷新方法将数据刷目的地中
		bufw.flush();
		//关闭缓冲区,其实关闭的就是被缓冲的流对象
		fw.close();
	}
}
/*
BufferReader示例
*/
import java.io.BufferedReader;
import java.io.FileReader;
public class BufferedReaderDemo {
	public static void main(String[] args) throws Exception{
		FileReader fr = new FileReader("buf.txt");
		BufferedReader bufr = new BufferedReader(fr);
		String line = null;
		//读取一行数据使用BufferedReader类中的readLine()方法
		while((line = bufr.readLine()) != null){
			System.out.println(line);
		}
		bufr.close();
	}
}
/*
使用缓冲区复制文件
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class CopyTextBufTest {
	public static void main(String[] args) throws Exception{
		FileReader fr = new FileReader("buf.txt");
		BufferedReader bufr = new BufferedReader(fr);
		FileWriter fw = new FileWriter("buf_copy.txt");
		BufferedWriter bufw = new BufferedWriter(fw);
		//方式一
		String line = null;
		while((line = bufr.readLine()) != null){
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}
		/*方式二
		int ch = 0;
		while((ch = bufr.read()) != -1){
			bufw.write(ch);
		}
		*/
		bufr.close();
		bufw.close();
	}
}

LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。

import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class LineNumberReaderDemo {
	public static void main(String[] args) throws IOException{
		FileReader fr = new FileReader("LineNumberReaderDemo.java");
		LineNumberReader lnr = new LineNumberReader(fr);
		String line = null;
		lnr.setLineNumber(100);
		while((line = lnr.readLine()) != null){
			System.out.println(lnr.getLineNumber()+" : "+line);
		}
		lnr.close();
	}
}

装饰设计模式

对原有类进行了功能的改变,增强。

class Person{
	void eat(){
		System.out.println("hava a meal");
	}
}
//采用装饰的方式增强Person类
class NewPerson{
	private Person p;
	NewPerson(Person p){
		this.p = p;
	}
	public void eat(){
		System.out.println("wine");
		p.eat();
		System.out.println("dessert");
	}
}
//采用继承的方式增强Person类
class NewPerson2 extends Person{
	public void eat(){
		System.out.println("wine");
		super.eat();
		System.out.println("dessert");
	}
}
public class PersonDemo {
	public static void main(String[] args){
		Person p = new Person();
		NewPerson np1 = new NewPerson(p);
		np1.eat();
		System.out.println("------------------");
		NewPerson2 np2 = new NewPerson2();
		np2.eat();
	}
}

装饰和继承都能实现一样的特点:进行功能的扩展增强。但装饰比继承灵活.它将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。装饰类和被装饰类都必须所属同一个接口或者父类。

/*
 * 定义一个读取缓冲区类,模拟一个BufferedReader
 */
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
class MyBufferedReader{
	private Reader r;
	//定义一个数组作为缓冲区
	private char[] buf = new char[1024];
	//定义一个指针用于操作这个数组中的元素,当操作到最后一个元素后,指针应该归0
	private int pos = 0;
	//定义一个计数器用于记录缓冲区中的数据个数,当该数据减到0,就从源中继续获取数据到缓冲区中
	private int count = 0;
	MyBufferedReader(Reader r){
		this.r = r;
	}
	//该方法从缓冲区中一次取一个字符
	public int myRead() throws IOException{
		if (count == 0){
			count = r.read(buf);
			//每次获取数据到缓冲区后,角标归0
			pos = 0;
		}
		if (count < 0)
			return -1;
		char ch = buf[pos];
		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();
	}
}
public class MyBufferedReaderDemo {
	public static void main(String[] args) throws IOException{
		FileReader fr = new FileReader("buf.txt");
		MyBufferedReader bufr = new MyBufferedReader(fr);
		String line = null;
		while((line = bufr.myReadLine()) != null){
			System.out.println(line);
		}
		bufr.myClose();
	}
}

三、字节流

字节流集成体系简图
这里写图片描述

字节流不仅可以操作字符,还可以操作其他媒体文件
  InputStream类
  read()方法:逐字节地以二进制的原始方式读取数据
  public int read(); 读入一个字节,-1表示无
  public int read(byte b[]); 返回读入的字节数
  public int read(byte[] b, int off, int len);

OutputStream类
  write()方法
  它的功能是将字节写入流中
  public void write (int b);// 将参数b的低位字节写入到输出流
  public void write (byte b[]);// 将字节数组b[]中的全部字节顺序写入到输出流
  public void write(byte[] b, int off, int len);// 将字节数组b[]中从off开始的len个字节写入到流中
  public void flush (); 刷新缓存,实际写入到文件、网络
  public void close(); 关闭流

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo {
	public static void main(String[] args) throws IOException{
		demo_write();
		demo_read1();
		System.out.println("-----------------");
		demo_read2();
		System.out.println("-----------------");
		demo_read3();
	}
	public static void demo_write() throws IOException{
		//创建字节输出流对象,用于操作文件
		FileOutputStream fos = new FileOutputStream("bytedemo.txt");
		//写数据,直接写入到了目的地中
		fos.write("abcdefg".getBytes());
		//关闭资源
		fos.close();
	}
	//读取方式一
	public static void demo_read1() throws IOException{
		//创建一个读取流对象,和指定文件关联
		FileInputStream fis = new FileInputStream("bytedemo.txt");
		//打印字符字节大小,不过要少用,文件太大会溢出
		byte[] buf = new byte[fis.available()];
		fis.read(buf);
		System.out.println(new String(buf));
		fis.close();
	}
	//读取方式二 —— 推荐使用
	public static void demo_read2() throws IOException{
		FileInputStream fis = new FileInputStream("bytedemo.txt");
		byte[] buf = new byte[1024];
		int len = 0;
		while((len = fis.read(buf)) != -1){
			System.out.println(new String(buf,0,len));
		}
		fis.close();
	}
	//读取方式三
	public static void demo_read3() throws IOException{
		FileInputStream fis = new FileInputStream("bytedemo.txt");
		//一次读取一个字节
		int ch = 0;
		while((ch = fis.read()) != -1){
			System.out.print((char)ch);
		}
		fis.close();
	}
}

FileOutputStream、FileInputStream的flush方法内容为空,没有任何实现,调用没有意义。
  字节流的缓冲区:提高了字节流的读写效率。

/*
 * 通过两种方式对MP3进行拷贝,比较它们的效率
 */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyMp3Test {
	public static void main(String[] args) throws IOException{
		copy_1();
		copy_2();
	}
	//使用字节流原始方式
	public static void copy_1() throws IOException{
		FileInputStream fis = new FileInputStream("0.mp3");
		FileOutputStream fos = new FileOutputStream("1.mp3");
		byte[] buf = new byte[1024];
		int len = 0;
		while((len =  fis.read(buf)) != -1){
			fos.write(buf,0,len);
		}
		fis.close();
		fos.close();
	}
	//使用字节流缓冲区
	public static void copy_2() throws IOException{
		FileInputStream fis = new FileInputStream("0.mp3");
		BufferedInputStream bufis = new BufferedInputStream(fis);
		FileOutputStream fos = new FileOutputStream("1.mp3");
		BufferedOutputStream bufos = new BufferedOutputStream(fos);
		int ch = 0;
		while((ch = bufis.read()) != -1){
			bufos.write(ch);
		}
		bufis.close();
		bufos.close();
	}
}

键盘本身就是一个标准的输入设备。对于java而言,对于这种输入设备都有对应的对象。
  1、获取键盘录入数据,然后将数据流向显示器,那么显示器就是目的地。
  通过System类的setIn,setOut方法可以对默认设备进行改变。
  System.setIn(new FileInputStream(“1.txt”));//将源改成文件1.txt
  System.setOut(new PrintStream(“2.txt”));//将目的改成文件2.txt
  因为字节流处理的是文本数据,可以转换成字符流,操作更方便。
  BfferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
  BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
  2、默认的输入和输出系统不需要关,它会随着系统的结束而消失。

/*
 * 实例:获取用户键盘录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。
 */
import java.io.IOException;
import java.io.InputStream;
public class ReadKey {
	public static void main(String[] args) throws IOException{
		readKey();
	}
	public static void readKey() throws IOException{
		//创建容器
		StringBuilder sb = new StringBuilder();
		//获取键盘读取流
		InputStream in = System.in;
		//定义变量记录读取到的字节,并循环获取
		int ch = 0;
		while ((ch = in.read()) != -1){
			//在存储之前需要判断是否是换行标记
			if (ch == '\r')
				continue;
			if (ch == '\n'){
				String temp = sb.toString();
				if ("over".equals(temp))
					break;
				System.out.println(temp);
				sb.delete(0,sb.length());
			}else{
				//将读取到的字节存储到StringBuilder中
				sb.append((char)ch);
			}
		}
	}
}

四、转换流

转换流——字符流与字节流之间的桥梁。
  应用:字节流中的数据都是字符时,转成字符流操作更高效。
  InputStreamReader:字节到字符的桥梁,解码。
  OutputStreamWriter:字符到字节的桥梁,编码。
  使用场景:
  1)源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁,提高对文本操作的便捷。
  2)操作文本涉及到具体的指定编码表时,必须使用转换流。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
//import java.io.InputStream;
import java.io.InputStreamReader;
//import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class TransStreamDemo {
	public static void main(String[] args) throws IOException{
		/*字节流
		InputStream in = System.in;
		//将字节转成字符
		InputStreamReader isr = new InputStreamReader(in);
		//对字符流进行高效修饰,缓冲区
		BufferedReader bufr = new BufferedReader(isr);
		*/
		//上面三行代码可以简化为:
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		/*字节流
		OutputStream out = System.out;
		//将字符转成字节
		OutputStreamWriter osw = new OutputStreamWriter(out);
		//对字节流进行高效修饰,缓冲区
		BufferedWriter bufw = new BufferedWriter(osw);
		*/
		//上面三行代码可以简化为:
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
		String line = null;
		//读取到了字符串数据
		while ((line = bufr.readLine()) != null){
			if ("over".equals(line))
				break;
			//将字符数据用缓冲区对象写入缓冲区,目的地是osw
			bufw.write(line.toUpperCase());
			bufw.newLine();
			//osw.write(line.toUpperCase() + "\r\n");可以替代上面两行代码
			bufw.flush();
		}
	}
}

流的操作规律:

想要知道对象的开发时用到哪些对象,要明确以下四点:
  1)源和目的
  源:InputStream,Reader 目的:OutputStream,Writer
  2)数据是否是纯文本数据
  是纯文本:Reader,Writer 不是纯文本:InputStream,OutputStream
  3)设备
  硬盘:File 键盘:System.in 控制台:System.out 内存:数组 网络:Socket流
  4)是否需要高效(转换流,缓冲区)

/*
 * 将一个中文字符串数据按照指定的编码表写入到一个文本文件中。
 * 目的:OutputStream,Writer
 * 是纯文本:Writer
 * 设备:硬盘File
 * 任何Java识别的字符数据使用的都是Unicode码表,但是FileWriter写入本地文件使用的是本地编码, 也就是GBK码表。
 * 而OutputStreamWriter可使用指定的编码将要写入流中的字符编码成字节。
 */
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class StreamDemo1 {
	public static void main(String[] args) throws IOException{
		writeText();
	}
	public static void writeText() throws IOException{
		//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"GBK");
		//UTF-8编码,一个中文三个字节。
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
		osw.write("您好");
		osw.close();
		//输出文本文件内容
		InputStreamReader isr = new InputStreamReader(new FileInputStream("d.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();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值