Python에서 stdout을 파이핑 할 때 올바른 인코딩 설정


343

파이썬 프로그램의 출력을 파이핑 할 때, 파이썬 인터프리터는 인코딩에 대해 혼란스러워서 None으로 설정합니다. 이것은 다음과 같은 프로그램을 의미합니다 :

# -*- coding: utf-8 -*-
print u"åäö"

정상적으로 실행되면 정상적으로 작동하지만 다음과 같이 실패합니다.

UnicodeEncodeError : 'ascii'코덱은 위치 0에서 문자 u '\ xa0'을 인코딩 할 수 없습니다. 서 수가 범위 내에 있지 않습니다 (128)

파이프 시퀀스에서 사용될 때.

배관 할 때이 작업을 수행하는 가장 좋은 방법은 무엇입니까? 쉘 / 파일 시스템 / 사용중인 인코딩을 무엇이든 사용하도록 지시 할 수 있습니까?

지금까지 내가 본 제안은 site.py를 직접 수정 하거나이 핵을 사용하여 기본 인코딩을 하드 코딩하는 것입니다.

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print u"åäö"

배관 작업을하는 더 좋은 방법이 있습니까?



2
Windows에서이 문제가 발생하면 chcp 65001스크립트를 실행 하기 전에 실행할 수도 있습니다 . 여기에는 문제가있을 수 있지만 종종 도움이되며 입력을 많이 필요로하지 않습니다 (미만 set PYTHONIOENCODING=utf_8).
Tomasz Gandor

chcp 명령은 PYTHONIOENCODING 설정과 다릅니다. chcp는 터미널 자체의 구성 일 뿐이며 파일에 쓰는 것과는 아무런 관련이 없다고 생각합니다 (stdout을 파이핑 할 때 수행하는 작업입니다). 시도 setx PYTHONENCODING utf-8입력을 저장하려는 경우가 영구적으로.
ejm


나는 다소 관련된 문제에 직면하여 여기서 해결책을 찾았습니다.-> stackoverflow.com/questions/48782529/…
bkrishna2006

답변:


162

파이썬은 터미널 응용 프로그램이 사용하는 인코딩으로 출력을 인코딩하기 때문에 스크립트에서 실행될 때 코드가 작동합니다. 배관하는 경우 직접 인코딩해야합니다.

일반적으로 내부적으로 항상 유니 코드를 사용하십시오. 수신 한 내용을 디코딩하고 전송 한 내용을 인코딩하십시오.

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

또 다른 교훈적인 예는 ISO-8859-1과 UTF-8 사이를 변환하여 모든 것을 대문자로 만드는 Python 프로그램입니다.

import sys
for line in sys.stdin:
    # Decode what you receive:
    line = line.decode('iso8859-1')

    # Work with Unicode internally:
    line = line.upper()

    # Encode what you send:
    line = line.encode('utf-8')
    sys.stdout.write(line)

사용하는 일부 모듈과 라이브러리는 그것이 ASCII라는 사실에 의존 할 수 있기 때문에 시스템 기본 인코딩을 설정하는 것은 좋지 않습니다. 하지마


11
문제는 사용자가 인코딩을 명시 적으로 지정하고 싶지 않다는 것입니다. 그는 IO에 유니 코드를 사용하기를 원합니다. 그가 사용하는 인코딩은 터미널 응용 프로그램 설정이 아닌 로캘 설정에 지정된 인코딩이어야합니다. AFAIK, Python 3 은이 경우 로케일 인코딩을 사용합니다 . 변화 sys.stdout는 더 즐거운 방법 인 것 같습니다.
Andrey Vlasovskikh

4
인코딩 또는 디코딩 호출이 없거나 어딘가에 한 번 추가 될 때 모든 문자열의 인코딩 / 디코딩은 버그를 유발하도록 바인딩됩니다. 출력이 터미널 인 경우 출력 인코딩을 설정할 수 있으므로 출력이 터미널이 아닌 경우 설정할 수 있습니다. 그것을 지정하는 표준 LC_CTYPE 환경조차 있습니다. 파이썬에서는 이것을 존중하지 않습니다.
Rasmus Kaj

65
이 답변은 잘못되었습니다. 프로그램의 각 입력 및 출력을 수동으로 변환 해서는 안됩니다 . 그것은 깨지기 쉽고 완전히 유지할 수 없습니다.
Glenn Maynard

29
@ Glenn Maynard : IYO가 정답은 무엇입니까? '이 답변이 잘못되었습니다'
smci

14
@smci : 답변은 스크립트를 수정하지 않습니다 PYTHONIOENCODING. 파이썬 2에서 스크립트의 표준 출력을 재지 정하는 경우 설정 됩니다.
jfs

168

먼저이 솔루션에 대해 :

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

매번 주어진 인코딩으로 명시 적으로 인쇄하는 것은 실용적이지 않습니다. 반복적이고 오류가 발생하기 쉽습니다.

더 나은 솔루션은 sys.stdout프로그램 시작시 변경 하여 선택한 인코딩으로 인코딩하는 것입니다. 다음은 Python 에서 찾은 솔루션 중 하나 입니다. sys.stdout.encoding은 어떻게 선택됩니까? , 특히 "toka"의 주석 :

import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)

7
불행히도 sys.stdout을 유니 코드 만 허용하도록 변경하면 인코딩 된 바이트 문자열을 허용 할 것으로 예상되는 많은 라이브러리가 손상됩니다.
nosklo

6
nosklo : 그러면 출력이 터미널 일 때 어떻게 안정적으로 자동 작동 할 수 있습니까?
Rasmus Kaj

3
@Rasmus Kaj : 고유 한 유니 코드 인쇄 기능을 정의하고 유니 코드를 인쇄 할 때마다이 함수를 사용하십시오 : def myprint(unicodeobj): print unicodeobj.encode('utf-8')-검사하여 터미널 인코딩을 자동으로 감지 sys.stdout.encoding하지만 그 경우 None(예 : 출력을 파일로 리디렉션 할 때) 어쨌든 별도의 기능이 필요합니다.
nosklo

3
@nosklo : sys.stdout이 유니 코드 만 허용하지 않습니다. str과 unicode를 모두 StreamWriter에 전달할 수 있습니다.
Glenn Maynard

9
나는이 대답이 python2 용이라고 생각합니다. python2 및 python3을 모두 지원하도록 의도 된 코드에서는이 점에주의하십시오 . 나를 위해 python3에서 실행될 때 문제가 발생합니다.
wim

130

환경 변수 "PYTHONIOENCODING"을 "utf_8"로 변경하려고 할 수 있습니다. 나는 이 문제에 관한 나의 시련에 관한 페이지를 썼다 .

블로그 게시물의 Tl; dr :

import sys, locale, os
print(sys.stdout.encoding)
print(sys.stdout.isatty())
print(locale.getpreferredencoding())
print(sys.getfilesystemencoding())
print(os.environ["PYTHONIOENCODING"])
print(chr(246), chr(9786), chr(9787))

당신을 제공합니다

utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻

2
sys.stdout.encoding을 변경해도 작동하지 않지만 sys.stdout을 변경하면 작동 sys.stdout = codecs.getwriter(encoding)(sys.stdout)합니다. 이것은 파이썬 프로그램 내에서 수행 할 수 있으므로 사용자는 env 변수를 설정하지 않아도됩니다.
blueFast

7
@ jeckyll2hide : PYTHONIOENCODING작동합니다. 바이트가 텍스트로 해석되는 방식은 사용자 환경에 의해 정의됩니다 . 스크립트는 어떤 문자 인코딩을 사용할지 사용자 환경을 가정하고 지시해서는 안됩니다. Python이 설정을 자동으로 선택하지 않으면 PYTHONIOENCODING스크립트에 대해 설정할 수 있습니다. 출력이 파일 / 파이프로 리디렉션되지 않는 한 필요하지 않습니다.
jfs

8
+1. 솔직히 나는 그것이 파이썬 버그라고 생각합니다. 출력을 리디렉션 할 때 터미널에 있지만 파일에있는 동일한 바이트를 원합니다. 어쩌면 모든 사람을위한 것이 아니지만 좋은 기본값입니다. 일반적으로 "작동"하는 간단한 작업에 대한 설명없이 열심히 충돌하는 것은 나쁜 기본값입니다.
SnakE

@SnakE : 파이썬 구현이 의도적으로 시작 시간에 stdout에서 철분하고 영구적으로 인코딩을 선택하는 이유를 나중에 합리화 할 수있는 유일한 방법은 잘못 인코딩 된 항목이 나중에 나오는 것을 방지하기위한 것일 수 있습니다. 또는 변경하는 것은 구현되지 않은 기능 일뿐입니다.이 경우 사용자가 나중에 변경할 수 있도록하는 것은 합리적인 Python 기능 요청입니다.
daveagp

2
@daveagp 내 요점은, 내 프로그램의 행동은 프로그램이 리디렉션되는지 아닌지에 의존해서는 안된다는 것입니다. 파이썬은 다른 콘솔 도구에 대한 나의 경험과 상반됩니다. 이것은 가장 놀라운 원칙을 위반합니다. 나는 매우 강력한 근거가 없다면 디자인 결함이라고 생각합니다.
SnakE

62
export PYTHONIOENCODING=utf-8

일을하지만 파이썬 자체에서 설정할 수는 없습니다 ...

우리가 할 수있는 일은 설정되어 있지 않은지 확인하고 사용자에게 다음을 사용하여 호출 스크립트 전에 설정하도록 지시하는 것입니다.

if __name__ == '__main__':
    if (sys.stdout.encoding is None):
        print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout."
        exit(1)

주석에 응답하도록 업데이트하십시오. stdout으로 파이핑 할 때 문제가 발생했습니다. Fedora 25 Python 2.7.13에서 테스트했습니다.

python --version
Python 2.7.13

고양이 b.py

#!/usr/bin/env python
#-*- coding: utf-8 -*-
import sys

print sys.stdout.encoding

./b.py 실행 중

UTF-8

./b.py 실행 중 | 적게

None

2
이 검사는 Python 2.7.13에서 작동하지 않습니다. 로캘 값 sys.stdout.encoding에 따라 자동으로 설정 LC_CTYPE됩니다.
amphetamachine

1
mail.python.org/pipermail/python-list/2011-June/605938.html 예제는 여전히 작동합니다. 예를 들어 ./a.py> out.txt를 사용하는 경우 sys.stdout.encoding이 없음
Sérgio

Backblaze B2의 동기화 스크립트와 비슷한 문제가 있었으며 PYTHONIOENCODING = utf-8 내보내기로 문제를 해결했습니다. 데비안 스트레치에서 파이썬 2.7.
0x3333

5

지난주 비슷한 문제 가있었습니다 . 내 IDE (PyCharm)에서 쉽게 고칠 수있었습니다.

여기 내 수정이 있었다 :

PyCharm 메뉴 표시 줄에서 시작 : 파일-> 설정 ...-> 편집기-> 파일 인코딩 다음 "IDE 인코딩", "프로젝트 인코딩"및 "속성 파일의 기본 인코딩"을 모두 UTF-8로 설정하면 이제 작동합니다. 매력처럼.

도움이 되었기를 바랍니다!


4

크레이그 맥퀸의 대답은 위생적인 ​​버전입니다.

import sys, codecs
class EncodedOut:
    def __init__(self, enc):
        self.enc = enc
        self.stdout = sys.stdout
    def __enter__(self):
        if sys.stdout.encoding is None:
            w = codecs.getwriter(self.enc)
            sys.stdout = w(sys.stdout)
    def __exit__(self, exc_ty, exc_val, tb):
        sys.stdout = self.stdout

용법:

with EncodedOut('utf-8'):
    print u'ÅÄÖåäö'

2

나는 다음과 같은 호출로 "자동화"할 수 있었다.

def __fix_io_encoding(last_resort_default='UTF-8'):
  import sys
  if [x for x in (sys.stdin,sys.stdout,sys.stderr) if x.encoding is None] :
      import os
      defEnc = None
      if defEnc is None :
        try:
          import locale
          defEnc = locale.getpreferredencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.getfilesystemencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.stdin.encoding
        except: pass
      if defEnc is None :
        defEnc = last_resort_default
      os.environ['PYTHONIOENCODING'] = os.environ.get("PYTHONIOENCODING",defEnc)
      os.execvpe(sys.argv[0],sys.argv,os.environ)
__fix_io_encoding() ; del __fix_io_encoding

예,이 "setenv"가 실패하면 무한 루프를 얻을 수 있습니다.


1
흥미롭지 만 파이프는 이것에 대해 행복하지 않은 것 같습니다
n611x007

2

나는 여기서 무슨 일이 일어나고 있는지 깨닫기 전에 오랜 시간 동안 실험해야했던 것을 언급 할 것이라고 생각했습니다. 이것은 여기에있는 모든 사람들에게 너무나 명백하여 언급하지 않았습니다. 그러나 그들이 그렇게한다면 저에게 도움이 될 것입니다.

주의 : Jython을 특별히 v 2.7로 사용하고 있기 때문에 CPython 에는 적용되지 않을 수도 있습니다 ...

NB2 : 내 .py 파일의 첫 두 줄은 다음과 같습니다.

# -*- coding: utf-8 -*-
from __future__ import print_function

"%"(일명 "보간 연산자") 문자열 구성 메커니즘으로 인해 추가 문제가 발생합니다. "환경"의 기본 인코딩이 ASCII이고 다음과 같은 작업을 시도하면

print( "bonjour, %s" % "fréd" )  # Call this "print A"

Eclipse에서 실행하는 데 어려움이 없습니다 ... Windows CLI (DOS 창)에서 인코딩은 코드 페이지 850 (내 Windows 7 OS) 또는 이와 유사한 것으로 유럽 최소한의 악센트 문자를 처리 할 수 ​​있습니다. 작동합니다.

print( u"bonjour, %s" % "fréd" ) # Call this "print B"

작동합니다.

OTOH가 CLI에서 파일로 직접 전송하는 경우 stdout 인코딩은 None이며 기본적으로 ASCII로 설정됩니다 (어쨌든 내 OS에서) 위의 인쇄 중 하나를 처리 할 수 ​​없습니다 ... (두려운 인코딩 오류).

따라서 stdout을 다음과 같이 사용하여 리디렉션 할 수 있습니다.

sys.stdout = codecs.getwriter('utf8')(sys.stdout)

CLI 파이프에서 파일로 실행 해보십시오 ... 매우 이상하게도 위의 인쇄 A는 작동하지만 ... 위의 인쇄 B는 인코딩 오류를 발생시킵니다! 그러나 다음은 정상적으로 작동합니다.

print( u"bonjour, " + "fréd" ) # Call this "print C"

나는 (가짜)에 온 결론은 지정된 문자열이 될 경우이다 유니 코드 은 "U"접두어를 사용하여 문자열을 그 기본 환경 인코딩의 사용을 포함 나타나는 % -handling 메커니즘에 제출 에 관계없이 stdout을 리디렉션하도록 설정했는지 여부

사람들이 이것을 다루는 방법은 선택의 문제입니다. 유니 코드 전문가 가이 문제가 발생하는 이유, 내가 어떤 식으로 잘못했는지, 선호되는 솔루션, CPython 에도 적용 되는지 여부, 파이썬 3에서 발생하는지 등을 말하려고합니다.


그것은 이상한 일이 아닙니다. 왜냐하면 "fréd"바이트 시퀀스이고 유니 코드 문자열이 아니기 때문에 codecs.getwriter래퍼는 그것을 그대로 둡니다. 당신은 u또는 필요합니다 from __future__ import unicode_literals.
Matthias Urlichs

@MatthiasUrlichs OK ... 고마워요.하지만 IT의 가장 열악한 측면 중 하나를 인코딩하는 것을 발견했습니다. 어디서 이해를 얻습니까? 예를 들어, 방금 인코딩에 대한 또 다른 질문을 게시했습니다. stackoverflow.com/questions/44483067/… : 이것은 Java, Eclipse, Cygwin & Gradle에 관한 것입니다. 당신의 전문 지식이 지금까지 나아간다면, 무엇을 더 배울 수 있는지 알고 싶은 무엇보다도 도와주세요!
마이크 설치류

1

레거시 응용 프로그램에서이 문제가 발생하여 인쇄 된 위치를 식별하기가 어려웠습니다. 나는이 해킹으로 나를 도왔다 :

# encoding_utf8.py
import codecs
import builtins


def print_utf8(text, **kwargs):
    print(str(text).encode('utf-8'), **kwargs)


def print_utf8(fn):
    def print_fn(*args, **kwargs):
        return fn(str(*args).encode('utf-8'), **kwargs)
    return print_fn


builtins.print = print_utf8(print)

내 스크립트 위에 test.py :

import encoding_utf8
string = 'Axwell Λ Ingrosso'
print(string)

이렇게하면 인코딩을 사용하기 위해 모든 인쇄 호출이 변경되므로 콘솔에서 다음을 인쇄합니다.

$ python test.py
b'Axwell \xce\x9b Ingrosso'

1

Windows에서는 Sublime Text와 같은 편집기에서 Python 코드를 실행할 때이 문제가 매우 자주 발생했지만 명령 줄에서 실행하면 그렇지 않습니다 .

이 경우 편집기의 매개 변수를 확인하십시오. SublimeText의 경우 다음과 같이 Python.sublime-build해결되었습니다.

{
  "cmd": ["python", "-u", "$file"],
  "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
  "selector": "source.python",
  "encoding": "utf8",
  "env": {"PYTHONIOENCODING": "utf-8", "LANG": "en_US.UTF-8"}
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.