-------- 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解码也不能得到原来的结果了。