下面是对文件的'n'行进行跟踪的代码。
<code>
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
class TailCommand {
public static void main(String args[]) {
int j;
try {
/*
* Receive file name and no of lines to tail as command line
* argument
*/
RandomAccessFile randomFile = new RandomAccessFile(args[0], "r");
long numberOfLines = Long.valueOf(args[1]).longValue();
long lineno = 0;
String str;
String outstr;
StringBuilder sb = new StringBuilder();
Map<Long, String> strmap = new HashMap<Long, String>();
while ((str = randomFile.readLine()) != null) {
strmap.put(lineno + 1, str);
lineno++;
}
System.out.println("Total no of lines in file is " + lineno);
long startPosition = lineno - numberOfLines;
while (startPosition <= lineno) {
if (strmap.containsKey(startPosition)) {
// System.out.println("HashMap contains "+ startPosition
// +" as key");
outstr = (String) strmap.get(startPosition);
sb.append(outstr);
System.out.println(outstr);
}
startPosition++;
}
// Collection coll = strmap.values();
// System.out.println(coll+"size"+strmap.size());
// System.out.println(sb);
} catch (Exception e) {
e.printStackTrace();
}
}
}
我使用了以下方法:文件和要跟踪的行号作为命令行参数
接收。- 使用readLine方法获取文件中的行数
- 为每个readLine调用使用一个递增数
- 将递增式和readLinemethod返回的字符串存储在Hash Map中
- 因此整个文件被存储在哈希映射
- 现在您可以使用散列映射键从 的特定行检索文件的值
- 您可以使用stringbuilder从特定行打印选择
我的怀疑,
我的方法有效吗?对于大于10MB的大文件,我可以使用这种方法吗?如果更多的人必须同时从同一个文件中退出,我需要做些什么改进?我可以使用StringBuilder更大的文件吗?
正如我在评论djna的回答时提到的,你做得不是很有效:
- 您正在读取整个文件。如果文件很大,而n行很小,那么你只是在浪费时间、I/O和其他东西。 你还在浪费内存。
- 没有缓冲(除了
RandomAccessFile#readLine() may or may not provide
),这也会导致一些可能的减速
那么,我要做的就是从末尾开始以块的形式读入文件,并分别处理这些块。
RandomAccessFile raf = new RandomAccessFile(new File(file), "r");
List<String> lines = new ArrayList<String>();
final int chunkSize = 1024 * 32;
long end = raf.length();
boolean readMore = true;
while (readMore) {
byte[] buf = new byte[chunkSize];
// Read a chunk from the end of the file
long startPoint = end - chunkSize;
long readLen = chunkSize;
if (startPoint < 0) {
readLen = chunkSize + startPoint;
startPoint = 0;
}
raf.seek(startPoint);
readLen = raf.read(buf, 0, (int)readLen);
if (readLen <= 0) {
break;
}
// Parse newlines and add them to an array
int unparsedSize = (int)readLen;
int index = unparsedSize - 1;
while (index >= 0) {
if (buf[index] == 'n') {
int startOfLine = index + 1;
int len = (unparsedSize - startOfLine);
if (len > 0) {
lines.add(new String(buf, startOfLine, len));
}
unparsedSize = index + 1;
}
--index;
}
// Move end point back by the number of lines we parsed
// Note: We have not parsed the first line in the chunked
// content because could be a partial line
end = end - (chunkSize - unparsedSize);
readMore = lines.size() < linesToRead && startPoint != 0;
}
// Only print the requested number of lines
if (linesToRead > lines.size()) {
linesToRead = lines.size();
}
for (int i = linesToRead - 1; i >= 0; --i) {
pw.print(lines.get(i));
}
我的方法有效吗?对于大于10MB的大文件,我可以使用这种方法吗?
是的,它是有效的。是的,您"可以"将其用于较大的文件,但由于您总是扫描整个文件,因此文件的时间越长,性能就会降低。同样,由于您将所有内容存储在内存中,因此内存需求将一直增加,直到一个非常大的文件开始导致OutOfMemoryError
问题。
如果更多的人必须同时从同一个文件中退出,我需要做什么改进?
无,因为您只跟踪最后的n
行。每个人都可以简单地运行自己的程序实例。如果您希望随着时间的推移跟踪文件的更新(就像tail
所做的那样,如果您省略了-n
参数),那么您必须进行一些更改。
我可以使用StringBuilder更大的文件吗?
当然可以,但我不清楚你会得到什么。
我个人建议按如下方式重组你的算法:
- 查找到文件末尾
- 向后解析,直到遇到所需数量的
n
字符。 - 向前读到文件的末尾,边读边打印。
这样就不需要缓冲文件中的每一行,并且在非常大的文件大小上也不会降低性能。
看起来你把整个文件保存在内存中,你只需要保留"n"行。因此,与其分配一个大小为n的数组,不如将其用作环缓冲区。
在您显示的代码中,您似乎没有使用StringBuilder,我猜您正在使用它来构建输出。因为这应该只取决于n,而不是文件的大小,我不明白为什么使用StringBuilder会有问题。
你基本上是在内存中读取整个文件——要做到这一点,你不需要随机访问文件,真的。
如果文件很大,这可能不是最好的选择。
为什么不使用HashMap来存储(行号,在文件中的位置),而不是(行号->行)。这样你就知道最后n行在哪个位置查找了。
另一种方法是使用n个字符串的缓冲区(数组)-到目前为止的最后n行。但是要小心,当读取新行时,您不想移动缓冲区中的所有元素(即1->0,2->1,…), n->(n-1),然后在末尾添加新行)。使用循环缓冲代替。在缓冲区中保留结束位置的索引,并在添加新行时覆盖下一个位置。如果你在位置n-1,下一个是0(如此循环)。
我根据以上建议修改了代码,如下所示:
使用的逻辑描述如下:
1。使用文件
的长度查找EOF文件2.将文件指针从EOF向后移动并检查是否出现" n"。
3.如果发现出现'n',则增加行计数器和将readline的输出放到hashMap
4.从hashMap中按降序检索值上述方法不会引起内存问题,这是很明显的。请建议。
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
class NewTailCommand {
public static void main(String args[]) {
Map<Long, String> strmap = new HashMap<Long, String>();
long numberOfLines = Long.valueOf(args[1]).longValue();
try {
/*
* Receive file name and no of lines to tail as command line
* argument
*/
RandomAccessFile randomFile = new RandomAccessFile(args[0], "r");
long filelength = randomFile.length();
long filepos = filelength - 1;
long linescovered = 1;
System.out.println(filepos);
for (linescovered = 1; linescovered <= numberOfLines; filepos--) {
randomFile.seek(filepos);
if (randomFile.readByte() == 0xA)
if (filepos == filelength - 1)
continue;
else {
strmap.put(linescovered,randomFile.readLine());
linescovered++;
}
}
} catch (Exception e) {
e.printStackTrace();
}
long startPosition = numberOfLines;
while (startPosition != 0) {
if (strmap.containsKey(startPosition)) {
// System.out.println("HashMap contains "+ startPosition
// +" as key");
String outstr = (String) strmap.get(startPosition);
System.out.println(outstr);
startPosition--;
}
}
}
}