您的位置:首页 > 其它

IO流三:字节流文件读写操作、拷贝图片、带缓冲区的字节流、read()原理、读取键盘录入

2014-03-22 11:42 489 查看

1  字节流文件读写操作

字符流:FileReader、BufferedReader

    FileWriter、BufferedWriter

字节流:InputStream :读

        OutputStream :写

字符流操作字符数据,多用于文本文件。字节流操作字节数据,例如图片文件等。

字符流读入字符数组char[],字节流读入字节数组byte[]。

代码示例:

import java.io.*;

class FileStreamDemo {
public static void main(String[] args) throws IOException{
writeFile();
readFile_1();
readFile_2();
readFile_3();
}

public static void writeFile() throws IOException{
FileOutputStream fos = new FileOutputStream("fos.txt");//文件不存在则创建。

//String类的getBytes()方法获取字节数组。
fos.write("FileStream".getBytes());

/* 	字节流的写入不需要刷新。字符流需要刷新是因为,先逐个字节的将数据从流对象写入到一个缓冲区,
再查表,有匹配的字符时,再刷新写入目的地。 */

fos.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 readFile_2() throws IOException{
FileInputStream fis = new FileInputStream("fos.txt");

byte[] buf = new byte[1024];
int len = 0;

while((len=fis.read(buf))!=-1){ //读取到buf数组中
System.out.println(new String(buf,0,len));
}
fis.close();
}

public static void readFile_3() throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");

//int num = fis.available();
//available方法获取流对象中的字节数,回车符'\r'和换行符'\n'各占一个。

byte[] buf = new byte[fis.available()]; //定义一个刚刚好的缓冲区,不用再循环了。
//但是如果这个数组长度过长,内存就溢出了。
fis.read(buf);
System.out.println(new String(buf));

fis.close();
}
}


2  拷贝图片

复制一个图片文件,用到的流对象:FileInputStream、FileOutputStream。

 

思路:

1,字节流读取对象InputStream和图片文件相关联。

2,用字节流输出对象OutputStream创建一个文件,用于存储读取到底的图片数据。

3,通过循环读写,完成数据的存储。

代码示例:

import java.io.*;

class CopyPictureDemo{
public static void main(String[] args){
FileInputStream fis = null;
FileOutputStream fos = null;

try{
fis = new FileInputStream("e:\\EVE.jpg"); //读
fos = new FileOutputStream("e:\\EVE_副本.jpg"); //写

byte[] buf = new byte[1024];
int len = 0;

while((len=fis.read(buf))!=-1){ //读取到数组中
fos.write(buf,0,len);
}
}
catch(IOException e){
System.out.println(e.toString());
}
finally{
try{
if(fis!=null)
fis.close();
}
catch(IOException e){
System.out.println(e.toString());
}
try{
if(fos!=null)
fos.close();
}
catch(IOException e){
System.out.println(e.toString());
}
}
}
}


3  带缓冲区的字节流:复制MP3文件

通过复制MP3文件,演示带缓冲区的字节流:BufferedInputStream、BufferedOutputStream。

代码示例:

import java.io.*;

class CopyMp3Demo {
public static void main(String[] args) throws IOException{
long start = System.currentTimeMillis(); //获取时间
copy_1();
long end = System.currentTimeMillis(); //获取时间

System.out.println((end-start)+"毫秒"); //执行时间
}

//通过字节流的缓冲区完成复制
public static void copy_1() throws IOException{
BufferedInputStream bufis =
new BufferedInputStream(new FileInputStream("e:\\GetUp.mp3"));

BufferedOutputStream bufos =
new BufferedOutputStream(new FileOutputStream("e:\\GetUp-副本.mp3"));

byte[] bt = new byte[1024];
int len = 0;

while((len=bufis.read(bt))!=-1){
bufos.write(bt,0,len);
}
bufis.close();
bufos.close();
}

//MyBufferedInputStream 的演示。
public static void copy_2() throws IOException{
MyBufferedInputStream bufis =
new MyBufferedInputStream(new FileInputStream("e:\\GetUp.mp3"));
BufferedOutputStream bufos =
new BufferedOutputStream(new FileOutputStream("e:\\GetUp-副本.mp3"));

int by = 0;

while((by=bufis.myRead())!=-1){
bufos.write(by);
}
bufis.myClose();
bufos.close();
}
}


4  自定义带缓冲区的字节流和read()方法的原理

上一节代码CopyMp3Demo中copy_2的演示。

代码示例:

import java.io.*;

class MyBufferedInputStream {
private InputStream in = null;
private byte[] buf = new byte[1024];

//pos指针从数组中取元素,count计数器存储数组中的元素个数。
private int pos = 0, count = 0;

MyBufferedInputStream(InputStream in){
this.in = in;
}

//一次读一个字节,从缓冲区(字节数组)获取。
public int myRead() throws IOException{

//通过in对象读取硬盘上的数据,并存储到buf中。
if(count==0){
count = in.read(buf);
if(count<0)
return -1;
pos = 0;
byte b = buf[pos];

count--;
pos++;
return b&255; //从byte类型提升到int类型
}
else if(count>0){
byte b = buf[pos];

count--;
pos++;
return b&255; //从byte类型提升到int类型
}
return -1;
}
public void myClose() throws IOException{
in.close();
}
}


-1的二进制表示:补码表示

0000 0001

1111 1110

1111 1111   -1       读到8个1,就返回了-1,所以没有执行read方法。

 

为什么read()返回是int类型,而不是byte类型?

因为返回byte类型的话,遇到连续8个1时,返回-1,误以为是结束标记,就不执行whlie循环。

提升到int类型后,32个1才表示“-1”,才是结束标记。

byte: -1 ----> int: -1     //一个int类型是32个二进制位,而一个字节8个二进制位,所以被提升了。

 11111111 11111111 11111111 11111111 -1   //提升时若要还是-1,应在前面补1。

---> 00000000 00000000 00000000 11111111    255  //但为了避免遇到8个1返回-1,被误以为是结束标记。

                                               //应该在前面补0。

 

11111111   --->提升了一个int类型,那不还是-1吗?是-1的原因是因为在8个1前面补的是1导致的。

那么我只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现。

 

怎么补0呢?

  11111111 11111111 11111111 11111111

& 00000000 00000000 00000000 11111111     255    //和255做与运算,补0.

--------------------------------------------

  00000000 00000000 00000000 11111111

 

 

事实上,read方法返回int类型时,进行补0的操作,

而write方法先进行去0的操作,再把11111111写入目的地,最终写入的数据还是这一个字节11111111。

 

5  读取键盘录入

System.out:对应标准的输出设备,控制台。

System.in:对应标准的输入设备,键盘。

 

需求:

1,通过键盘读取录入。

2,当录入一行数据后,就将该行数据进行打印。

3,如果录入“over”的话,那么停止录入。

其实就是带缓冲区的IO流中readLine()的原理。

 

代码示例:

import java.io.*;

class ReadInDemo{
public static void main(String[] args) throws IOException{
InputStream in = System.in; //System.in对象,读取键盘录入。
StringBuilder sb = new StringBuilder();

while(true){
int ch = in.read();
if(ch=='\r')
continue;
if(ch=='\n'){ //读到换行符,就打印缓冲区中的数据
String s = sb.toString();
if(s.equals("over"))
break;
System.out.println(s);
sb.delete(0,sb.length()); //清空缓冲区。
}
else
sb.append((char)ch);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐