2013-10-31 144 views
2

我有以下簡單的程序:管道輸出使蟒程序失敗

# -*- coding: utf-8 -*- 

GREEK = u'ΑΒΓΔ ΕΖΗΘ ΙΚΛΜ ΝΞΟΠ ΡΣΤΥ ΦΧΨΩ αβγδ εζηθ ικλμ νξοπ ρςτυ φχψω' 

print GREEK 

在終端上運行該產生作爲人口會:

$ python test.py 
ΑΒΓΔ ΕΖΗΘ ΙΚΛΜ ΝΞΟΠ ΡΣΤΥ ΦΧΨΩ αβγδ εζηθ ικλμ νξοπ ρςτυ φχψω 

但輸出通過管道到另一個程序,原因錯誤:

$ python test.py | less 

Traceback (most recent call last): 
    File "test.py", line 5, in <module> 
    print GREEK 
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128) 
Traceback (most recent call last): 
    File "ddd.py", line 5, in <module> 
    print GREEK 
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128) 
  • 這是爲什麼失敗?爲什麼重定向會影響程序的運行方式?我本來期望在shell中運行的程序是總是重定向:有時是到終端程序,有時是到另一個程序(在這種情況下爲less)。爲什麼「目標」程序會影響源程序的執行?
  • 我可以做些什麼來確保程序獨立運行,無論它是發送到終端還是發送到另一個目的地?
+0

和http:// stackove rflow.com/questions/17918746/print-unicode-string-to-console-ok-but-fails-when-redirect-to-a-file-how-to-fix/17918823#17918823和http:// stackoverflow。理解-python-unicode-and-linux-terminal和http://stackoverflow.com/questions/17430168/python-encoding-issue-when-using-linux –

+0

首先,_you_知道什麼編碼其他程序期望它的stdin?如果是這樣,你必須告訴Python。如果沒有,這裏沒有合理的答案,因爲Python不能在你不能的時候奇蹟般地知道它...... – abarnert

回答

1

您的輸出程序的編碼不支持字符。另一種方法是始終編碼任何不在您的程序中,並在需要時將其解碼回來。

# -*- coding: utf-8 -*- 

GREEK = u'ΑΒΓΔ ΕΖΗΘ ΙΚΛΜ ΝΞΟΠ ΡΣΤΥ ΦΧΨΩ αβγδ εζηθ ικλμ νξοπ ρςτυ φχψω' 

print GREEK.encode('utf-8') 

這工作,但它只會顯示編碼字符串,而不是原來的,因爲你的終端應用程序不使用相同的編碼。

+0

這是一個替代方案,但它意味着我的代碼中的很多變化:現在所有的打印語句必須改變:( – dangonfast

+0

我還有一個問題:我如何管道(在這種情況下'less')知道輸入流的編碼? – dangonfast

+0

AFAIK,我不認爲該功能可用。唯一的方法是確保一切都編碼正確 – aIKid

4

在此基礎上的其他相關問題,我已經實現了以下解決方案,這似乎是相當強大的,並且不需要在我的代碼庫的所有打印語句的任何變化:

# -*- coding: utf-8 -*- 

import sys 

def set_output_encoding(encoding='utf-8'): 
    import sys 
    import codecs 
    '''When piping to the terminal, python knows the encoding needed, and 
     sets it automatically. But when piping to another program (for example, 
     | less), python can not check the output encoding. In that case, it 
     is None. What I am doing here is to catch this situation for both 
     stdout and stderr and force the encoding''' 
    current = sys.stdout.encoding 
    if current is None : 
     sys.stdout = codecs.getwriter(encoding)(sys.stdout) 
    current = sys.stderr.encoding 
    if current is None : 
     sys.stderr = codecs.getwriter(encoding)(sys.stderr) 

GREEK = u'ΑΒΓΔ ΕΖΗΘ ΙΚΛΜ ΝΞΟΠ ΡΣΤΥ ΦΧΨΩ αβγδ εζηθ ικλμ νξοπ ρςτυ φχψω' 

set_output_encoding() 

print GREEK 
print >> sys.stderr, GREEK 

爲了測試這個:

python ddd.py    # Do not pipe anything 
python ddd.py | less  # Pipe stdout, let stderr go to the terminal 
python ddd.py 2>&1 | less # Pipe both stdout and stderr to less 

他們都產生預期:

ΑΒΓΔ ΕΖΗΘ ΙΚΛΜ ΝΞΟΠ ΡΣΤΥ ΦΧΨΩ αβγδ εζηθ ικλμ νξοπ ρςτυ φχψω 
ΑΒΓΔ ΕΖΗΘ ΙΚΛΜ ΝΞΟΠ ΡΣΤΥ ΦΧΨΩ αβγδ εζηθ ικλμ νξοπ ρςτυ φχψω 
+0

這是一個很好的解決方案! – aIKid