从一个文件读取一个字节块并写入其他文件直到读取所有块?

时间:2013-10-17 14:37:13

标签: java file file-io inputstream fileoutputstream

我正在开展一个项目,我必须在其中阅读一些文件阅读写作任务。我必须一次从一个文件中读取8个字节并对该块执行一些操作然后将该块写入第二个文件,然后重复该循环,直到第一个文件在每次8个字节的块中完全读取并且后面操作数据应添加/附加到第二个。但是,这样做,我遇到了一些问题。以下就是我的尝试:

private File readFromFile1(File file1) {

    int offset = 0;
    long message= 0;

    try {
        FileInputStream fis = new FileInputStream(file1);
        byte[] data = new byte[8];
        file2 = new File("file2.txt");
        FileOutputStream fos = new FileOutputStream(file2.getAbsolutePath(), true);
        DataOutputStream dos = new DataOutputStream(fos);

        while(fis.read(data, offset, 8) != -1)
        {
            message = someOperation(data); // operation according to business logic
            dos.writeLong(message);
        }
        fos.close();
        dos.close();
        fis.close(); 
    } catch (IOException e) {
        System.out.println("Some error occurred while reading from File:" + e);
    }
    return file2;
}

我没有以这种方式获得所需的输出。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:0)

请考虑以下代码:

private File readFromFile1(File file1) {

    int offset = 0;
    long message = 0;
    File file2 = null;

    try {
        FileInputStream fis = new FileInputStream(file1);

        byte[] data = new byte[8]; //Read buffer
        byte[] tmpbuf = new byte[8]; //Temporary chunk buffer

        file2 = new File("file2.txt");
        FileOutputStream fos = new FileOutputStream(file2.getAbsolutePath(), true);
        DataOutputStream dos = new DataOutputStream(fos);

        int readcnt; //Read count
        int chunk; //Chunk size to write to tmpbuf

        while ((readcnt = fis.read(data, 0, 8)) != -1) {

            //// POINT A ////
            //Skip chunking system if an 8 byte octet is read directly.
            if(readcnt == 8 && offset == 0){
                message = someOperation(tmpbuf); // operation according to business logic
                dos.writeLong(message);
                continue;
            }

            //// POINT B ////
            chunk = Math.min(tmpbuf.length - offset, readcnt); //Determine how much to add to the temp buf.

            System.arraycopy(data, 0, tmpbuf, offset, chunk); //Copy bytes to temp buf

            offset = offset + chunk; //Sets the offset to temp buf

            if (offset == 8) {
                message = someOperation(tmpbuf); // operation according to business logic
                dos.writeLong(message);

                if (chunk < readcnt) {
                    System.arraycopy(data, chunk, tmpbuf, 0, readcnt - chunk);
                    offset = readcnt - chunk;
                } else {
                    offset = 0;
                }
            }
        }

        //// POINT C ////
        //Process remaining bytes here...
        //message = foo(tmpbuf);
        //dos.writeLong(message);

        fos.close();
        dos.close();
        fis.close(); 
    } catch (IOException e) {
        System.out.println("Some error occurred while reading from File:" + e);
    }

    return file2;
}

在这段代码中,我所做的是:

  1. 修改您的阅读代码,以包含read()方法实际读取的字节数(注明为readcnt)。
  2. 添加了一个字节分块系统(在分块缓冲区中至少有8个字节后才会进行处理。)
  3. 允许单独处理最终字节(不构成8字节八位字节)。
  4. 从代码中可以看出,正在读取的数据首先存储在一个分块缓冲区(表示为tmpbuf)中,直到至少有8个字节可用。只有当8个字节不可用时才会发生这种情况(如果8个字节直接可用且没有任何内容被分块,则直接处理。参见代码中的“A点”)。这是作为一种优化形式完成的,以防止过多的阵列副本。

    分块系统使用偏移量,每次将字节写入tmpbuf时递增,直到达到值8(因为在“块”赋值中使用的Math.min()方法将限制值,因此不会超过此值)。在偏移== 8时,继续执行处理代码。

    如果该特定读取产生的字节数多于实际处理的字节数,则继续从头开始将它们写入tmpbuf,同时适当设置偏移量,否则将偏移量设置为0。

    重复循环。

    代码将保留数据的最后几个字节不适合数组tmpbuf中的八位字节,偏移量变量表示实际写入了多少。然后可以在C点单独处理该数据。

    似乎比它应该更复杂,并且可能有一个更好的解决方案(可能使用现有的java库方法),但是在我的头脑中,这就是我得到的。希望这很清楚,你可以理解。

答案 1 :(得分:-1)

您可以使用以下内容,它使用NIO,尤其是ByteBuffer类来处理long。你当然可以用标准的java方式实现它,但由于我是NIO粉丝,这是一个可能的解决方案。

代码中的主要问题是while(fis.read(data, offset, 8) != -1)将读取最多 8个字节,而不是总是8个字节,加上读取这么小的部分效率不高。

我在我的代码中添加了一些注释,如果不清楚请发表评论。我的someOperation(...)函数只复制缓冲区中的下一个long值。

<强>更新

添加了finally块以关闭文件。

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;

public class TestFile {

  static final int IN_BUFFER_SIZE = 1024 * 8;
  static final int OUT_BUFFER_SIZE = 1024 *9; // make the out-buffer > in-buffer, i am lazy and don't want to check for overruns
  static final int MIN_READ_BYTES = 8;
  static final int MIN_WRITE_BYTES = 8;

  private File readFromFile1(File inFile) {

    final File outFile = new File("file2.txt");

    final ByteBuffer inBuffer = ByteBuffer.allocate(IN_BUFFER_SIZE);
    final ByteBuffer outBuffer = ByteBuffer.allocate(OUT_BUFFER_SIZE);

    FileChannel readChannel = null;
    FileChannel writeChannel = null;
    try {
      // open a file channel for reading and writing
      readChannel = FileChannel.open(inFile.toPath(), StandardOpenOption.READ);
      writeChannel = FileChannel.open(outFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);

      long totalReadByteCount = 0L;
      long totalWriteByteCount = 0L;

      boolean readMore = true;
      while (readMore) {

        // read some bytes into the in-buffer
        int readOp = 0;
        while ((readOp = readChannel.read(inBuffer)) != -1) {
          totalReadByteCount += readOp;
        } // while

        // prepare the in-buffer to be consumed
        inBuffer.flip();

        // check if there where errors
        if (readOp == -1) {
          // end of file reached, read no more
          readMore = false;
        } // if

        // now consume the in-buffer until there are at least MIN_READ_BYTES in the buffer
        while (inBuffer.remaining() >= MIN_READ_BYTES) {
          // add data to the write buffer
          outBuffer.putLong(someOperation(inBuffer));
        } // while

        // compact the in-buffer and prepare for the next read, if we need to read more.
        // that way the possible remaining bytes of the in-buffer can be consumed after leaving the loop
        if (readMore) inBuffer.compact();

        // prepare the out-buffer to be consumed
        outBuffer.flip();

        // write the out-buffer until the buffer is empty
        while (outBuffer.hasRemaining())
          totalWriteByteCount += writeChannel.write(outBuffer);

        // prepare the out-buffer for writing again
        outBuffer.flip();
      } // while

      // error handling
      if (inBuffer.hasRemaining()) {
        System.err.println("Truncated data! Not a long value! bytes remaining: " + inBuffer.remaining());
      } // if

      System.out.println("read total: " + totalReadByteCount + " bytes.");
      System.out.println("write total: " + totalWriteByteCount + " bytes.");

    } catch (IOException e) {
      System.out.println("Some error occurred while reading from File: " + e);
    } finally {
      if (readChannel != null) {
        try {
          readChannel.close();
        } catch (IOException e) {
          System.out.println("Could not close read channel: " + e);
        } // catch
      } // if

      if (writeChannel != null) {
        try {
          writeChannel.close();
        } catch (IOException e) {
          System.out.println("Could not close write channel: " + e);
        } // catch
      } // if
    } // finally

    return outFile;
  }

  private long someOperation(ByteBuffer bb) {
    // consume the buffer, do whatever you want with the buffer.
    return bb.getLong(); // consumes 8 bytes of the buffer.
  }


  public static void main(String[] args) {
    TestFile testFile = new TestFile();
    File source = new File("input.txt");
    testFile.readFromFile1(source);
  }

}