2011-06-25 52 views
1

我有一個函數「a()」,它調用寫入stdout的另一個函數「b()」。我不能修改「B()」,但我希望能夠讀什麼「b」被寫和寫回標準輸出爲「B」的閱讀,意思是:Java - 讀取另一個線程正在寫入stdout/stderr - 如何?

public void a() { 
    // start a thread that listens to stdout. 
    // the thread should print a name to stdout after "b" print "Please enter your name" 
    b(); 
} 

public void b() { // I cannot modify this function 
    System.out.println("Welcome! The time is " + System.currentTimeMillis()); 
    System.out.println("Please enter your name"); 
    String name = ... 
    // ... b reads here the name that the thread from function a() will write 
    // ... 
    System.out.println("This is the name that was entered: " + name); 
} 

我想過啓動「B 「在一個新的過程中,但我不知道如何在主函數中包裝」b「並使用命令行運行它 - 我很樂意提供建議。 如果它不是一個進程,我不知道如何實現將由「a()」激活的線程。

我試着使用:

BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); 
String line; 
while ((line = stdin.readLine()) != null) { 
... 
} 

但它沒有趕上什麼是 「b」 被寫入。

感謝您的幫助

+0

你可以用'System.setIn'和'System.setOut'來完成它,但這是一個很糟糕的做法。參數化b的輸​​入和輸出流會好得多。 – trutheality

+0

謝謝。我如何參數化b的輸​​入和輸出流? (我沒有訪問b的代碼)。 – Liz

+0

@Liz如果你沒有訪問代碼,那麼你不能。你必須去user270349的解決方案。 – trutheality

回答

0

如何與System.setOut

設置的System.out
2

您可以運行B()在另一個進程,但你並不需要這麼做。

System.out是一個PrintStream。如果仔細閱讀javadoc,您會注意到System.setOut方法。有了它,你可以用另一個PrintStream替換System.out。

實施例(未測試):

PrintStream originalOut = System.out; // To get it back later 
ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
PrintStream newOut = new PrintStream(baos); 
System.setOut(newOut); 

b(); 
System.out.flush(); 

System.setOut(originalOut); // So you can print again 

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 

// Now you can read from bais what b() wrote to System.out 

此溶液具有不線程安全的問題。如果任何其他線程在「更改」時寫入System.out,輸出也會被重定向。爲了擺脫這個問題,我認爲你需要在另一個JVM上運行b(),或者使用一個PrintStream來分割(deMux)輸出,這取決於線程或上下文。

+0

謝謝。我不想改變System.out,因爲它會影響寫入標準輸出/日誌等的所有其他線程。所以我想這個過程是最好的選擇在這裏... – Liz

1

不幸的是,在Java中這樣做並不容易。最大的問題是System.outSystem.in是分別從FileDescriptor.outFileDescriptor.in創建的兩個單獨的文件。它們沒有以任何方式連接,因此您不能寫入System.out,並期望在System.in中看到它。

你的選擇是:在外部進程

  1. 運行b()莫名其妙。是的,你需要將它放在一個具有main()函數的類中,並且執行許多複雜的過程設置,例如獲取到java.exe的路徑以及設置類路徑等等。好的部分是寫入和讀取過程將作爲你的期望。

  2. 創建兩個自定義輸入和輸出流,可以將所有流量複製到另一個輸入/輸出流,並將其發送到System.{in,out},並使用System.set{In,Out}進行設置。通過這種方式,您可以監視這些流而不會影響可能使用System.{in,out}的其他代碼。

正如在2中提到的自定義OutputStream的例子,嘗試這樣的事情:

class CopyOutputStream extends OutputStream { 
    private final OutputStream str1; 
    private final OutputStream str2; 

    public CopyOutputStream(OutputStream str1, OutputStream str2) { 
     this.str1 = str1; 
     this.str2 = str2; 
    } 

    @Override 
    public void write(int b) throws IOException { 
     str1.write(b); 
     str2.write(b); 
    } 

    // ... 

    @Override 
    public void close() throws IOException { 
     try { 
      str1.close(); 
     } finally { 
      str2.close(); 
     } 
    } 
} 

// then in a() do 

public void a(){ 
    //Create a pipe to capture data written to System.out 
    final PipedInputStream pipeIn = new PipedInputStream(); 
    final PipedOutputStream pipeOut = new PipedOutputStream(pipeIn); 

    OutputStream out = new CopyOutputStream(System.out, pipeOut); 
    //From now on everything written to System.out will be sent to 
    // System.out first and then copied to pipeOut and will be available 
    // to read from pipeIn. 
    System.setOut(new PrintStream(out)); 

    // In another thread: read data from System.out 
    BufferedReader reader = new BufferedReader(new InputStreamReader(pipeIn)); 
    String name = reader.readLine(); 
} 

不幸的是,你將不得不重複上述過程爲System.in,這意味着更加瘋狂的代碼,但我不要認爲它會比這更容易。

如果你是準備了一些非常瘋狂的動作,也許你可以得到一些Java庫(最有可能與本機代碼),可以給你當前正在運行的JVM的Process對象的保持,然後用get{Input,Output}Stream()方法做這項工作。

相關問題