IO字节流

来源:互联网 发布:淘宝买手机 编辑:程序博客网 时间:2024/06/06 02:42

        字节流的两个基类是InputStreamOutputStream,相应的缓冲区是BufferedInputStream和BufferedOutputStream。它的操作与字符流类似,可以参与字符流的定义、读取、写入、处理异常的格式。只不过是处理的数据不同,因为对于非字符的数据,比如图片、视频、音频文件(例如mp3)等,这些文件只能用字节流对之进行操作。

字节流的写操作,不用刷新动作,也能写入目的地,这是为什么?

       字符流底层用也的字节流,因为涉及编码,比如写一个汉字,它有两个字节,你不能读一个字节就写入,而要等到两个字节都读取完后,才写入,所以必须要有flush机制。而字节流,不涉及编码,可以读一个就写一个而不出现问题,所以不用刷新也能写入。

字节流特有的读取方式available()方法可以知道文件的所有字节数,以此可以定义一个刚刚好的字节数组,只用读取一次,然后写入来完成文件复制。但注意:如果字节说过大,会造成内存溢出。


字节流读写示例:

package tan;import java.io.*;class  FileStream{public static void main(String[] args) throws IOException{//writeFile();readFile_3();}//第三种读取方式:字节流特有的读取方式,使用available()方法。//该方法要慎用!!!否则会发生内存溢出public static void readFile_3()throws IOException{FileInputStream fis = new FileInputStream("fos.txt");//int num = fis.available();byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。fis.read(buf);System.out.println(new String(buf));fis.close();}//最佳方法: 第二种读取方式:利用字符数组作为缓冲区,读取。 public static void readFile_2()throws IOException{FileInputStream fis = new FileInputStream("fos.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 readFile_1()throws IOException{FileInputStream fis = new FileInputStream("fos.txt");int ch = 0;while((ch=fis.read())!=-1){System.out.println((char)ch);}fis.close();}//该写方法执行时就会自动创建一个文件public static void writeFile()throws IOException{FileOutputStream fos = new FileOutputStream("fos.txt");//将字符转换成字节,【对字节最小单位操作】无需刷新可直接写入缓冲区fos.write("abcdetan".getBytes());fos.close();}}


练习1:复制一个图片

注意:字符流也可以复制图片,但是它读取一段字节后就会查表进行编码,如果表中有对应的编码值那么没有问题,如果没有对应的编码,则会在编码表的未知区域找一个类似的编码进行替代,这时就改变了原来的字节,导致图片格式不对,复制的图片无法打开。

思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。

package tan;import java.io.*;public class CopyPic {public static void main(String[] args) {FileInputStream fis=null;FileOutputStream fos=null;try {fis=new FileInputStream("3.jpg");fos=new FileOutputStream("bg.jpg");byte[] buff=new byte[1024];int len=0;while((len=fis.read(buff))!=-1){fos.write(buff, 0, len);}} catch (IOException e) {throw new RuntimeException("文件复制失败!");}finally{try {if(fis!=null){fis.close();}} catch (IOException e) {throw new RuntimeException("文件读取关闭失败!");}try {if(fos!=null){fos.close();}} catch (IOException e2) {throw new RuntimeException("文件写入关闭失败!");}}}}

练习二:复制MP3【使用字节缓冲区】

两种方法,一种是java提升的缓冲区,一种是自定义的缓冲区。


复制媒体文件要注意的问题


1.复制MP3因为有可能读取的字节8个1,值为-1,,返回值是int类型,类型提升后结果仍为-1,这会导致数据并未读完,但myRead()结果却为-1,从而使得复制mp3文件失败。为避免这种情况,可以采取这种方法,读取的字节类型提升后前24位补的是1,我们让它补0。怎么补零?类型提升的过程我们是没法控制的,那么可以将提升后的结果&255,结果就转化成最后一个字节不变,而前三个字节位都变成了0,&255后的值作为read方法的返回值。这其实也是为什么read()方法的返回值是int而不是byte的原因。


2.字节写入流的write(intb)方法,在写入时,有个强转(截取)动作,即把最低8位为保留,而把高24位舍弃。


代码示例:

自定义缓冲区读取流:

package tan;import java.io.*;public class MyBufferedInputStream {private InputStream in;private byte[] buf = new byte[1024 * 4];private int pos, count;MyBufferedInputStream(InputStream in) {this.in = in;}// 一次读一个字节,从缓冲区(字节数组)获取。public int myRead() throws IOException {// 通过in对象读取硬盘上数据,并存储buf中。if (count == 0) { // count=0时开始存数据count = in.read(buf);// 记录个数if (count < 0) {return -1;}pos = 0; // 每次取出后pos指针位置要归0byte b = buf[pos]; // 取0角标元素count--; // 取完一次元素减一pos++;return b & 255;} else if (count > 0) { // 取出数据byte b = buf[pos];count--;pos++;return b & 0xff;}return -1;}public void myClose() throws IOException {in.close();}}


MP3文件复制代码:

package tan;import java.io.*;public class CopyMp3 {public static void main(String[] args) throws IOException {long start = System.currentTimeMillis();//copyMp3_1();copyMp3_2();long end = System.currentTimeMillis();System.out.println((end-start)+"毫秒");}   //通过自定义的字节流缓冲区完成复制。public static void copyMp3_2() throws IOException{MyBufferedInputStream bufis=new MyBufferedInputStream(new FileInputStream("爱到花开.mp3"));BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("副本_2.mp3"));int len=0;while((len=bufis.myRead())!=-1){bufos.write(len);}bufos.close();bufis.myClose();}//通过字节流的缓冲区完成复制。public static void copyMp3_1() throws IOException{BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("爱到花开.mp3"));BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("副本_爱到花开.mp3"));int len=0;while((len=bufis.read())!=-1){bufos.write(len);}bufos.close();bufis.close();}}

读取键盘录入
  • System.out:对应的是标准输出设备,控制台。
  • System.in:对应的标准输入设备:键盘。

需求:
     通过键盘录入数据,当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么停止录入。


小问题:

1.为什么它读硬盘上的文件文件时,没有阻塞?

      因为硬盘上的文件就算是空,为0字节,因为文件有结束标记(windows系统给每个磁盘文件都加有结束标记),会返回-1,而不阻塞。 而在dos命令行窗口下,启动后,既没有数据,也没有结束标记,所以一直阻塞,而“ctrl+c”命令,其实就是输入一个结束标记。 

2.为什么dos命令窗口下,只用我敲回车键后,控制台才打印,而不是我录入一个字符它就打印? 
     因为,录入的字符,只有在敲回车键后才写入in关联的流里边,这时流里边才有数据,从而被read()方法读取到。 

3.为什么我敲入一个字符回车后,却打印出来3个数字? 
     因为,windows中,敲回车键,就是加入换行符,而windows中的换行符是用\r\n两个字符表示的,所以会有3个。


程序示例:

package tan;import java.io.*;public class ReadIn {public static void main(String[] args) throws IOException{InputStream in=System.in;StringBuilder sb=new StringBuilder();while(true){//read()是一个阻塞式的方法,它每次读取一个字节,如果没有数据,它会一直等待。 int ch=in.read();if(ch=='\r'){continue;}if(ch=='\n'){String s=sb.toString();if("over".equals(s)){break;}System.out.println(s.toUpperCase());//转为大写sb.delete(0, sb.length());//清空容器}else sb.append((char)ch);}}}



 

0 0
原创粉丝点击