Java FileInputStream 与FileOutputStream 的区别运行

Java FileInputStream FileOutputStream difference in the run

有人能告诉我为什么 1.运行 是错误的吗? (return代码为0,但是写入的文件只有原来的一半

提前致谢!

public class FileCopyFisFos {

    public static void main(String[] args) throws IOException {

        FileInputStream fis = new FileInputStream("d:/Test1/OrigFile.MP4");
        FileOutputStream fos = new FileOutputStream("d:/Test2/DestFile.mp4");

// 1. run
//        while (fis.read() != -1){
//            int len = fis.read();
//            fos.write(len);
//        }

// 2. run
//        int len;
//        while ((len = fis.read()) != -1){
//            fos.write(len);
//        }

        fis.close();
        fos.close();
    }
}

FileInputStreamread() 方法遵循以下逻辑:

Reads a byte of data from this input stream. This method blocks if no input is yet available.

所以将其return的值赋给一个变量,如:

while((len = fis.read())!= -1) 

避免忘记刚刚从流中读取的数据字节,因为每次 read() 调用都会分配给您的 len 变量。


相反,此代码绕过流中每两个字节之一,因为在 while 条件下执行的 read() 从未分配给变量。所以流在没有读取一半字节的情况下前进(分配给 len):

while (fis.read() != -1) {      // reads a byte of data (but not saved)
   int len = fis.read();        // next byte of data saved
   fos.write(len);              // possible -1 written here    
}

@aran 和其他人已经指出了您问题的解决方案。

但是还有更多的方面,所以我扩展了你的例子:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopyFisFos {

    public static void main(final String[] args) throws IOException {
        final File src = new File("d:/Test1/OrigFile.MP4");
        final File sink = new File("d:/Test2/DestFile.mp4");

        {
            final long startMS = System.currentTimeMillis();
            final long bytesCopied = copyFileSimple(src, sink);
            System.out.println("Simple copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
        }
        {
            final long startMS = System.currentTimeMillis();
            final long bytesCopied = copyFileSimpleFaster(src, sink);
            System.out.println("Simple+Fast copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
        }
        {
            final long startMS = System.currentTimeMillis();
            final long bytesCopied = copyFileFast(src, sink);
            System.out.println("Fast copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
        }

        System.out.println("Test completed.");
    }

    static public long copyFileSimple(final File pSourceFile, final File pSinkFile) throws IOException {
        try (
                final FileInputStream fis = new FileInputStream(pSourceFile);
                final FileOutputStream fos = new FileOutputStream(pSinkFile);) {

            long totalBytesTransferred = 0;
            while (true) {
                final int readByte = fis.read();
                if (readByte < 0) break;

                fos.write(readByte);
                ++totalBytesTransferred;
            }
            return totalBytesTransferred;
        }
    }

    static public long copyFileSimpleFaster(final File pSourceFile, final File pSinkFile) throws IOException {
        try (
                final FileInputStream fis = new FileInputStream(pSourceFile);
                final FileOutputStream fos = new FileOutputStream(pSinkFile);
                BufferedInputStream bis = new BufferedInputStream(fis);
                BufferedOutputStream bos = new BufferedOutputStream(fos);) {

            long totalBytesTransferred = 0;
            while (true) {
                final int readByte = bis.read();
                if (readByte < 0) break;

                bos.write(readByte);
                ++totalBytesTransferred;
            }
            return totalBytesTransferred;
        }
    }

    static public long copyFileFast(final File pSourceFile, final File pSinkFile) throws IOException {
        try (
                final FileInputStream fis = new FileInputStream(pSourceFile);
                final FileOutputStream fos = new FileOutputStream(pSinkFile);) {

            long totalBytesTransferred = 0;
            final byte[] buffer = new byte[20 * 1024];
            while (true) {
                final int bytesRead = fis.read(buffer);
                if (bytesRead < 0) break;

                fos.write(buffer, 0, bytesRead);
                totalBytesTransferred += bytesRead;
            }
            return totalBytesTransferred;
        }
    }

}

该代码附带的提示:

  • 有一个 java.nio 包,通常可以用更少的代码更快地完成这些事情。
  • 复制单个字节比批量复制慢 1'000-40'000 倍。
  • 使用 try/resource/catch 是避免 reserved/locked 资源(如文件等)出现问题的最佳方法。
  • 如果你解决了一些很常见的问题,我建议你把它放在你自己的实用程序 class 甚至你自己的库中。
  • 有像 BufferedInputStream 和 BufferedOutputStream 这样的助手 class 可以极大地提高效率;参见示例 copyFileSimpleFaster()。
  • 但像往常一样,对实施影响最大的是概念的质量;参见示例 copyFileFast()。
  • 还有更高级的概念(类似于 java.nio),这些概念考虑了 OS 缓存行为等概念,这将提高性能。

检查我的输出,或运行自己检查,以查看性能差异:

Simple copy transferred 1608799 bytes in 12709ms
Simple+Fast copy transferred 1608799 bytes in 51ms
Fast copy transferred 1608799 bytes in 4ms
Test completed.