Java FileInputStream FileOutputStream разница в прогоне

Может ли кто-нибудь сказать мне, почему 1. запуск неправильный? (Код возврата равен 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();
    }
}

person pojna    schedule 02.02.2021    source источник
comment
Вы читаете байт, чтобы узнать, достигли ли вы конца потока, а затем отбрасываете его. Затем вы читаете второй байт и записываете его. Вы продолжаете повторять это, пока не дойдете до конца потока ... отбросив все остальные байты.   -  person Jon Skeet    schedule 03.02.2021
comment
Очевидно, потому что вы вызываете чтение дважды за итерацию и используете только второе значение   -  person Selvin    schedule 03.02.2021
comment
первый fis.read() в состоянии while навсегда забыт.   -  person aran    schedule 03.02.2021


Ответы (2)


FileInputStream read() метод следует этой логике:

Читает байт данных из этого входного потока. Этот метод блокируется, если ввод еще не доступен.

Итак, присвоив значение его возврата переменной, например:

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

Избегает того, чтобы байт данных, только что прочитанных из потока, был забыт, поскольку каждый вызов read() будет назначен вашей переменной len.


Вместо этого этот код пропускает один из каждых двух байтов из потока, поскольку read(), выполняемый в условии while, никогда не присваивается переменной. Таким образом, поток продвигается без чтения половины байтов (присвоенных 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    
}
person aran    schedule 02.02.2021
comment
Большой! Большое тебе спасибо!! (Теперь я понял, почему у него только половина исходного объема файла.) - person pojna; 03.02.2021
comment
пожалуйста! Дело в том, что каждый вызов read () будет продвигать поток. Рад, что ты смог это исправить :) - person aran; 03.02.2021
comment
Нет, это ты и друзья помогли мне это исправить. (На самом деле я должен был заметить это из-за того, что в результате половина КБ. - person pojna; 03.02.2021
comment
Кстати, в чем разница между write () с одним параметром и с тремя параметрами? byte [] bys = новый байт [1024]; int len; в то время как ((len = fis.read ())! = -1) {fos.write (bys); // fos.write (bys, 0, len); } - person pojna; 03.02.2021
comment
Первый docs. oracle.com/javase/7/docs/api/java/io/ запишет всю длину байта [], переданного в качестве параметра. - person aran; 03.02.2021
comment
Второй docs.oracle.com/javase/7/docs/api/java/io/ запишет количество байтов, указанное параметром len, из байта [], также указав начальное смещение, с которого следует начинать пишу - person aran; 03.02.2021
comment
Имейте в виду, что существует также метод write (int), который также принимает один параметр (это int вместо byte []): docs.oracle.com/javase/7/docs/api/java/io/ - person aran; 03.02.2021
comment
Спасибо! Но, к сожалению, для меня это не совсем так. Первая альтернатива, с одним параметром в write (), кажется бесконечным процессом и не прекращает запись данных, пока диск не заполнится. - person pojna; 03.02.2021
comment
Вы должны отладить это и проверить, достигает ли ваш поток EOF, поскольку он должен прекратить запись после завершения чтения. - person aran; 03.02.2021
comment
Хорошо, спасибо за подсказку! Понятно. Прекрасное время! - person pojna; 03.02.2021
comment
Только что заметил, что вы читаете файлы MP4; Имейте в виду, что вы должны обернуть InputStream в ByteArrayInputStream или аналогичный, чтобы получить правильные результаты. Взгляните сюда: stackoverflow.com/questions/35828216/ Надеюсь, это поможет! - person aran; 03.02.2021
comment
Спасибо еще раз! Думаю, вы исправили суть. Это был пример с jpg, и я пробовал с mp3, а также с mp4, используя FileInputStream / FileOutputStream. Он правильно работает с байтом, но, возможно, не с байтом []. Затем я проверю с помощью ByteArrayOutputStream / ByteArrayInputStream. Спасибо еще раз! - person pojna; 03.02.2021
comment
@pojna более чем приветствую! Да пребудет с вами сила во время кодирования - person aran; 03.02.2021
comment
Ответ @pojna JayC667 лучше, поскольку он дает гораздо больше информации по этой проблеме. Примите это без колебаний! - person aran; 03.02.2021
comment
Ты такой добрый и щедрый. Я сделал и поменял обратно. Друзья познаются в беде. Своевременная помощь, конечно же, стоит также более структурированных подсказок и советов. Кстати, ваш ответ был кратким, что очень ценно для новичков, я просто придерживаюсь этого и благодарю вас обоих, а также всех других добровольцев за ваш вклад! - person pojna; 03.02.2021

@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 - лучший способ избежать проблем с зарезервированными / заблокированными ресурсами, такими как файлы и т. Д.
  • Если вы решаете что-то довольно банальное, я предлагаю вам поместить это в собственный служебный класс или даже в свою собственную библиотеку.
  • Существуют вспомогательные классы, такие как BufferedInputStream и BufferedOutputStream, которые в значительной степени заботятся об эффективности; см. пример copyFileSimpleFaster ().
  • Но, как обычно, больше всего влияет на реализацию качество концепции; см. пример copyFileFast ().
  • Существуют даже более продвинутые концепции (похожие на java.nio), которые учитывают такие концепции, как поведение кэширования ОС и т. Д., Что придаст производительности еще один удар.

Проверьте мои результаты или запустите его самостоятельно, чтобы увидеть разницу в производительности:

Simple copy transferred 1608799 bytes in 12709ms
Simple+Fast copy transferred 1608799 bytes in 51ms
Fast copy transferred 1608799 bytes in 4ms
Test completed.
person JayC667    schedule 02.02.2021
comment
полный ответ на этот вопрос, так что добро пожаловать, чтобы отметить вас принятой галочкой! Этот ответ заслуживает больше, чем мой - person aran; 03.02.2021
comment
Фантастический! Замечательное резюме! Вы правы, цель присвоения - четко определить разницу между byte, byte [] в FileOutputStream, а также в BufferedOutputStream. Взглянем на nio поближе, так как это ново для меня. Большое спасибо и хорошего времени! - person pojna; 03.02.2021