给定一个Java InputStream,我如何确定流中的当前偏移量?

17

我希望有一个通用且可重复使用的getPosition()方法,它可以告诉我从流的起始点开始已读取的字节数。理想情况下,我希望它适用于所有的InputStream,这样我就不必在从不同来源获取它们时对每个进行包装了。

这样的东西存在吗?如果没有,有人可以推荐一个现有的计数InputStream的实现吗?

4个回答

22

你需要遵循java.io中已经建立的装饰器模式来实现这个功能。

让我们在这里尝试一下:

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public final class PositionInputStream
  extends FilterInputStream
{

  private long pos = 0;

  private long mark = 0;

  public PositionInputStream(InputStream in)
  {
    super(in);
  }

  /**
   * <p>Get the stream position.</p>
   *
   * <p>Eventually, the position will roll over to a negative number.
   * Reading 1 Tb per second, this would occur after approximately three 
   * months. Applications should account for this possibility in their 
   * design.</p>
   *
   * @return the current stream position.
   */
  public synchronized long getPosition()
  {
    return pos;
  }

  @Override
  public synchronized int read()
    throws IOException
  {
    int b = super.read();
    if (b >= 0)
      pos += 1;
    return b;
  }

  @Override
  public synchronized int read(byte[] b, int off, int len)
    throws IOException
  {
    int n = super.read(b, off, len);
    if (n > 0)
      pos += n;
    return n;
  }

  @Override
  public synchronized long skip(long skip)
    throws IOException
  {
    long n = super.skip(skip);
    if (n > 0)
      pos += n;
    return n;
  }

  @Override
  public synchronized void mark(int readlimit)
  {
    super.mark(readlimit);
    mark = pos;
  }

  @Override
  public synchronized void reset()
    throws IOException
  {
    /* A call to reset can still succeed if mark is not supported, but the 
     * resulting stream position is undefined, so it's not allowed here. */
    if (!markSupported())
      throw new IOException("Mark not supported.");
    super.reset();
    pos = mark;
  }

}

InputStreams旨在是线程安全的,因此需要大量使用同步。我曾尝试使用volatileAtomicLong位置变量,但是最好还是使用同步,因为它允许一个线程在不释放锁的情况下操作流并查询其位置。

PositionInputStream is = …
synchronized (is) {
  is.read(buf);
  pos = is.getPosition();
}

装饰器模式是解决这个问题的好方法,建议使用它。另一种选择是删除同步关键字,并记录实现不是线程安全的。调用读取(read())然后检查位置的调用者无论如何都必须同步才能实现可用的(原子)行为。 - volley
我不认为有人知道这个已经存在的实现吗?重新实现一个非常普遍的需求似乎是NIH综合症。 - Chris R
我喜欢这个想法,而且它很容易插入应用程序中。然而,这个类的用户应该知道一个严重的问题:如果缓冲读取器使用这个类,那么报告的位置是错误的,因为数据是按块而不是按字节读取的。 - Asterios
3
报告的位置信息在流中始终是正确的;InputStreamReader 就像任何其他正在读取流的东西一样。您不能指望流跟踪其客户端对其消耗的数据所做的操作(也就是,一个 Reader 读取了多少字节实际上已经被 Reader 检查过)。这种功能必须内置在 Reader 中。 - erickson
每秒读取1 Tb,这将在大约三个月后发生。 - 我的InputStream肯定会持续那么长时间 :P - Twometer
对我来说很好用,而且易于实现,对像我这样的业余爱好者来说非常棒。 - Larphoid

16

请看Commons IO包中的CountingInputStream,他们还有其他很多实用的InputStream变体。



2

InputStream(输入流)不适合处理可能无限的数据,因此计数器会妨碍其使用。除了将它们全部封装起来之外,您还可以通过方面(aspects)来实现某些功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接