ضبط الترميز الصحيح عند توصيل الأنابيب في بايثون

سئل على ٢٩ يناير ٢٠٠٩  ·  تمت مشاهدة 219.6k مرة  ·  مصدر

Joakim Lundborg picture
في ٢٩ يناير ٢٠٠٩

عند تمرير إخراج برنامج Python ، يتم الخلط بين مترجم Python حول الترميز ويضبطه على None. هذا يعني برنامج مثل هذا:

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

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

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

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

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

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

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

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

الإجابات

Craig McQueen picture
في ٢٣ يوليو ٢٠٠٩
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)
nosklo picture
في ٢٩ يناير ٢٠٠٩
162

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

القاعدة الأساسية هي: استخدم دائمًا Unicode داخليًا. قم بفك تشفير ما تستقبله ، وقم بترميز ما ترسله.

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

مثال تعليمي آخر هو برنامج Python للتحويل بين 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. لا تفعل ذلك.

daveagp picture
في ٢٦ أكتوبر ٢٠١٠
134

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

TL ؛ د من مشاركة المدونة:

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
ö ☺ ☻
Sérgio picture
في ١٥ يونيو ٢٠١١
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

القط بي بي

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

print sys.stdout.encoding

الجري. / b.py

UTF-8

تشغيل ./b.py | أقل

None
CLaFarge picture
في ٢١ يونيو ٢٠١٥
5

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

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

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

أتمنى أن يساعدك هذا!

Tompa picture
في ١٣ أبريل ٢٠١٥
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'ÅÄÖåäö'
jno picture
في ١٥ مارس ٢٠١٢
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".

mike rodent picture
في ٧ مارس ٢٠١٤
2

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

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

NB2: أول سطرين من ملف .py هنا هما:

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

تتسبب آلية إنشاء سلسلة "٪" (AKA "عامل الاستيفاء") في حدوث مشكلات إضافية أيضًا ... إذا كان التشفير الافتراضي لـ "البيئة" هو 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"

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

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

cessor picture
في ٢٢ فبراير ٢٠١٨
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'
Basj picture
في ١٥ نوفمبر ٢٠١٩
1

على نظام Windows ، واجهت هذه المشكلة كثيرًا عند تشغيل كود Python من محرر (مثل Sublime Text) ، ولكن ليس عند تشغيله من سطر الأوامر.

في هذه الحالة ، تحقق من معلمات المحرر الخاص بك. في حالة 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"}
}