java IO FileInputStream与FileOutputStream

FileInputStream :用于读取原始字节流,入图像数据。如果读取字符流,请考虑使用FileReader

public class FileInputStream extends InputStream 

继承了静态类 InputStream

public abstract class InputStream implements Closeable {

    private static final int MAX_SKIP_BUFFER_SIZE = 2048;

    public abstract int read() throws IOException;

    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }

        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }

        return n - remaining;
    }

    public int available() throws IOException {
        return 0;
    }
    public void close() throws IOException {}

    public synchronized void mark(int readlimit) {}

    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    public boolean markSupported() {
        return false;
    }

}

这也说明了静态类不一定只有静态方法 (笑)

构造方法有

//输入相关的文件名
//如果文件不存在,是一个目录而不是一个正常的文件,亦或是因为某些原因文件不能被打开,就会抛出异常
//return :创建一一个被打开文件的字节流
public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null); 
    } 
//输入文件
//异常同上
//return :创建一一个被打开文件的字节流
 public FileInputStream(File file) throws FileNotFoundException {
        String name = (file != null ? file.getPath() : null); //获取文件的路径
        SecurityManager security = System.getSecurityManager();//启动java安全管理器
        if (security != null) {
            security.checkRead(name); //检查文件的权限,是否允许阅读。不能的话抛出异常
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) { //检查文件是否包含无效路径
            throw new FileNotFoundException("Invalid file path");
        }
        fd = new FileDescriptor();
        fd.attach(this); //关联FileInputStream实例与FileDescriptor实例
        path = name;
        open(name); //打开文件进行读取
    }

public FileInputStream(FileDescriptor fdObj) {
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
        if (security != null) {
            security.checkRead(fdObj);
        }
        fd = fdObj;
        path = null;

        /*
         * FileDescriptor is being shared by streams.
         * Register this stream with FileDescriptor tracker.
         */
        fd.attach(this);
    }
//读取b.length字节的数据流入一个字节数组 b
//byte b[] 就是一个缓冲区
//return : 每一次读入缓冲区的总字节数,如果到达结尾后,返回-1
 public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }
// off b的起始偏移量
// len 每一次读取多少
// 每一次读入缓冲区的总字节数,如果到达结尾后,返回-1
public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }

 //native  表示调用的方法在外部定义
 private native int readBytes(byte b[], int off, int len) throws IOException;
@Test
    public void ioTest() throws IOException {
		InputStream inputStream = new FileInputStream(new File("C:\\file\\hello.txt"));
		byte[] b = new byte[4];
		int len = 0;
		while((len = inputStream.read(b))!= -1) {
			System.out.println(len);
		}
    }

// 4 4 4 4 2 每一次读取4个 

//如果改成这样,则会抛出异常
while((len = inputStream.read(b,1,4))!= -1) { 
			System.out.println(len);
		}

//如果这样的话,每一次读取两个
while((len = inputStream.read(b,1,2))!= -1) {
			System.out.println(len);
		}
// 2 2 2 2 2 2 2 2 2

//返回可以被此输入流读取的剩余字节数的估计值
public native int available() throws IOException;
//跳过并丢失流中的n节字节数据
public native long skip(long n) throws IOException;

 @Test
    public void ioTest() throws IOException {
		InputStream inputStream = new FileInputStream(new File("C:\\file\\hello.txt"));
		System.out.println(inputStream.available());
		inputStream.skip(3);
		System.out.println(inputStream.available());
    }

// 18 15

下面是关于FileOutputStream

是一个将数据写入文件的的输出流

public class FileOutputStream extends OutputStream

下面是它的构造函数

//创建一个写入指定文件的输出流
//如果文件是一个目录,或不存在但不能被创建,或者是因为某些原因不能被打开,则抛出异常
//如果有安全管理,那么需要check文件是否允许被写入


//name : 文件的路径+文件的名字+后缀
//可以清楚的看见,这个构造函数使用了File
public FileOutputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null, false);
    }

//append: 如果为true,将写入文件的末尾而不是开头
public FileOutputStream(String name, boolean append)
        throws FileNotFoundException
    {
        this(name != null ? new File(name) : null, append);
    }


public FileOutputStream(File file) throws FileNotFoundException {
        this(file, false);
    }

//说到底,前边三个构造函数最终都是调用这个构造函数
 public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        this.fd = new FileDescriptor();
        fd.attach(this);
        this.append = append;
        this.path = name;

        open(name, append);
    }

public FileOutputStream(FileDescriptor fdObj) {
        SecurityManager security = System.getSecurityManager();
        if (fdObj == null) {
            throw new NullPointerException();
        }
        if (security != null) {
            security.checkWrite(fdObj);
        }
        this.fd = fdObj;
        this.append = false;
        this.path = null;

        fd.attach(this);
    }

FileDescriptor 是“文件描述符”。
FileDescriptor 可以被用来表示开放文件、开放套接字等。
以FileDescriptor表示文件来说:当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作。

//将一个字节写入输出流中
public void write(int b) throws IOException {
        write(b, append);
    }

//从指定的b中获取b.length个字节写入输出流中
public void write(byte b[]) throws IOException {
        writeBytes(b, 0, b.length, append);
    }
//从指定的b中偏移量off快开始,获取len个字节写入输出流中
public void write(byte b[], int off, int len) throws IOException {
        writeBytes(b, off, len, append);
    }
@Test
    public void ioTest() throws IOException {
		InputStream inputStream = new FileInputStream("C:\\file\\hello.txt");
		OutputStream outputStream = new FileOutputStream("C:\\file\\hello1.txt",true);
		byte[] b = new byte[3];
		int len = 0;
		while((len = inputStream.read(b))!= -1) {
			outputStream.write(b);
		}
		outputStream.close();
		inputStream.close();
		
    }

但这样有时候会出问题,就是当你每次读取的字节数不能被问文件的总字节数整除的时候,就会多写入一些字节

根据输出的结果,我推测是因为每一次使用read的时候,并不是一次性覆盖b。而是一个一个的覆盖,先覆盖第一个,然后第二个。。。等到文件结尾的时候,就不覆盖了。所以,容易写入上一次没有覆的字节

解决方法之一,还是用这些 

byte[] b = new byte[inputStream.available()];

猜你喜欢

转载自blog.csdn.net/qq_35815781/article/details/85088157