我有一个将信息写入文件的应用程序。该信息在执行后用于确定应用程序的通过/失败/正确性。我希望能够在写入文件时读取它,以便可以实时进行通过/失败/正确性检查。
我认为可以做到这一点,但是使用Java时所涉及的陷阱是什么?如果阅读赶上了写作,它会只是等待更多的写操作直到文件关闭,还是此时读将引发异常?如果是后者,我该怎么办?
我的直觉目前将我推向BufferedStreams。这是要走的路吗?
我有一个将信息写入文件的应用程序。该信息在执行后用于确定应用程序的通过/失败/正确性。我希望能够在写入文件时读取它,以便可以实时进行通过/失败/正确性检查。
我认为可以做到这一点,但是使用Java时所涉及的陷阱是什么?如果阅读赶上了写作,它会只是等待更多的写操作直到文件关闭,还是此时读将引发异常?如果是后者,我该怎么办?
我的直觉目前将我推向BufferedStreams。这是要走的路吗?
Answers:
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。是的,我知道我仍然处在石器时代。
如果要在写入文件时读取文件,并且仅读取新内容,则以下内容将帮助您实现相同目的。
要运行此程序,您将从命令提示符/终端窗口启动它,并将文件名传递给read。除非您终止该程序,否则它将读取文件。
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();
}
}
BufferedReader
,每次循环调用中的创建都是毫无意义的。创建它之后,就不必跳过,因为缓冲的读取器将在到达时读取新行。
您还可以查看用于锁定文件一部分的java通道。
http://java.sun.com/javase/6/docs/api/java/nio/channels/FileChannel.html
该功能FileChannel
可能是一个开始
lock(long position, long size, boolean shared)
对该方法的调用将阻塞,直到可以锁定该区域为止
它每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);
}
}
}
答案似乎是“否” ...和“是”。似乎没有真正的方法知道文件是否已打开以供其他应用程序写入。因此,从此类文件读取将一直进行到内容耗尽为止。我接受了Mike的建议,并编写了一些测试代码:
Writer.java将字符串写入文件,然后等待用户按下Enter键,然后再将另一行写入文件。其想法是可以启动它,然后可以启动阅读器以查看它如何处理“部分”文件。我写的读者在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();
}
}
Reader.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图形尾巴可以执行此操作。
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;
}