I/O流


什么是流?

流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是 Internet 上的某个 URL。流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能往输入流写,而不能读它。实际上,流的源端和目的端可简单地看成是字节的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。
对流形象的比喻——水流 ,文件======程序 ,文件和程序之间连接一个管道,水流就在之间形成了,自然也就出现了方向:可以流进,也可以流出.便于理解,于是可以这么形象的定义流: 流就是一个管道里面有流水,这个管道连接了文件和程序。

常用流的分类:
在java中流可以按多种方式分类,

1.按流的方向分为:输入流和输出流

输入流:如:inputstream  reader用于读取数据

输出流:如:outputstream  writer用于写入数据

2.按流的数据单位不同分为:字节流和字符流

字节流:如:inputstream   outputsteam用于操作字节级别的数据

字符流:如:writer  reader用于操作字符级别的数据

3.按流的功能不同分为:节点流和处理流(包装流)和转换流

节点流:如:Fileinputstream  Fileoutputstream  Filereader   FileWriter用于操作硬盘等设备上的文件

包装流:如:BufferedWriter  BufferredReader 等用于操作其他流对象的流进行功能的增强和效率的提高

转换流:如:intputstreamreader   outputstreamwriter 用于字符流字节流之间的转换以便于操作数据

流的使用:

输入输出流

1,明确操作的是字节级数据还是字符级数据
如果是字符级数据就用Writer  Reader体系
如果是字节级数据就用inputstream  outputstream体系

2,明确操作的源或目的是文件还是系统输入
如果是文件就用加前缀File
如果不是文件就不加File

eg1:读取一个硬盘上的文本文件并打印出来
明确是操作字符级数据并且操作的是硬盘上的文件夹所以使用FileReader如下:

class IoDemo{
	public static void main(String[] args) throws IOException{
		//创建一个文件读取流,并与指定文件相关联。
		//要保证该文件是已经存在的,如果存在,会发生异常FileNotFoundException
		FileReader fr = new FileReader("demo.txt");
		int ch;
		while((ch = fr.read())!= -1){  //注意read返回的是ASCLL码(int)
			System.out.println((char)ch);
		}
	}
}

如果demo.txt文件过大那么这个程序就要进行多次循环,导致程序的效率较低于是进行了以下改进方案:

class IoDemo{
	public static void main(String[] args) throws IOException{
		FileReader fr = new FileReader("IoDemo.java");
		char[] buf = new char[1024];
		int num;
		while((num = fr.read(buf))!= -1){
			System.out.println(new String(buf,0,num));
		}
	}
}
eg2:将c盘的一张图片复制到d盘下
明确操作的是字节级的数据并且是操作硬盘上的文件所以使用FileInputstream和FileOutputStream
class IoDemo{
	public static void main(String[] args)throws IOException{

		FileInputStream fis = new FileInputStream("c:\\1.jpg");
		FileOutputStream fos = new FileOutputStream("d:\\1.jpg");
		
		byte[] buf = new byte[1024];
		int len;
		while((len = fis.read(buf)) != -1){
			fos.write(buf,0,len);
		}
		fis.close();
		fos.close();
	}
}
eg3:键盘输入的字母大写输出
明确操作的是字节级的数据并且不是文件所以使用inputsteam
class IoDemo{
	public static void main(String[] args)throws IOException{
		InputStream in = System.in;
		StringBuilder sb = new StringBuilder();
		
		while(true){
			int ch = in.read();
			if(ch == 13)
				continue;
			if(ch == 10){
				String s = sb.toString();
				if("over".equals(s))
					break;
				System.out.println(s.toUpperCase());
				sb.delete(0,sb.length());
			}
			else
				sb.append((char)ch);
				
		}
	}
}

缓冲流BuffredReader  BufferedWriter

缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象
BufferedWriter缓冲区中提供了一个跨平台的换行方法newline()
BufferedReader缓冲区中提供了读行方法:readline()读到行尾返回null;
eg:

class IoDemo{
	public static void main(String[] args) throws IOException{
		FileWriter fw = new FileWriter("a.txt");
		FileReader fr = new FileReader("a.txt");
		BufferedWriter bufw = new BufferedWriter(fw);
		BufferedReader bufr = new BufferedReader(fr);
		for(int i = 0;i<4;i++){
			bufw.write("kch"+i);
			bufw.newLine();
		}
		bufw.close();//此时关掉的就是缓冲区中的流对象,不用再写fw.close()
	
		String s;
		while((s = bufr.readLine()) != null){
			System.out.println(s);
		}
		bufr.close();
	}
}
我们可以看到不管是Bufferedreader还是BufferedWriter都是在增强的其操作的流对象的功能,提高程序的效率。那么我们可以联想到自己定义一个buffered来实现自己想增强的功能

自定义buffered
eg:自定义一个和bufferedreader功能相似的buffered理解bufferedreader的读行操作原理

class MyBufferedReader{
	private FileReader r;
	MyBufferedReader(FileReader r){
		this.r = r;
	}
	public String myReadLine() throws IOException{
		StringBuilder sb = new StringBuilder();
		int ch;
		while((ch = r.read())!= -1){
			if((char)ch == '\r')
				continue;
			if((char)ch == '\n')
				return sb.toString();
			else
				sb.append((char)ch);
		}
		if(sb.length()!=0)  //没有这句就会读到最后一句但是不返回最后一句
			return sb.toString();
		return null;
	}
	public void myClose() throws IOException{
		r.close();
	}
}
class IoDemo{
	public static void main(String[] args){
		MyBufferedReader mybuf = null;
		try{
			mybuf = new MyBufferedReader(new FileReader("a.txt"));
			String line;
			while((line = mybuf.myReadLine())!= null){
				System.out.println(line);
			}
		}catch(IOException e){
			throw new RuntimeException("读取失败");
		}finally{
			try{
				if(mybuf != null)
					mybuf.myClose();
			}catch(IOException e){
				throw new RuntimeException("关闭失败");
			}
		}
	}
}

转换流
1,为了操作数据方便有时需要使用转换流
比如:System.in 键盘输入,对应的是字节流InputStream。为了操作键盘的文件数据方便,转成字符流按字符串操作是最方便的,所以System.in转成Reader.
InputStreamReader isr = new InputStreamReader(System.in);,
2,想要把录入的数据按照指定的编码表编码并将将数据保存到文件中,或者要读入指定编码的文件时需要转换流,只有转换流可以指定编码,改转换流对象要就收一个字节输出流(只有字节级数据可以重新按指定编码类型编码)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"))
eg:

class IoDemo{
	public static void main(String[] args)throws IOException{
		//获取键盘录入对象。
		InputStream in = System.in;
		//将字节流对象转换成字符流对象,使用交换流inputstreamreader
		InputStreamReader isr = new InputStreamReader(in);
<span style="white-space:pre">		</span>//使用Buffered增强功能
		BufferedReader bufr = new BufferedReader(isr);
		//将键盘输入的数据按指定的编码格式写到文件中
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),"utf-8"));
		String line = null;
		while((line = bufr.readLine())!=null){
			if("over".equals(line))
				break;
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}
		
	}
}

其他常用的流:

1,PrintWriter
打印流,可以操作字节和字符级的流,如果构造函数中有true,则println  print  format支持自动刷新可以简化代码书写

eg:

import java.io.*;  
public class Test {  
    public static void main(String[] args) throws IOException{  
        BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));  
        PrintWriter out=new PrintWriter(new FileWriter("123.txt"),true);  
        String line=null;  
        while((line=bufr.readLine())!=null){  
            out.println(line);//本方法带有换行功能  此时不需要手动刷新  
            if("over".equals(line))  
                break;  
        }  
        bufr.close();  
        out.close();  
    }  
}  

2,sequenceinputstream
合并流,将多个文件合并到一个流中去
eg:

import java.io.*;
import java.util.*;
public class Test {
	public static void main(String[] args) throws IOException{
		//创建Vector集合
		Vector<FileInputStream> v = new Vector<FileInputStream>();
		//为集合添加流
		v.add(new FileInputStream("1.txt"));
		v.add(new FileInputStream("2.txt"));
		v.add(new FileInputStream("3.txt"));
		//创建集合的迭代
		Enumeration<FileInputStream> e = v.elements();
		
		SequenceInputStream sis = new SequenceInputStream(e);
		FileOutputStream fos = new FileOutputStream("4.txt");
		byte[] buf = new byte[1024];
		int len =0;
		while((len=sis.read(buf))!=-1)
		{
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();
	}
}

3,管道流
PipedOutputStream:管道输出流。
PipedInputStream:管道输入流。

方法:
连接管道输入流和输出流
connect(PipedOutputStream pos)
connect(PipedInputStream pis)
管道流通常与多线程结合,数据由某个线程从PipedInputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream对象。不建议对这两个对象使用单个线程,因为这样可能造成死锁。

eg:

import java.io.*;
public class Test {
	public static void main(String[] args) throws IOException, ClassNotFoundException{
		PipedInputStream pis = new PipedInputStream();
		PipedOutputStream pos = new PipedOutputStream();
		//连接管道输出流和输入流
		pis.connect(pos);
		Read r = new Read(pis);
		Write w = new Write(pos);
		//创建多线程
		Thread t1 = new Thread(r);
		Thread t2 = new Thread(w);
		t1.start();
		t2.start();
	}
}
//将PipedInputStream封装到线程
class Read implements Runnable{
	private PipedInputStream pis;
	Read(PipedInputStream pis){
		this.pis=pis;
	}
	public void run(){
		
		try {
			System.out.println("waiting for read data");
			byte[] buf=new byte[1024];
			int len;
			while((len=pis.read(buf))!=-1){
				System.out.println("read data done");
				System.out.println(new String(buf));
			}
		pis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
//将PipedOutputStream封装到线程
class Write implements Runnable{
	private PipedOutputStream pos;
	Write(PipedOutputStream pos){
		this.pos=pos;
	}
	public void run(){
		
		try {
			System.out.println("please waiting");
			Thread.sleep(3000);
			String str="demo.java";
			pos.write(str.getBytes());
			pos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
4,RandomAccessFile类
该类不算是IO体系中的子类,而是直接继承Object,但它是IO包成员,因为它具备读写功能,内部封装了一个数组,且通过指针对数组的元素进行操作,同时可通过seek改变指针的位置。该类支持对随机访问文件的读写。自身具备读写方法。随机访问文件的行为类似存储在文件系统中的一个大型byte数组。存在指向该隐含数组的光标或索引,称为文件指针,该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用。输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。
构造函数;
RandomAccessFile(File file,String mode):
RandomAccessFile(String name,String mode):
创建从中读取和向其中写入(可选)的随机访问文件流;
注:
如果模式为r(只读),则实例化对象时不会创建文件,会去读取一个已经寻在的文件,如果该文件不存在,则会包异常。
如果模式为rw,而构造函数中文件不存在,会走动创建,如果该文件已存在,则不会覆盖。
特有方法;
getFilePointer(); 获取指针位置
seek(long pos):设置指针位置
skipBytes(int s):指针跳过s个直接位置。如果是为负数,不跳过任何字节,即该方法只会向后跳跃。
eg:
public class Test {
	public static void main(String[] args) throws IOException, ClassNotFoundException{
		RandomAccessFile raf = new RandomAccessFile("123.txt", "rw");
		
		//获取指针位置
		long pointer = raf.getFilePointer();
		System.out.println("pointer:"+pointer);

		//设置指针位置
		raf.seek(0);
		int num = raf.readInt();
		System.out.println("num:"+num);

		//跳过2个字节
		raf.skipBytes(2);
		System.out.println((char)raf.read());
		raf.close();
	}
}

I/O流的应用

学习玩i/o流后写了下面3个生活中常见的用到了流技术的实例:

实例1:日志文件
当程序发生异常我们要记录并保存异常的信息到文件中,而不是仅仅在控制台上显示

import java.text.*;
class IoDemo{
	public static void main(String[] args)throws IOException{
		try{
			int[] arr = new int [2];
			System.out.println(arr[3]);
		}catch(Exception e){
			Date d = new Date();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");
			String s = sdf.format(d);
			PrintStream ps = new PrintStream("a.txt");
			ps.println(s);
			System.setOut(ps);
			e.printStackTrace(System.out);
		}
	}
}

运行结果:


实例2:搜索文件目录
但我们要想知道某个目录下的所有某种文件的所有路径,不如d:\\demo目录下所用的java文件
class IoDemo{
	public static void main(String[] args){
		File dir = new File("d:\\demo");
		List list = new ArrayList<File>();
		fileToList(dir,list);
		File file = new File(dir,"javapath.txt");
		writeToFile(list,file);
	}
	public static void fileToList(File dir,List<File> list){
		File[] files = dir.listFiles();
		for(File file : files){
			if(file.isDirectory())
				fileToList(file,list);
			else{
				if(file.getName().endsWith(".java"))
					list.add(file);
			}
		}
	}
	public static void writeToFile(List<File> list,File file){
		BufferedWriter bufw = null;
		try{
			bufw = new BufferedWriter(new FileWriter(file));
			for(File f : list){
				String path = f.getAbsolutePath();
				bufw.write(path);
				bufw.newLine();
				bufw.flush();
			}
		}catch(IOException e){
			System.out.println("fause");
		}
		finally{
			try{
				if(bufw != null)
					bufw.close();
			}catch(IOException e){
				System.out.println("closefause");
			}
		}
	}
}

运行结果:


实例3::软件注册配置文件
我们辛苦开发的一个软件想要别人试用5次后就缴费可以利用配置文件来做
class IoDemo{
	public static void main(String[] args)throws IOException{
		Properties prop = new Properties();

		File file = new File("count.ini");
		if(!file.exists())
			file.createNewFile();
		FileInputStream fis = new FileInputStream(file);
		prop.load(fis);
		String value = prop.getProperty("time");
		int count = 0;
		if(value != null){
			count = Integer.parseInt(value);
			if(count >= 3){
				System.out.println("please pay for this soft!");
				return;
			}
		}
		count++;
		prop.setProperty("time",count+"");
		FileOutputStream fos = new FileOutputStream(file);
		prop.store(fos,"");
		fos.close();
		fis.close();
	}
}

运行结果:






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值