Java:ASCII随机行文件访问状态
有没有比创建满足以下标准的流文件读取器类更好的[预先存在的可选Java 1.6]解决方案Java:ASCII随机行文件访问状态,java,io,Java,Io,有没有比创建满足以下标准的流文件读取器类更好的[预先存在的可选Java 1.6]解决方案 给定一个任意大的ASCII文件,其中每一行都以\n 对于某些方法的每次调用readLine()从文件中随机读取一行 在文件句柄的生命周期中,对readLine()的调用不应返回同一行两次 更新: 所有的行最终都必须被读取 上下文:文件的内容是从unixshell命令创建的,以获取给定目录中包含的所有路径的目录列表;有数百万到十亿个文件(在目标文件中产生数百万到十亿行)。如果有某种方法可以在创建期间将
- 给定一个任意大的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)并随动删除,提供更多的随机性
或者,您可以填充并使用随机访问的思想,在拾取索引/偏移时将其从列表中删除
你能把每一行都填充成相同的长度吗?是的,我可以填充这些行。如果每个记录都是相同的长度,你可以从文件长度计算行数,并通过将记录数乘以长度来寻找行的开头。当你问这个问题时,我意识到填充将有助于降低文件的复杂性解决方案:为了消除重复,请将文件中的最后一条记录移动到您上次读取的位置,并截断文件中的最后一条记录—假设错误处理是随机到达末尾和结尾