ضبط الترميز الصحيح عند توصيل الأنابيب القياسية في Python

StackOverflow https://stackoverflow.com/questions/492483

سؤال

عند توصيل مخرجات برنامج بايثون، يرتبك مترجم بايثون بشأن التشفير ويضبطه على لا شيء.يعني برنامج مثل هذا:

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

ستعمل بشكل جيد عند التشغيل بشكل طبيعي، ولكنها تفشل مع:

خطأ في ترميز Unicode:لا يمكن لبرنامج الترميز 'ascii' ترميز الحرف u'\xa0' في الموضع 0:ترتيبي ليس في النطاق(128)

عند استخدامها في تسلسل الأنابيب.

ما هي أفضل طريقة لإنجاز هذا العمل عند الأنابيب؟هل يمكنني فقط أن أخبره باستخدام أي ترميز لنظام الملفات/نظام الملفات/أي شيء يستخدم؟

الاقتراحات التي رأيتها حتى الآن هي تعديل site.py الخاص بك مباشرة، أو تشفير التشفير الافتراضي باستخدام هذا الاختراق:

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

هل هناك طريقة أفضل لجعل الأنابيب تعمل؟

هل كانت مفيدة؟

المحلول

والتعليمات البرمجية يعمل عند تشغيله في برنامج نصي لبيثون بترميز الإخراج إلى كل ما ترميز تطبيق محطة الخاص بك يستخدم. إذا كنت الأنابيب يجب ترميز ذلك بنفسك.

ووبحكم التجربة هو: دائما تستخدم Unicode داخليا. فك رموز ما تظهر، وترميز ما ترسلها.

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

ومثال تعليمي آخر هو برنامج بيثون لتحويل بين ISO-8859-1 وUTF-8، مما يجعل كل شيء أحرف كبيرة بينهما.

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. لا تفعل ذلك.

نصائح أخرى

أولاً بالنسبة لهذا الحل:

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

ليس من العملي الطباعة بشكل صريح باستخدام ترميز معين في كل مرة.سيكون ذلك متكررًا وعرضة للخطأ.

الحل الأفضل هو التغيير sys.stdout في بداية البرنامج، للتشفير باستخدام الترميز المحدد.إليك أحد الحلول التي وجدتها بايثون:كيف يتم اختيار sys.stdout.encoding؟, وخاصة تعليق "توكا":

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

وأنت قد ترغب في محاولة تغيير متغير البيئة "PYTHONIOENCODING" إلى "UTF_8". كنت قد كتبت الصفحة في محنة بلدي مع هذه المشكلة .

يرة تركية، والدكتور من بلوق وظيفة:

ويتيح لك

utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻
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)

وتحديث للرد على التعليق: المشكلة فقط موجودة عند الأنابيب إلى المعياري. أنا اختبرت في فيدورا 25 بيثون 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

وكان لي مشكلة مشابهة الأسبوع الماضي . كان من السهل إصلاح في بلدي IDE (PyCharm).

وهنا كان لي الإصلاح:

وانطلاقا من شريط القوائم PyCharm: ملف -> إعدادات -> محرر -> الترميز ملف، ثم تعيين: "ترميز IDE"، "ترميز المشروع" و "الترميز الافتراضي لملفات خصائص" ALL إلى UTF-8 و وقالت انها تعمل الآن وكأنه سحر.

وآمل أن يساعد هذا!

وهناك نسخة مصححة القول الإجابة كريغ ماكوين.

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'ÅÄÖåäö'

ويمكنني أن "أتمتة" مع دعوة ل:

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

نعم، فمن الممكن الحصول على حلقة لا نهائية هنا إذا فشل هذا "ابق ضاغطا".

اعتقدت أنني سأذكر شيئًا هنا كان علي أن أقضي وقتًا طويلاً في تجربته قبل أن أدرك أخيرًا ما كان يحدث.قد يكون هذا واضحًا جدًا للجميع هنا لدرجة أنهم لم يكلفوا أنفسهم عناء ذكره.لكن كان من الممكن أن يساعدني لو فعلوا ذلك، وعلى هذا المبدأ...!

ملحوظة:انا استخدم جيثون على وجه التحديد، الإصدار 2.7، لذلك ربما لا ينطبق هذا على سي بايثون...

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 الخاص بي) أو شيء مشابه، والذي يمكنه التعامل مع الأحرف ذات العلامات الأوروبية على الأقل، لذلك سيعمل.

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

سوف تعمل أيضا.

إذا قمت، OTOH، بالتوجيه إلى ملف من CLI، فسيكون ترميز stdout بلا، والذي سيكون افتراضيًا هو ASCII (على نظام التشغيل الخاص بي على أي حال)، والذي لن يكون قادرًا على التعامل مع أي من المطبوعات المذكورة أعلاه...(خطأ ترميز رهيب).

إذن قد تفكر في إعادة توجيه stdout الخاص بك باستخدام

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

وحاول تشغيل أنابيب CLI إلى ملف ...من الغريب جدًا أن الطباعة A أعلاه ستعمل ...لكن الطباعة B أعلاه سوف تؤدي إلى خطأ الترميز!ومع ذلك، فإن ما يلي سوف يعمل بشكل جيد:

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

الاستنتاج الذي توصلت إليه (مؤقتًا) هو أنه إذا كانت السلسلة المحددة لتكون a يونيكود يتم إرسال السلسلة التي تستخدم البادئة "u" إلى آلية التعامل مع % التي يبدو أنها تتضمن استخدام ترميز البيئة الافتراضية، بغض النظر عما إذا كنت قد قمت بتعيين stdout لإعادة التوجيه!

إن كيفية تعامل الناس مع هذا الأمر هي مسألة اختيار.أود أن أرحب بخبير Unicode ليقول لماذا يحدث هذا، وما إذا كنت قد أخطأت بطريقة ما، وما هو الحل المفضل لهذا، وما إذا كان ينطبق أيضًا على سي بايثون, سواء حدث ذلك في Python 3 وما إلى ذلك وما إلى ذلك.

في Ubuntu 12.10 وGNOME Terminal، لا يحدث أي خطأ عندما يقوم البرنامج بالطباعة إلى stdout أو توصيله بأنبوب لبرامج أخرى.كلا ترميز الملف والترميز الطرفي ترميز UTF-8.

$ cat a.py
# -*- coding: utf-8 -*-
print "åäö"
$ python a.py
åäö
$ python a.py | tee out
åäö

ما هو نظام التشغيل والمحاكي الطرفي الذي تستخدمه؟سمعت أن بعض زملائي لديهم مشاكل مماثلة عند الاستخدام iTerm 2 ونظام التشغيل العاشر؛قد يكون iTerm 2 هو السبب.

تحديث:هذه الإجابة خاطئة - راجع التعليقات للحصول على التفاصيل

وأنا واجهت هذه المشكلة في تطبيق إرث، وكان من الصعب تحديد أين ما كان المطبوعة. لقد ساعدت نفسي مع هذا الإختراق:

# 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)

لاحظ أن هذا يغير ALL يدعو الطباعة إلى استخدام الترميز، وبالتالي فإن وحدة التحكم طباعة هذا:

$ python test.py
b'Axwell \xce\x9b Ingrosso'
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top