<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    10.2. 標準輸入、輸出和錯誤

    UNIX 用戶已經對標準輸入、標準輸出和標準錯誤的概念非常熟悉了。這一節是為其他不熟悉的人準備的。

    標準輸入和標準錯誤 (通常縮寫為 stdoutstderr) 是內建在每一個 UNIX 系統中的管道。當你 print 某些東西時,結果前往 stdout 管道;當你的程序崩潰并打印出調試信息 (例如 Python 中的 traceback (錯誤跟蹤)) 的時候,信息前往 stderr 管道。通常這兩個管道只與你正在工作的終端窗口相聯,所以當一個程序打印時,你可以看到輸出,而當一個程序崩潰時,你可以看到調試信息。(如果你正在一個基于窗口的 Python IDE 上工作,stdoutstderr 缺省為你的“交互窗口”。)

    例 10.8. stdoutstderr 介紹

    >>> for i in range(3):
    ...     print 'Dive in'             1
    Dive in
    Dive in
    Dive in
    >>> import sys
    >>> for i in range(3):
    ...     sys.stdout.write('Dive in') 2
    Dive inDive inDive in
    >>> for i in range(3):
    ...     sys.stderr.write('Dive in') 3
    Dive inDive inDive in
    1 正如在例 6.9 “簡單計數”中看到的,你可以使用 Python 內置的 range 函數來構造簡單的計數循環,即重復某物一定的次數。
    2 stdout 是一個類文件對象;調用它的 write 函數可以打印出你給定的任何字符串。實際上,這就是 print 函數真正做的事情;它在你打印的字符串后面加上一個硬回車,然后調用 sys.stdout.write 函數。
    3 在最簡單的例子中,stdoutstderr 把它們的輸出發送到相同的地方:Python IDE (如果你在一個 IDE 中的話),或者終端 (如果你從命令行運行 Python 的話)。和 stdout 一樣,stderr 并不為你添加硬回車;如果需要,要自己加上。

    stdoutstderr 都是類文件對象,就像在第 10.1 節 “抽象輸入源”中討論的一樣,但是它們都是只寫的。它們都沒有 read 方法,只有 write 方法。然而,它們仍然是類文件對象,因此你可以將其它任何 (類) 文件對象賦值給它們來重定向其輸出。

    例 10.9. 重定向輸出

    [you@localhost kgp]$ python stdout.py
    Dive in
    [you@localhost kgp]$ cat out.log
    This message will be logged instead of displayed

    (在 Windows 上,你要使用 type 來代替 cat 顯示文件的內容。)

    如果您還沒有下載本書附帶的樣例程序, 可以 下載本程序和其他樣例程序

    #stdout.py
    import sys
    
    print 'Dive in'                                          1
    saveout = sys.stdout                                     2
    fsock = open('out.log', 'w')                             3
    sys.stdout = fsock                                       4
    print 'This message will be logged instead of displayed' 5
    sys.stdout = saveout                                     6
    fsock.close()                                            7
    
    1 打印輸出到 IDE交互窗口” (或終端,如果從命令行運行腳本的話)。
    2 始終在重定向前保存 stdout,這樣的話之后你還可以將其設回正常。
    3 打開一個新文件用于寫入。如果文件不存在,將會被創建。如果文件存在,將被覆蓋。
    4 所有后續的輸出都會被重定向到剛才打開的新文件上。
    5 這樣只會將輸出結果“打印”到日志文件中;在 IDE 窗口中或在屏幕上不會看到輸出結果。
    6 在我們將 stdout 搞亂之前,讓我們把它設回原來的方式。
    7 關閉日志文件。

    重定向 stderr 以完全相同的方式進行,只要把 sys.stdout 改為 sys.stderr

    例 10.10. 重定向錯誤信息

    [you@localhost kgp]$ python stderr.py
    [you@localhost kgp]$ cat error.log
    Traceback (most recent line last):
      File "stderr.py", line 5, in ?
        raise Exception, 'this error will be logged'
    Exception: this error will be logged

    如果您還沒有下載本書附帶的樣例程序, 可以 下載本程序和其他樣例程序

    #stderr.py
    import sys
    
    fsock = open('error.log', 'w')               1
    sys.stderr = fsock                           2
    raise Exception, 'this error will be logged' 3 4
    
    1 打開你要存儲調試信息的日志文件。
    2 將新打開的日志文件的文件對象賦值給 stderr 以重定向標準錯誤。
    3 引發一個異常。從屏幕輸出上可以注意到這個行為沒有 在屏幕上打印出任何東西。所有正常的跟蹤信息已經寫進 error.log
    4 還要注意你既沒有顯式關閉日志文件,也沒有將 stderr 設回最初的值。這樣挺好,因為一旦程序崩潰 (由于引發的異常),Python 將替我們清理并關閉文件,因此永遠不恢復 stderr 不會造成什么影響。然而對于 stdout,恢復初始值相對更為重要――你可能會在后面再次操作標準輸出。

    向標準錯誤寫入錯誤信息是很常見的,所以有一種較快的語法可以立刻導出信息。

    例 10.11. 打印到 stderr

    >>> print 'entering function'
    entering function
    >>> import sys
    >>> print >> sys.stderr, 'entering function' 1
    entering function
    
    1 print 語句的快捷語法可以用于寫入任何打開的文件 (或者是類文件對象)。在這里,你可以將單個 print 語句重定向到 stderr 而且不用影響后面的 print 語句。

    另一方面,標準輸入是一個只讀文件對象,它表示從前一個程序到這個程序的數據流。這個對于老的 Mac OS 用戶和 Windows 用戶可能不太容易理解,除非你受到過 MS-DOS 命令行的影響。在 MS-DOS 命令行中,你可以使用一行指令構造一個命令的鏈,使得一個程序的輸出就可以成為下一個程序的輸入。第一個程序只是簡單地輸出到標準輸出上 (程序本身沒有做任何特別的重定向,只是執行了普通的 print 語句等),然后,下一個程序從標準輸入中讀取,操作系統就把一個程序的輸出連接到一個程序的輸入。

    例 10.12. 鏈接命令

    [you@localhost kgp]$ python kgp.py -g binary.xml         1
    01100111
    [you@localhost kgp]$ cat binary.xml                      2
    <?xml version="1.0"?>
    <!DOCTYPE grammar PUBLIC "-//diveintopython.org//DTD Kant Generator Pro v1.0//EN" "kgp.dtd">
    <grammar>
    <ref id="bit">
      <p>0</p>
      <p>1</p>
    </ref>
    <ref id="byte">
      <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
    <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
    </ref>
    </grammar>
    [you@localhost kgp]$ cat binary.xml | python kgp.py -g - 3 4
    10110001
    1 正如你在第 9.1 節 “概覽”中看到的,該命令將只打印一個隨機的八位字符串,其中只有 0 或者 1
    2 該處只是簡單地打印出整個 binary.xml 文檔的內容。(Windows 用戶應該用 type 代替 cat。)
    3 該處打印 binary.xml 的內容,但是“|”字符,稱為“管道”符,說明內容不會打印到屏幕上;它們會成為下一個命令的標準輸入,在這個例子中是你調用的 Python 腳本。
    4 為了不用指定一個文件 (例如 binary.xml),你需要指定“-”,它會使得你的腳本從標準輸入載入腳本,而不是從磁盤上的特定文件。 (下一個例子更多地說明了這是如何實現的)。所以效果和第一種語法是一樣的,在那里你要直接指定語法文件,但是想想這里的擴展性。讓我們把 cat binary.xml 換成別的什么東西――例如運行一個腳本動態生成語法――然后通過管道將它導入你的腳本。它可以來源于任何地方:數據庫,或者是生成語法的元腳本,或者其他。你根本不需要修改你的 kgp.py 腳本就可以并入這個功能。你要做的僅僅是從標準輸入取得一個語法文件,然后你就可以將其他的邏輯分離出來,放到另一程序中去了。

    那么腳本是如何“知道”在語法文件是“-”時從標準輸入讀取? 其實不神奇;它只是代碼。

    例 10.13. 在 kgp.py 中從標準輸入讀取

    
    def openAnything(source):
        if source == "-":    1
            import sys
            return sys.stdin
    
        # try to open with urllib (if source is http, ftp, or file URL)
        import urllib
        try:
    
    [... snip ...]
    1 這是 toolbox.py 中的 openAnything 函數,以前在第 10.1 節 “抽象輸入源”中你已經檢視過了。所有你要做的就是在函數的開始加入 3 行代碼來檢測源是否是“-”;如果是,返回 sys.stdin。就這么簡單!記住,stdin 是一個擁有 read 方法的類文件對象,所以其它的代碼 (在 kgp.py 中,在那里你調用了 openAnything) 一點都不需要改動。
    97av