تغيير الترميز الافتراضي لبايثون؟

سئل على ١٦ فبراير ٢٠١٠  ·  تمت مشاهدة 305.2k مرة  ·  مصدر

Ali Nadalizadeh picture
في ١٦ فبراير ٢٠١٠

لدي العديد من مشكلات "يتعذر ترميزها" و "لا يمكنني فك تشفيرها" مع Python عندما أقوم بتشغيل تطبيقاتي من وحدة التحكم. ولكن في Eclipse PyDev IDE ، يتم تعيين ترميز الأحرف الافتراضي على UTF-8 ، وأنا بخير.

لقد بحثت عن تعيين الترميز الافتراضي ، ويقول الناس إن Python تحذف الوظيفة sys.setdefaultencoding عند بدء التشغيل ، ولا يمكننا استخدامها.

إذن ما هو أفضل حل لها؟

الإجابات

Eric O Lebigot picture
في ١٣ يوليو ٢٠١٣
165

إليك طريقة أبسط (اختراق) تعيد لك وظيفة setdefaultencoding() التي تم حذفها من sys :

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

(ملاحظة بخصوص Python 3.4+: reload() موجود في مكتبة importlib .)

هذا ليس بالأمر الآمن ، على الرغم من ذلك: من الواضح أن هذا اختراق ، حيث تمت إزالة sys.setdefaultencoding() عمداً من sys عند بدء Python. يمكن أن تؤدي إعادة تمكينه وتغيير الترميز الافتراضي إلى كسر الكود الذي يعتمد على ASCII باعتباره الإعداد الافتراضي (يمكن أن يكون هذا الرمز طرفًا ثالثًا ، مما يجعل إصلاحه مستحيلًا أو خطيرًا بشكل عام).

iman picture
في ٢١ نوفمبر ٢٠١٤
74

إذا حصلت على هذا الخطأ عند محاولة توجيه / إعادة توجيه إخراج البرنامج النصي الخاص بك

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

فقط قم بتصدير PYTHONIOENCODING في وحدة التحكم ثم قم بتشغيل التعليمات البرمجية الخاصة بك.

export PYTHONIOENCODING=utf8

lukmdo picture
في ٢٥ أكتوبر ٢٠١١
52

أ) للتحكم في المخرجات sys.getdefaultencoding() :

python -c 'import sys; print(sys.getdefaultencoding())'

ascii

ثم

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

و

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

يمكنك وضع ملف sitecustomize.py أعلى في PYTHONPATH .

قد ترغب أيضًا في تجربة reload(sys).setdefaultencoding بواسطةEOL

ب) للتحكم في stdin.encoding و stdout.encoding تريد تعيين PYTHONIOENCODING :

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

ثم

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

أخيرًا: يمكنك استخدام A) أو B) أو كليهما!

ChristopheD picture
في ١٦ فبراير ٢٠١٠
18

بدءًا من PyDev 3.4.1 ، لم يعد يتم تغيير الترميز الافتراضي. انظر هذه التذكرة لمزيد من التفاصيل.

بالنسبة للإصدارات السابقة ، يتمثل الحل في التأكد من أن PyDev لا يعمل مع UTF-8 باعتباره الترميز الافتراضي. ضمن Eclipse ، قم بتشغيل إعدادات الحوار ("تشغيل التكوينات ، إذا كنت أتذكر بشكل صحيح) ؛ يمكنك اختيار الترميز الافتراضي في علامة التبويب المشتركة. قم بتغييره إلى US-ASCII إذا كنت تريد أن تكون هذه الأخطاء "مبكرة" (بمعنى آخر: في بيئة PyDev الخاصة بك). راجع أيضًا منشور المدونة الأصلي لهذا الحل البديل .

kiril picture
في ١٦ سبتمبر ٢٠١٦
13

فيما يتعلق ببايثون 2 (وبايثون 2 فقط) ، تعتمد بعض الإجابات السابقة على استخدام الاختراق التالي:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

لا ينصح باستخدامه (تحقق من هذا أو هذا )

في حالتي ، يأتي مع تأثير جانبي: أنا أستخدم أجهزة الكمبيوتر المحمولة من ipython ، وبمجرد أن أقوم بتشغيل الكود ، لم تعد وظيفة ´print تعمل. أعتقد أنه سيكون هناك حل لذلك ، لكن ما زلت أعتقد أن استخدام الاختراق لا ينبغي أن يكون الخيار الصحيح.

بعد تجربة العديد من الخيارات ، كان الخيار الذي نجح معي يستخدم نفس الرمز في sitecustomize.py ، حيث من المفترض أن يكون هذا الجزء من الكود . بعد تقييم هذه الوحدة النمطية ، يتم إزالة وظيفة setdefaultencoding من النظام.

لذا فإن الحل هو إلحاق الملف /usr/lib/python2.7/sitecustomize.py الرمز:

import sys
sys.setdefaultencoding('UTF8')

عند استخدام virtualenvwrapper ، يكون الملف الذي أقوم بتحريره هو ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py .

وعندما أستخدم مع دفاتر بيثون وكوندا ، يكون السعر ~/anaconda2/lib/python2.7/sitecustomize.py

ibotty picture
في ١٧ يونيو ٢٠١٥
8

هناك منشور مدونة ثاقب حول هذا الموضوع.

راجع https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/ .

أعيد صياغة محتواها أدناه.

في Python 2 الذي لم يتم كتابته بقوة فيما يتعلق بترميز السلاسل ، يمكنك إجراء عمليات على سلاسل مشفرة بشكل مختلف ، والنجاح. على سبيل المثال ، يؤدي ما يلي إلى إرجاع True .

u'Toshio' == 'Toshio'

قد ينطبق ذلك على كل سلسلة (عادية ، غير مسبوقة) تم ترميزها في sys.getdefaultencoding() ، والتي كانت افتراضية إلى ascii ، ولكن ليس غيرها.

كان من المفترض أن يتم تغيير الترميز الافتراضي على مستوى النظام في site.py ، ولكن ليس في مكان آخر. كانت الاختراقات (المعروضة هنا أيضًا) لتعيينها في وحدات المستخدم هي: الاختراقات ، وليس الحل.

قام Python 3 بتغيير ترميز النظام إلى الإعداد الافتراضي utf-8 (عندما يكون LC_CTYPE مدركًا للشفرة الموحدة) ، ولكن تم حل المشكلة الأساسية مع مطلب تشفير سلاسل "بايت" بشكل صريح كلما تم استخدامها مع سلاسل Unicode.

kxr picture
في ٩ فبراير ٢٠١٧
4

أولاً: reload(sys) وتعيين بعض الترميز الافتراضي العشوائي فقط فيما يتعلق بالحاجة إلى تدفق طرفي الإخراج هو ممارسة سيئة. غالبًا ما يغير reload الأشياء في النظام التي تم وضعها وفقًا للبيئة - مثل sys.stdin / stdout streams و sys.excepthook وما إلى ذلك.

حل مشكلة التشفير على stdout

أفضل حل أعرفه لحل مشكلة الترميز لسلاسل unicode print وما بعد ascii str (على سبيل المثال من القيم الحرفية) على sys.stdout هو: الاهتمام بنظام sys .stdout (كائن يشبه الملف) وهو قادر ومتسامح اختياريًا فيما يتعلق بالاحتياجات:

  • عندما يكون sys.stdout.encoding هو None لسبب ما ، أو غير موجود ، أو خطأ خطأ أو "أقل" مما تستطيعه بالفعل المحطة الطرفية أو الدفق stdout ، ثم حاول تقديم .encoding الصحيح السمة sys.stdout & sys.stderr بترجمة كائن يشبه الملف.

  • عندما لا يزال الجهاز الطرفي / الدفق غير قادر على ترميز جميع أحرف unicode التي تحدث ، وعندما لا تريد كسر print السبب ، يمكنك تقديم سلوك ترميز مع استبدال في ملف الترجمة- مثل الكائن.

هنا مثال:

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

class SmartStdout:
    def __init__(self, encoding=None, org_stdout=None):
        if org_stdout is None:
            org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
        self.org_stdout = org_stdout
        self.encoding = encoding or \
                        getattr(org_stdout, 'encoding', None) or 'utf-8'
    def write(self, s):
        self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
    def __getattr__(self, name):
        return getattr(self.org_stdout, name)

if __name__ == '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

    us = u'aouäöüфżß²'
    print us
    sys.stdout.flush()

استخدام القيم الحرفية لسلسلة ما وراء ascii في كود Python 2/2 + 3

السبب الوحيد الجيد لتغيير الترميز الافتراضي العام (إلى UTF-8 فقط) على ما أعتقد يتعلق بقرار رمز مصدر التطبيق - وليس بسبب مشكلات ترميز دفق الإدخال / الإخراج: لكتابة ما وراء سلسلة أحرف ascii في رمز دون فرض لاستخدام u'string' style هروب unicode دائمًا. يمكن القيام بذلك بشكل متسق (على الرغم مما تقوله مقالة # encoding: utf-8 " أو ascii (بدون تصريح). قم بتغيير أو إسقاط المكتبات التي لا تزال تعتمد بطريقة غبية جدًا على أخطاء التشفير الافتراضية لـ ascii التي تتجاوز chr # 127 (وهو أمر نادر اليوم).

وافعل هذا في بداية التطبيق (و / أو عبر sitecustomize.py) بالإضافة إلى مخطط SmartStdout أعلاه - بدون استخدام reload(sys) :

...
def set_defaultencoding_globally(encoding='utf-8'):
    assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
    import imp
    _sys_org = imp.load_dynamic('_sys_org', 'sys')
    _sys_org.setdefaultencoding(encoding)

if __name__ == '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

بهذه الطريقة تعمل النصوص الحرفية ومعظم العمليات (باستثناء تكرار الحرف) بشكل مريح دون التفكير في تحويل يونيكود كما لو كان هناك Python3 فقط. يحتاج إدخال / إخراج الملف بالطبع دائمًا إلى عناية خاصة فيما يتعلق بالترميز - كما هو الحال في Python3.

ملاحظة: يتم تحويل سلاسل السهول ضمنيًا من utf-8 إلى unicode في SmartStdout قبل تحويلها إلى ترميز تدفق الإخراج.

Att Righ picture
في ٢٥ مايو ٢٠١٧
4

هذا هو الأسلوب الذي استخدمته لإنتاج كود متوافق مع كل من python2 و python3 وأنتج دائمًا إخراج utf8 . لقد وجدت هذه الإجابة في مكان آخر ، لكن لا يمكنني تذكر المصدر.

يعمل هذا الأسلوب عن طريق استبدال sys.stdout بشيء لا يشبه الملف تمامًا (ولكن لا يزال يستخدم الأشياء الموجودة في المكتبة القياسية فقط). قد يتسبب هذا في مشاكل للمكتبات الأساسية الخاصة بك ، ولكن في الحالة البسيطة حيث يكون لديك تحكم جيد في كيفية استخدام sys.stdout من خلال إطار العمل الخاص بك ، يمكن أن يكون هذا نهجًا معقولًا.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
Dalton Bentley picture
في ٦ يونيو ٢٠١٧
1

يعد هذا اختراقًا سريعًا لأي شخص (1) يعمل على نظام Windows الأساسي (2) يقوم بتشغيل Python 2.7 و (3) منزعجًا لأن برنامجًا رائعًا (على سبيل المثال ، لم تكتبه أنت ، لذا لم يكن مرشحًا على الفور لطباعة التشفير / فك التشفير المناورات) لن تعرض "أحرف unicode الجميلة" في بيئة IDLE (تطبع Pythonwin جيدًا رموز unicode) ، على سبيل المثال ، رموز First Order Logic الأنيقة التي يستخدمها Stephan Boyer في الإخراج من المثل التربوي في

لم تعجبني فكرة فرض إعادة تحميل sys ولم أستطع جعل النظام يتعاون مع متغيرات البيئة المتغيرة مثل PYTHONIOENCODING (جرب متغير بيئة Windows المباشر وأيضًا إسقاط ذلك في sitecustomize.py في حزم الموقع كواحد لاينر = 'utf-8').

لذلك ، إذا كنت على استعداد لاختراق طريقك إلى النجاح ، فانتقل إلى دليل IDLE الخاص بك ، عادةً: "C: \ Python27 \ Lib \ idlelib" حدد موقع الملف IOBinding.py. قم بعمل نسخة من هذا الملف وقم بتخزينه في مكان آخر حتى تتمكن من العودة إلى السلوك الأصلي عندما تختار. افتح الملف في idlelib باستخدام محرر (على سبيل المثال ، IDLE). انتقل إلى منطقة الرمز هذه:

# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()

encoding = "ascii"
if sys.platform == 'win32':
    # On Windows, we could use "mbcs". However, to give the user
    # a portable encoding name, we need to find the code page 
    try:
        # --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
        # --> encoding = locale.getdefaultlocale()[1]
        encoding = 'utf-8'
        codecs.lookup(encoding)
    except LookupError:
        pass

بعبارة أخرى ، قم بالتعليق على سطر الكود الأصلي بعد " try " الذي كان يجعل متغير الترميز مساويًا لـ locale.getdefaultlocale (لأن ذلك سيمنحك cp1252 وهو ما لا تريده) وبدلاً من ذلك أجبره على "utf-8" (عن طريق إضافة السطر " encoding =" utf-8 "كما هو موضح).

أعتقد أن هذا يؤثر فقط على عرض IDLE على stdout وليس الترميز المستخدم لأسماء الملفات وما إلى ذلك (الذي تم الحصول عليه مسبقًا في تشفير نظام الملفات). إذا كانت لديك مشكلة مع أي رمز آخر تقوم بتشغيله في IDLE لاحقًا ، فما عليك سوى استبدال ملف IOBinding.py بالملف الأصلي غير المعدل.

twasbrillig picture
في ١٢ أبريل ٢٠١٨
1

هذا أصلح المشكلة بالنسبة لي.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"