![](/img/trans.png)
[英]read from a file that is actively being written to using Java [Improvement]
[英]How do I use Java to read from a file that is actively being written to?
我有一個將信息寫入文件的應用程序。 此信息在執行后用於確定應用程序的通過/失敗/正確性。 我希望能夠在寫入文件時讀取文件,以便我可以實時進行這些通過/失敗/正確性檢查。
我認為這樣做是可能的,但是在使用 Java 時涉及哪些問題? 如果讀取趕上寫入,它會等待更多寫入直到文件關閉,還是此時讀取會拋出異常? 如果是后者,那我該怎么辦?
我的直覺目前正在將我推向 BufferedStreams。 這是要走的路嗎?
無法使用FileChannel.read(ByteBuffer)
使示例工作,因為它不是阻塞讀取。 然而,讓下面的代碼工作:
boolean running = true;
BufferedInputStream reader = new BufferedInputStream(new FileInputStream( "out.txt" ) );
public void run() {
while( running ) {
if( reader.available() > 0 ) {
System.out.print( (char)reader.read() );
}
else {
try {
sleep( 500 );
}
catch( InterruptedException ex ) {
running = false;
}
}
}
}
當然,同樣的事情可以作為計時器而不是線程工作,但我把它留給程序員。 我仍在尋找更好的方法,但這現在對我有用。
哦,我要說明一下:我使用的是 1.4.2。 是的,我知道我仍然處於石器時代。
如果您想在寫入文件時讀取文件並且只讀取新內容,那么以下內容將幫助您實現相同的目標。
要運行此程序,您將從命令提示符/終端窗口啟動它並傳遞文件名以進行讀取。 除非您終止程序,否則它將讀取文件。
java FileReader c:\\myfile.txt
當您鍵入一行文本時,將其從記事本中保存,您將在控制台中看到打印的文本。
public class FileReader {
public static void main(String args[]) throws Exception {
if(args.length>0){
File file = new File(args[0]);
System.out.println(file.getAbsolutePath());
if(file.exists() && file.canRead()){
long fileLength = file.length();
readFile(file,0L);
while(true){
if(fileLength<file.length()){
readFile(file,fileLength);
fileLength=file.length();
}
}
}
}else{
System.out.println("no file to read");
}
}
public static void readFile(File file,Long fileLength) throws IOException {
String line = null;
BufferedReader in = new BufferedReader(new java.io.FileReader(file));
in.skip(fileLength);
while((line = in.readLine()) != null)
{
System.out.println(line);
}
in.close();
}
}
我完全同意Joshua 的回答, Tailer在這種情況下適合這份工作。 這是一個例子:
它每 150 毫秒在文件中寫入一行,同時每 2500 毫秒讀取一次相同的文件
public class TailerTest
{
public static void main(String[] args)
{
File f = new File("/tmp/test.txt");
MyListener listener = new MyListener();
Tailer.create(f, listener, 2500);
try
{
FileOutputStream fos = new FileOutputStream(f);
int i = 0;
while (i < 200)
{
fos.write(("test" + ++i + "\n").getBytes());
Thread.sleep(150);
}
fos.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static class MyListener extends TailerListenerAdapter
{
@Override
public void handle(String line)
{
System.out.println(line);
}
}
}
您還可以查看用於鎖定文件的一部分的 java 通道。
http://java.sun.com/javase/6/docs/api/java/nio/channels/FileChannel.html
FileChannel
這個功能可能是一個開始
lock(long position, long size, boolean shared)
調用此方法將阻塞,直到可以鎖定該區域
答案似乎是“不”……和“是”。 似乎沒有真正的方法可以知道文件是否已打開以供另一個應用程序寫入。 因此,從這樣的文件中讀取只會繼續進行,直到內容用完為止。 我接受了 Mike 的建議並編寫了一些測試代碼:
Writer.java 將一個字符串寫入文件,然后等待用戶在將另一行寫入文件之前按回車鍵。 這個想法是可以啟動它,然后可以啟動閱讀器來查看它如何處理“部分”文件。 我寫的閱讀器在 Reader.java 中。
Writer.java
public class Writer extends Object
{
Writer () {
}
public static String[] strings =
{
"Hello World",
"Goodbye World"
};
public static void main(String[] args)
throws java.io.IOException {
java.io.PrintWriter pw =
new java.io.PrintWriter(new java.io.FileOutputStream("out.txt"), true);
for(String s : strings) {
pw.println(s);
System.in.read();
}
pw.close();
}
}
閱讀器.java
public class Reader extends Object
{
Reader () {
}
public static void main(String[] args)
throws Exception {
java.io.FileInputStream in = new java.io.FileInputStream("out.txt");
java.nio.channels.FileChannel fc = in.getChannel();
java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocate(10);
while(fc.read(bb) >= 0) {
bb.flip();
while(bb.hasRemaining()) {
System.out.println((char)bb.get());
}
bb.clear();
}
System.exit(0);
}
}
不保證此代碼是最佳實踐。
這留下了 Mike 建議的選項,即定期檢查是否有要從文件中讀取的新數據。 這然后需要用戶干預以在確定讀取完成時關閉文件讀取器。 或者,讀者需要知道文件的內容並能夠確定和結束寫入條件。 如果內容是 XML,則可以使用文檔結尾來表示這一點。
有一個開源 Java Graphic Tail 可以做到這一點。
https://stackoverflow.com/a/559146/1255493
public void run() {
try {
while (_running) {
Thread.sleep(_updateInterval);
long len = _file.length();
if (len < _filePointer) {
// Log must have been jibbled or deleted.
this.appendMessage("Log file was reset. Restarting logging from start of file.");
_filePointer = len;
}
else if (len > _filePointer) {
// File must have had something added to it!
RandomAccessFile raf = new RandomAccessFile(_file, "r");
raf.seek(_filePointer);
String line = null;
while ((line = raf.readLine()) != null) {
this.appendLine(line);
}
_filePointer = raf.getFilePointer();
raf.close();
}
}
}
catch (Exception e) {
this.appendMessage("Fatal error reading log file, log tailing has stopped.");
}
// dispose();
}
您無法讀取使用 FileInputStream、FileReader 或 RandomAccessFile 從另一個進程打開的文件。
但是直接使用 FileChannel 會起作用:
private static byte[] readSharedFile(File file) throws IOException {
byte buffer[] = new byte[(int) file.length()];
final FileChannel fc = FileChannel.open(file.toPath(), EnumSet.of(StandardOpenOption.READ));
final ByteBuffer dst = ByteBuffer.wrap(buffer);
fc.read(dst);
fc.close();
return buffer;
}
不是 Java 本身,但您可能會遇到一些問題,即您已將某些內容寫入文件,但尚未實際寫入 - 它可能位於某處的緩存中,而從同一文件中讀取實際上可能不會給您新信息。
簡短版本 - 使用 flush() 或任何相關的系統調用來確保您的數據實際寫入文件。
注意我不是在談論操作系統級別的磁盤緩存 - 如果您的數據進入這里,它應該在此之后出現在 read() 中。 可能是語言本身緩存寫入,等待緩沖區填滿或文件被刷新/關閉。
我從未嘗試過,但您應該編寫一個測試用例,看看在您到達末尾后從流中讀取是否有效,無論是否有更多數據寫入文件。
是否有不能使用管道輸入/輸出流的原因? 數據是從同一個應用程序寫入和讀取的嗎(如果是這樣,您有數據,為什么需要從文件中讀取)?
否則,可能會讀到文件末尾,然后監視更改並尋找您離開的地方並繼續……但要注意競爭條件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.