Java代码尾部n行文件相当于unix中的尾部命令



下面是对文件的'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();
    }
}
}

我使用了以下方法:文件和要跟踪的行号作为命令行参数

接收。
  1. 使用readLine方法获取文件中的行数
  2. 为每个readLine调用使用一个递增数
  3. 将递增式和readLinemethod返回的字符串存储在Hash Map中
  4. 因此整个文件被存储在哈希映射
  5. 现在您可以使用散列映射键从
  6. 的特定行检索文件的值
  7. 您可以使用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更大的文件吗?

当然可以,但我不清楚你会得到什么。

我个人建议按如下方式重组你的算法:

  1. 查找到文件末尾
  2. 向后解析,直到遇到所需数量的n字符。
  3. 向前读到文件的末尾,边读边打印。

这样就不需要缓冲文件中的每一行,并且在非常大的文件大小上也不会降低性能。

看起来你把整个文件保存在内存中,你只需要保留"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--;
        }
    }
}
}

最新更新