Java:ASCII随机行文件访问状态

Java:ASCII随机行文件访问状态,java,io,Java,Io,有没有比创建满足以下标准的流文件读取器类更好的[预先存在的可选Java 1.6]解决方案 给定一个任意大的ASCII文件,其中每一行都以\n 对于某些方法的每次调用readLine()从文件中随机读取一行 在文件句柄的生命周期中,对readLine()的调用不应返回同一行两次 更新: 所有的行最终都必须被读取 上下文:文件的内容是从unixshell命令创建的,以获取给定目录中包含的所有路径的目录列表;有数百万到十亿个文件(在目标文件中产生数百万到十亿行)。如果有某种方法可以在创建期间将

有没有比创建满足以下标准的流文件读取器类更好的[预先存在的可选Java 1.6]解决方案

  • 给定一个任意大的ASCII文件,其中每一行都以
    \n
  • 对于某些方法的每次调用
    readLine()
    从文件中随机读取一行
  • 在文件句柄的生命周期中,对
    readLine()
    的调用不应返回同一行两次
更新:

  • 所有的行最终都必须被读取

上下文:文件的内容是从unixshell命令创建的,以获取给定目录中包含的所有路径的目录列表;有数百万到十亿个文件(在目标文件中产生数百万到十亿行)。如果有某种方法可以在创建期间将路径随机分配到文件中,这也是一种可接受的解决方案。

预处理输入文件并记住每一新行的偏移量。使用
位集
跟踪使用的行。如果你想节省一些内存,那么记住每16行的偏移量;跳入文件并在16行的块中进行顺序查找仍然很容易。

因为您可以填充行,我会沿着这些行做一些事情,而且您还应该注意,即使这样,
列表
实际上可以容纳的内容也可能存在限制

每次要读取行时使用随机数并将其添加到
集合
也可以,但是这可以确保文件完全读取:

public class VeryLargeFileReading
    implements Iterator<String>, Closeable
{
    private static Random RND = new Random();
    // List of all indices
    final List<Long> indices = new ArrayList<Long>();
    final RandomAccessFile fd;

    public VeryLargeFileReading(String fileName, long lineSize)
    {
        fd = new RandomAccessFile(fileName);
        long nrLines = fd.length() / lineSize;
        for (long i = 0; i < nrLines; i++)
            indices.add(i * lineSize);
        Collections.shuffle(indices);
    }

    // Iterator methods
    @Override
    public boolean hasNext()
    {
        return !indices.isEmpty();
    }

    @Override
    public void remove()
    {
        // Nope
        throw new IllegalStateException();
    }

    @Override
    public String next()
    {
        final long offset = indices.remove(0);
        fd.seek(offset);
        return fd.readLine().trim();
    }

    @Override
    public void close() throws IOException
    {
        fd.close();
    }
}
公共类VeryLargeFileReading
实现迭代器,可关闭
{
私有静态随机RND=新随机();
//所有索引列表
最终列表索引=新的ArrayList();
最终随机访问文件fd;
public VeryLargeFileReading(字符串文件名,长行大小)
{
fd=新的随机访问文件(文件名);
长nrLines=fd.length()/lineSize;
对于(长i=0;i
为了避免读取整个文件(在您的情况下可能不可能),您可能需要使用一个而不是标准的java
文件输入流。使用
RandomAccessFile
,您可以使用
seek(long position)
方法跳到文件中的任意位置并开始读取。代码看起来像这样

RandomAccessFile raf = new RandomAccessFile("path-to-file","rw");
HashMap<Integer,String> sampledLines = new HashMap<Integer,String>();
for(int i = 0; i < numberOfRandomSamples; i++)
{
    //seek to a random point in the file
    raf.seek((long)(Math.random()*raf.length()));

    //skip from the random location to the beginning of the next line
    int nextByte = raf.read();
    while(((char)nextByte) != '\n')
    {
        if(nextByte == -1) raf.seek(0);//wrap around to the beginning of the file if you reach the end
        nextByte = raf.read();
    }

    //read the line into a buffer
    StringBuffer lineBuffer = new StringBuffer();
    nextByte = raf.read();
    while(nextByte != -1 && (((char)nextByte) != '\n'))
        lineBuffer.append((char)nextByte);

    //ensure uniqueness
    String line = lineBuffer.toString();
    if(sampledLines.get(line.hashCode()) != null)
        i--;
    else
       sampledLines.put(line.hashCode(),line);
}
RandomAccessFile raf=新的RandomAccessFile(“文件路径”、“rw”);
HashMap sampledLines=新HashMap();
对于(int i=0;i
这里,
sampledLines
应该将随机选择的行放在末尾。您可能还需要检查是否没有随机跳到文件末尾,以避免在这种情况下出现错误

编辑:我将其包装到文件的开头,以防您到达结尾。这是一张相当简单的支票

编辑2:我让它通过使用
哈希映射来验证行的唯一性

如果文件的数量确实是任意的,那么在内存使用方面跟踪处理过的文件可能会有相关的问题(或者如果在文件中而不是列表或集合中跟踪,则会有IO时间)。保持不断增加的选定行列表的解决方案也会遇到与时间相关的问题

我会考虑以下几点:

  • 创建n个“bucket”文件。n可以根据考虑文件和系统内存数量的因素来确定。(如果n很大,则可以生成n的子集以保持打开的文件句柄不变。)
  • 每个文件的名称都经过哈希处理,并放入相应的bucket文件中,根据任意条件对目录进行“分片”
  • 读入bucket文件内容(仅文件名)并按原样处理(散列机制提供的随机性),或者选择rnd(n)并随动删除,提供更多的随机性
  • 或者,您可以填充并使用随机访问的思想,在拾取索引/偏移时将其从列表中删除

  • 你能把每一行都填充成相同的长度吗?是的,我可以填充这些行。如果每个记录都是相同的长度,你可以从文件长度计算行数,并通过将记录数乘以长度来寻找行的开头。当你问这个问题时,我意识到填充将有助于降低文件的复杂性解决方案:为了消除重复,请将文件中的最后一条记录移动到您上次读取的位置,并截断文件中的最后一条记录—假设错误处理是随机到达末尾和结尾