黑马程序员_IO流(三)

-------- ASP.Net+Unity开发、.Net培训、期待与您交流! -----------

IO流(三)

PipedOutputStream/PipedIutputStream和PipedWriter/PipedReader(管道流)

前面说到读取流的时候,读取流和写入流之间没有直接的关系,它们之间需要一个数组或者字符串来进行数据之间的传输,管道就解决了读取流和写入流没有直接关系的局面,管道流可以将读取流和写入流接在一起,一边读取,一遍写入。那就要面临一个问题,是读取先还是写入先?由于它们之间的关系就像一根管子,所以要用到多线程执行读取和写,不然会照成死锁(因为read()是一个阻塞方法)。

管道流的一些特性:

可以将连个流直接关联在一起,形如:PipedInputStream(PipedOutputStream src)在管道输入流中通过参数直接关联到管道输出流,也可以通过connect(PipedOutputStream)方法进行连接。下面的实例启用了两个线程来跑管道流的读取和写入,使用connect的方法将两者连接起来,

实例理解:

package cn.itheima.IO;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

class PipedInOut {
	
		/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		
		
		PipedInputStream pis =new PipedInputStream();
		
		PipedOutputStream pos = new PipedOutputStream();
		pis.connect(pos);
		
		
		new Thread(new myread(pis)).start();
		new Thread(new myWriter(pos)).start();
		
		

	}

}
class  myread implements Runnable{
	
	PipedInputStream pis =null;
	myread(PipedInputStream pis){
		this.pis=pis;
		
	}

	@Override
	public void run(){
		// TODO Auto-generated method stub
	
		byte[] b= new byte[1024];
		int len=0;
		try {
			len=pis.read(b);
				System.out.println("数据写入管道");
				System.out.println("等待数据写入。。。。。。");
				System.out.println("收到的数据位:"+(new String(b,0,len)));
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				pis.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException("管道写入流错误");
			}
		}
		
		
		
	}
	
}
class myWriter implements Runnable{

PipedOutputStream pos =null;
myWriter(PipedOutputStream pos){
	this.pos = pos;
}
@Override
public void run() {
	// TODO Auto-generated method stub
	
	byte[] str = "黑马程序员".getBytes();
	
	try {
		
			Thread.sleep(7000);
		
		pos.write(str);
	}catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} 
	catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}finally{
		try {
			pos.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("管道写入流失败");
		}
	}
	
}

}


RandomAccessFile:

随机访问文件的读取,读、取一体,不属于IO类的继承类,直接继承Object类,但是他却是IO包中的成员,因为他具备了读和写的方法,内部封装了一个数组,可以通过指针获取数组里面的值,可以通过可以通过seek改变指针的位置。通过getFielPionter获取指针的位置。其内部完成读写的操作就是底部封装了读取流和写入流的功能,通过构造函数可知,该流对象只能操作文件,而且操作文件还有模式也就是:"r""rw""rws""rwd"写错了就报错。如果模式为"r",会去读一个已经存在的文件,文件不存在报告异常,模式里带有”w“,操作的文件不存在,则自动创建,如果存在,不覆盖

了解该类的方法:

read(int a):获取a的最低8位

readInt(int a):获取a的整数值32位

seek(int a):指针向前移动a个字节,a可为负值

sikpByte(int a):向前跳a个字节,只能向前,不能向后

package cn.itheima.IO;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		myWriter();
		myRead();

	}

	private static void myRead() throws IOException {
		// TODO Auto-generated method stub
		RandomAccessFile raf = new RandomAccessFile("test.txt", "r");
		//跳转指针去读数据
		//raf.seek(8);
		
		try {
	byte[] buf = new byte[4];
	
	int len = raf.read(buf);
	String str = new String(buf,0,len);
	System.out.println(str);
	
	int n = raf.readInt();
	System.out.println(n);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			raf.close();
	
	}

	private static void myWriter() throws FileNotFoundException, IOException {
		RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
		
		raf.write("王五".getBytes());
		raf.writeInt(12);
		raf.write("李四".getBytes());
		raf.writeInt(55);
		raf.close();
	}

}

ByteArrayInputStream/ByteArrayOutputStream

ByteArrayInputStream:其内包括了一个内部缓冲区,关联到源,将源中的数据存入自己的内部缓冲区,关联的是源而已,没有调用底层资源,所以关闭流是无效的(关闭后任然可以调用,也不会产生任何异常)。在构造的时候需要接受数据源,且数据源是一个字节数组,

ByteArrayOutputStream:数据被写入字节数组,缓冲区会随着字节数据的增长而增长,且不用flush,也不涉及底层资源操作,也就不需关闭流。在构造的时候不要定义目的,因为在封装的时候就已近封装了一个可变长的数组,实际上这个数组就是该流的目的。(使用流的思想来操作数组)

实例:

package cn.itheima.IO;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class ByteArrayInOut {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
    ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());
    
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    int len = 0 ;
    while((len=bis.read())!=-1){
    	bos.write(len);
    }
    System.out.println(bos);
	}

}

相对应的有CharArrayReader/CharArrayWriter:操作字符与StringReader/StringWriter操作字符串,不做详细介绍


字节编码:

关于转换流的的字符编码,就是在进行流的操作的时候指定了固定的编码,在编码和解码是也指定了固定的编码表,有什么编码表编的码就得用什么编码表去解,不然就会得到乱码,就那UTF-8和GBK来说,当向转换流中写入数据的时候,指定的编码是UTF-8,但是在编码的时候指定的GBK,则得到的将会是乱码,反之亦然。原因:使用UTF-8编码,一个字符将会有三个字节表示,也就是说在UTF-8中三个字节表示一个字符,但是在GBK中表示一个字符的只是两个字节,解码的时候如果去查GBK,原本在UTF-8中表示一个字符的三个字节在GBK中只需用到两个就可以表示一个字符,GBK码表拿到两个字节去查询GBK码表,得到的将和UTF-8中的自然不一样。所以说使用什么样的编码表去编码就得使用什么样的编码表去解码,以免出现乱码。

下面的实例是使用GBK编码,使用UTF-8解码导致的结果:

package cn.itheima.IO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;



public class UnicodeDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		
	Out();
	In();
	}

	private static void In() throws UnsupportedEncodingException, FileNotFoundException {
		// TODO Auto-generated method stub
		InputStreamReader isr =
				new InputStreamReader(new FileInputStream("test.txt"), "UTF-8");
		char[] buf =new char[50];
		int len =0 ;
		try {
			len = isr.read(buf);
			String str = new String(buf,0,len);
			System.out.println(str);
				
		
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				isr.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException("读取失败");
			}
		}
		
	}

	private static void Out() throws UnsupportedEncodingException,
			FileNotFoundException, IOException {
		OutputStreamWriter osw =
			new	OutputStreamWriter(new FileOutputStream("test.txt"), "GBK");
			osw.write("黑马程序员");
			osw.write(97);
			osw.close();
	}


//运行结果:????????a
}

如果想要得到正确的字符,则需要将解码使用的编码改变成为GBK编码,

注意:

如果在使用GBK编码的,而使用的ISO-8859解码,得到的也是乱码,如果想在不改变编码的情况下得到正确的结果,只需把得到的字符再进行一次ISO-8859编码,最后使用GBK解码即可得到正确的结果,原理:ISO-8859解码是一个字节代表一个字符,也就是说它会把GBK编的码一个一个地去查询ISO-8859编码,当然不会改变这些字节地大小,将他再次进行一编码,得到的字节码还是原来GBK所编的码,使用GBK解码当然能得到正确的结果,但是GBK和UTF-8就不一样,比如说只有一个字符,使用GBK编码就只会产生两个字节,但是使用UTF-8解码就需要三个字节,那么UTF-8就会去查询相似的结果,也就会把字节变为三个,就算再用UTF-8编码,GBk解码也不能得到原来的结果了。




-------- ASP.Net+Unity开发、.Net培训、期待与您交流! -----------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值