Groovy:从文件中读取一系列行

时间:2010-11-03 17:22:55

标签: java file-io groovy

我有一个文本文件,其中包含大量2,000,000行的大量数据。使用以下代码片段浏览文件很简单,但这不是我需要的; - )

def f = new File("input.txt")
f.eachLine() {
    // Some code here
}

我只需要从文件中读取特定范围的行。有没有办法像这样指定起始和结束行(伪代码)?在选择范围之前,我想避免使用readLines()将所有行加载到内存中。

// Read all lines from 4 to 48
def f = new File("input.txt")
def start = 4
def end = 48
f.eachLine(start, end) {
    // Some code here
}

如果Groovy无法做到这一点,那么欢迎任何Java解决方案: - )

干杯, 罗伯特

9 个答案:

答案 0 :(得分:8)

Java解决方案:

BufferedReader r = new BufferedReader(new FileReader(f));
String line;
for ( int ln = 0; (line = r.readLine()) != null && ln <= end; ln++ ) {
    if ( ln >= start ) {
        //Some code here
    }
}

Gross,嗯?

不幸的是,除非您的行是固定长度,否则您将无法有效地跳到start行,因为每行可能是任意长的,因此需要读取所有数据。但这并不排除更好的解决方案。

Java 8

认为值得更新,以展示如何使用Streams有效地执行此操作:

int start = 5;
int end = 12;
Path file = Paths.get("/tmp/bigfile.txt");

try (Stream<String> lines = Files.lines(file)) {
    lines.skip(start).limit(end-start).forEach(System.out::println);
}

因为Streams被懒惰地评估,它只会读取包括end在内的行(加上它选择做的内部缓冲)。

答案 1 :(得分:4)

这是一个Groovy解决方案。不幸的是,这将在start

之后读取文件的每一行
def start = 4
def end = 48

new File("input.txt").eachLine(start) {lineNo, line ->

    if (lineNo <= end) {
        // Process the line
    }
}

答案 2 :(得分:4)

Groovy现在可以从一些特殊的行开始。以下是docs on File

的两个引文
Object eachLine(int firstLine, Closure closure) 

Object eachLine(String charset, int firstLine, Closure closure) 

答案 3 :(得分:3)

我不相信有任何“神奇”的方式可以跳到文件中的任意“行”。行只是由换行符定义,所以没有实际读取文件,就无法知道它们的位置。我相信你有两个选择:

  1. 按照Mark Peter的回答,使用BufferedReader一次读取一行文件,直到找到所需的行。这显然会很慢。
  2. 计算下一次读取需要多少个字节(而不是行),并使用类似RandomAccessFile的内容直接搜索文件中的那个点。是否可以有效地知道正确的字节数取决于您的应用程序。例如,如果您按顺序一次读取文件,则只需记录您离开的位置。如果所有行都具有固定长度L字节,那么到达行N只是寻求定位N * L的问题。如果这是您经常重复的操作,则一些预处理可能有所帮助:例如,读取整个文件一次并在内存中的HashMap中记录每行的起始位置。下次你需要去N行时,只需在HashMap中查找它的位置并直接寻找到那一点。

答案 4 :(得分:2)

这应该这样做。我相信这不会在“结束”之后读取任何一行。

def readRange = {file ->
    def start = 10
    def end = 20
    def fileToRead = new File(file)
    fileToRead.eachLine{line, lineNo = 0 ->
        lineNo++
        if(lineNo > end) {
            return
        }
        if(lineNo >= start) {
            println line                
        }            
    }
}

答案 5 :(得分:2)

在Groovy中,您可以使用Category

class FileHelper {
    static eachLineInRange(File file, IntRange lineRange, Closure closure) {
        file.withReader { r->
            def line
            for(; (line = r.readLine()) != null;) {
                def lineNo = r.lineNumber
                if(lineNo < lineRange.from) continue
                if(lineNo > lineRange.to) break
                closure.call(line, lineNo)
            }
        }
    }
}

def f = '/path/to/file' as File
use(FileHelper) {
    f.eachLineInRange(from..to){line, lineNo ->
        println "$lineNo) $line"
    }
}

ExpandoMetaClass

File.metaClass.eachLineInRange = { IntRange lineRange, Closure closure ->
    delegate.withReader { r->
        def line
        for(; (line = r.readLine()) != null;) {
            def lineNo = r.lineNumber
            if(lineNo < lineRange.from) continue
            if(lineNo > lineRange.to) break
            closure.call(line, lineNo)
        }
    }
}

def f = '/path/to/file' as File
f.eachLineInRange(from..to){line, lineNo ->
    println "$lineNo) $line"
}

在此解决方案中,您按顺序从文件中读取每一行,但不要将它们全部保留在内存中。

答案 6 :(得分:1)

您必须从头开始迭代到达起始位置,但您可以使用LineNumberReader(而不是BufferedReader),因为它会跟踪您的行号。

    final int start = 4;
    final int end = 48;

    final LineNumberReader in = new LineNumberReader(new FileReader(filename));
    String line=null;
    while ((line = in.readLine()) != null && in.getLineNumber() <= end) {
        if (in.getLineNumber() >= start) {
            //process line
        }
    }

答案 7 :(得分:1)

感谢你的所有提示。根据你所写的内容,我拼凑了我自己的代码,这些代码似乎正在起作用。不优雅,但它的目的是: - )

def f = new RandomAccessFile("D:/input.txt", "r")
def start = 3
def end = 6
def current = start-1
def BYTE_OFFSET = 11
def resultList = []

if ((end*BYTE_OFFSET) <= f.length()) {
    while ((current*BYTE_OFFSET) < (end*BYTE_OFFSET)) {
        f.seek(current*BYTE_OFFSET)
        resultList << f.readLine()
        current++
    }
}

答案 8 :(得分:0)

以下是使用LineIterator中的FileUtilsCommons / IO的另一个Java解决方案:

public static Collection<String> readFile(final File f,
    final int startOffset,
    final int lines) throws IOException{
    final LineIterator it = FileUtils.lineIterator(f);
    int index = 0;
    final Collection<String> coll = new ArrayList<String>(lines);
    while(index++ < startOffset + lines && it.hasNext()){
        final String line = it.nextLine();
        if(index >= startOffset){
            coll.add(line);
        }
    }
    it.close();
    return coll;
}