Java:在不锁定文件的情况下打开和读取文件

Java:在不锁定文件的情况下打开和读取文件,java,filelock,java-io,Java,Filelock,Java Io,我需要能够用Java模拟“tail-f”。我试图读取另一个进程正在写入的日志文件,但当我打开该文件进行读取时,它会锁定该文件,而另一个进程无法再写入该文件。任何帮助都将不胜感激 以下是我当前使用的代码: public void read(){ Scanner fp = null; try{ fp = new Scanner(new FileReader(this.filename)); fp.useDelimiter("\n"); }cat

我需要能够用Java模拟“tail-f”。我试图读取另一个进程正在写入的日志文件,但当我打开该文件进行读取时,它会锁定该文件,而另一个进程无法再写入该文件。任何帮助都将不胜感激

以下是我当前使用的代码:

public void read(){
    Scanner fp = null;
    try{
        fp = new Scanner(new FileReader(this.filename));
        fp.useDelimiter("\n");
    }catch(java.io.FileNotFoundException e){
        System.out.println("java.io.FileNotFoundException e");
    }
    while(true){
        if(fp.hasNext()){
            this.parse(fp.next());
        }           
    }       
}

看看FileChannel API。要锁定文件,您可以选中Windows对文件使用强制锁定,除非您在打开时指定了正确的共享标志。如果你想打开一个繁忙的文件,你需要Win32 API
CreateFile
一个带有共享标志
file\u SHARE\u DELETE | file\u SHARE\u READ | file\u SHARE\u WRITE
的句柄

在JDK内部,有几个地方使用它来打开文件以读取属性和内容,但据我所知,它没有导出/提供给Java类库级别。因此,您需要找到一个本机库来实现这一点


我认为,作为一项快速解决方法,您可以从命令“cmd/D/C type file.lck”

读取
process.getInputStream()
,由于一些特殊情况,例如文件截断和(中间)删除,重建尾部是很棘手的。要在不锁定的情况下打开文件,请使用
StandardOpenOption。使用新的Java文件API阅读
,如下所示:

try (InputStream is = Files.newInputStream(path, StandardOpenOption.READ)) {
    InputStreamReader reader = new InputStreamReader(is, fileEncoding);
    BufferedReader lineReader = new BufferedReader(reader);
    // Process all lines.
    String line;
    while ((line = lineReader.readLine()) != null) {
        // Line content content is in variable line.
    }
}
有关我在Java中创建尾部的尝试,请参见:

  • 方法
    检查文件(…)
    in
  • 上面的命令用于创建尾部操作。
    queue.feed(lineContent)
    将行内容传递给侦听器进行处理,并将等于您的
    this.parse(…)
您可以自由地从代码中获得灵感,或者简单地复制您需要的部分。如果您发现任何我不知道的问题,请告诉我

java.io为您提供一个强制文件锁,java.nio为您提供一个 咨询文件锁

如果你想读取任何没有任何锁的文件,你可以使用下面的类

import java.nio.channels.FileChannel;
import java.nio.file.Paths;
如果要逐行跟踪文件,请使用以下代码

public void tail(String logPath){
    String logStr = null;
    FileChannel fc = null;
    try {
        fc = FileChannel.open(Paths.get(logPath), StandardOpenOption.READ);
        fc.position(fc.size());
    } catch (FileNotFoundException e1) {
        System.out.println("FileNotFoundException occurred in Thread : " + Thread.currentThread().getName());
        return;
    } catch (IOException e) {
        System.out.println("IOException occurred while opening FileChannel in Thread : " + Thread.currentThread().getName());
    }
    while (true) {
        try {
            logStr = readLine(fc);
            if (logStr != null) {
                System.out.println(logStr);
            } else {
                Thread.sleep(1000);
            }
        } catch (IOException|InterruptedException e) {
            System.out.println("Exception occurred in Thread : " + Thread.currentThread().getName());
            try {
                fc.close();
            } catch (IOException e1) {
            }
            break;
        }
    }
}

private String readLine(FileChannel fc) throws IOException {
    ByteBuffer buffers = ByteBuffer.allocate(128);
    // Standard size of a line assumed to be 128 bytes
    long lastPos = fc.position();
    if (fc.read(buffers) > 0) {
        byte[] data = buffers.array();
        boolean foundTmpTerminator = false;
        boolean foundTerminator = false;
        long endPosition = 0;
        for (byte nextByte : data) {
            endPosition++;
            switch (nextByte) {
            case -1:
                foundTerminator = true;
                break;
            case (byte) '\r':
                foundTmpTerminator = true;
                break;
            case (byte) '\n':
                foundTmpTerminator = true;
                break;
            default:
                if (foundTmpTerminator) {
                    endPosition--;
                    foundTerminator = true;
                }
            }
            if (foundTerminator) {
                break;
            }
        }
        fc.position(lastPos + endPosition);
        if (foundTerminator) {
            return new String(data, 0, (int) endPosition);
        } else {
            return new String(data, 0, (int) endPosition) + readLine(fc);
        }
    }
    return null;
}

如果没有其他办法,您可以使用jni访问win32 api(假设您在windows上,否则无论您使用的是什么api)。使用win32 api将是最丑陋的;)嘿,因为我对Java不太了解,这是我得到的最好的建议:)我选择Java是为了可移植性,所以这不是一个选项。我将研究FileChannel API,尽管我回家时Cshah提到过。谢谢你的帮助JNI的建议太离谱了。这里执行锁定的是Win32 API。Java没有做到这一点。使用FileChannel也不会有帮助。我将审查整个要求。日志文件不是用来解析的,它们是为人类准备的。如果您需要来自应用程序另一部分的通信,请使用套接字或数据库。您可以查看stackoverflow中可能存在的重复问题。我已经仔细查看了它,并尝试了几种策略--甚至除了FileChannel之外还使用java.io.RandomAccessFile,但我仍然无法让另一个进程写入我正在Java.rogue780中读取的文件-发布您尝试过的策略。请注意,如果另一个进程在写入文件之前需要一个独占锁,那么您基本上就是toast(当然,tail在这种情况下也不起作用)。但是您是否尝试过锁定(使用非独占读取锁)文件的顶部?锁定的行为也特定于底层操作系统。我没有尝试过共享锁定,但我想您可以在linux中尝试。我怀疑Windows是否会像和一样支持它。我不确定它是什么时候更改的,但我的文件是用RandomAccessFile打开的,它是用SHARE\u READ和SHARE\u WRITE打开的。我使用ProcMon.exe进行了测试