[英]Groovy: Reading a range of lines from file
我有一个文本文件,其中包含大量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解决方案:-)
干杯,罗伯特
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
}
}
总,呃?
不幸的是,除非您的线路是固定长度的,否则您将无法有效地跳到start
线,因为每条线路可能任意长,因此需要读取所有数据。 但这并不妨碍更好的解决方案。
认为值得更新以显示如何使用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
(加上它选择做的内部缓冲)。
Groovy现在可以从一些特殊的行开始。 以下是文档中文档的两个引用
Object eachLine(int firstLine, Closure closure)
Object eachLine(String charset, int firstLine, Closure closure)
这是一个Groovy解决方案。 不幸的是,这将在start
后读取文件的每一行
def start = 4
def end = 48
new File("input.txt").eachLine(start) {lineNo, line ->
if (lineNo <= end) {
// Process the line
}
}
我不相信有任何“神奇”的方法可以跳到文件中的任意“行”。 行只是由换行符定义,所以没有实际读取文件,就无法知道它们的位置。 我相信你有两个选择:
在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"
}
}
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"
}
在此解决方案中,您按顺序从文件中读取每一行,但不要将它们全部保留在内存中。
这应该做到这一点。 我相信这不会在“结束”之后读取任何一行。
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
}
}
}
您必须从头开始迭代到达起始位置,但您可以使用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
}
}
谢谢你的所有提示。 根据你所写的内容,我拼凑了我自己的代码,这些代码似乎正在起作用。 不优雅,但它的目的:-)
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++
}
}
下面是使用另一种Java解决方案LineIterator和文件实用程序从共享/ IO :
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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.