خطأ UnicodeEncode: لا يمكن ترميز برنامج الترميز 'charmap' - تعيين الأحرف إلى <undefined> ، وظيفة الطباعة

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

Carlos Eugenio Thompson Pinz&#243;n picture
في ٣١ يناير ٢٠١٣

أنا أكتب برنامج Python (Python 3.3) لإرسال بعض البيانات إلى صفحة ويب باستخدام طريقة POST. في الغالب من أجل عملية التصحيح ، أحصل على نتيجة الصفحة وأعرضها على الشاشة باستخدام الوظيفة print() .

الكود مثل هذا:

conn.request("POST", resource, params, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data.decode('utf-8'));

تقوم الطريقة HTTPResponse .read() بإرجاع عنصر bytes لترميز الصفحة (وهو مستند UTF-8 جيد التنسيق) بدا الأمر جيدًا حتى توقفت عن استخدام IDLE GUI لنظام Windows واستخدمت وحدة تحكم Windows بدلاً من ذلك. تحتوي الصفحة التي تم إرجاعها على حرف U + 2014 (em-dash) والذي تترجمه وظيفة الطباعة جيدًا في واجهة المستخدم الرسومية لـ Windows (أفترض أن صفحة الشفرة 1252) ولكنها ليست موجودة في وحدة تحكم Windows (صفحة الرمز 850). بالنظر إلى السلوك الافتراضي strict يظهر لي الخطأ التالي:

UnicodeEncodeError: 'charmap' codec can't encode character '\u2014' in position 10248: character maps to <undefined>

يمكنني إصلاحه باستخدام هذا الرمز القبيح تمامًا:

print(data.decode('utf-8').encode('cp850','replace').decode('cp850'))

الآن استبدل الحرف المخالف "-" بـ ? . ليست الحالة المثالية (يجب أن تكون الواصلة بديلاً أفضل) ولكنها جيدة بما يكفي لغرضي.

هناك العديد من الأشياء التي لا أحبها من الحل.

  1. الكود قبيح مع كل ذلك فك التشفير والترميز وفك التشفير.
  2. إنه يحل المشكلة لهذه الحالة فقط. إذا قمت بنقل البرنامج إلى نظام باستخدام بعض الترميز الآخر (latin-1 ، cp437 ، والعودة إلى cp1252 ، وما إلى ذلك) ، يجب أن يتعرف على التشفير الهدف. لم يحدث ذلك. (على سبيل المثال ، عند استخدام IDLE GUI مرة أخرى ، يتم فقد emdash أيضًا ، وهو ما لم يحدث من قبل)
  3. سيكون من الأجمل إذا تمت ترجمة الإمدش إلى واصلة بدلاً من ضجة استجواب.

لا تكمن المشكلة في emdash (يمكنني التفكير في عدة طرق لحل هذه المشكلة تحديدًا) ولكني بحاجة إلى كتابة رمز قوي. أقوم بتغذية الصفحة ببيانات من قاعدة بيانات ويمكن لهذه البيانات أن تعود. يمكنني توقع العديد من الحالات المتضاربة الأخرى: يمكن ترجمة 'Á' U + 00c1 (وهو أمر ممكن في قاعدة البيانات الخاصة بي) إلى CP-850 (ترميز DOS / Windows للغات أوروبا الغربية) ولكن ليس إلى CP-437 (ترميز للولايات المتحدة) اللغة الإنجليزية ، وهو الإعداد الافتراضي في العديد من تركيبات Windows).

إذن السؤال:

هل هناك حل أفضل يجعل الكود الخاص بي محايدًا من ترميز واجهة الإخراج؟

الإجابات

Dirk St&#246;cker picture
في ٢٠ أبريل ٢٠١٣
108

أرى ثلاثة حلول لهذا:

  1. قم بتغيير ترميز الإخراج ، لذلك سيخرج دائمًا UTF-8. انظر على سبيل المثال إعداد الترميز الصحيح عند توصيل الأنابيب stdout في Python ، لكن لم أتمكن من تشغيل هذه الأمثلة.

  2. بعد مثال رمز يجعل الإخراج على علم بمجموعة الأحرف المستهدفة.

    # -*- coding: utf-8 -*-
    import sys
    
    print sys.stdout.encoding
    print u"Stöcker".encode(sys.stdout.encoding, errors='replace')
    print u"Стоескер".encode(sys.stdout.encoding, errors='replace')
    

    يستبدل هذا المثال بشكل صحيح أي حرف غير قابل للطباعة في اسمي بعلامة استفهام.

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

  3. أعد تعيين ترميز الإخراج عالميًا في بداية البرنامج:

    تحتوي الصفحة http://www.macfreek.nl/memory/Encoding_of_Python_stdout على ملخص جيد لما يجب فعله لتغيير ترميز الإخراج. ولا سيما قسم "StreamWriter التفاف حول Stdout" مثير للاهتمام. في الأساس تقول لتغيير وظيفة ترميز الإدخال / الإخراج مثل هذا:

    في Python 2:

    if sys.stdout.encoding != 'cp850':
      sys.stdout = codecs.getwriter('cp850')(sys.stdout, 'strict')
    if sys.stderr.encoding != 'cp850':
      sys.stderr = codecs.getwriter('cp850')(sys.stderr, 'strict')
    

    في Python 3:

    if sys.stdout.encoding != 'cp850':
      sys.stdout = codecs.getwriter('cp850')(sys.stdout.buffer, 'strict')
    if sys.stderr.encoding != 'cp850':
      sys.stderr = codecs.getwriter('cp850')(sys.stderr.buffer, 'strict')
    

    إذا تم استخدامه في CGI لإخراج HTML ، يمكنك استبدال "صارم" بـ "xmlcharrefreplace" للحصول على علامات HTML المشفرة للأحرف غير القابلة للطباعة.

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

    # -*- coding: utf-8 -*-
    import sys
    import codecs
    sys.stdout = codecs.getwriter("iso-8859-1")(sys.stdout, 'xmlcharrefreplace')
    print u"Stöcker"                # works
    print "Stöcker".decode("utf-8") # works
    print "Stöcker"                 # fails
    
Jelle Fresen picture
في ١ مايو ٢٠١٥
30

بناءً على إجابة Dirk Stöcker ، إليك وظيفة غلاف أنيقة لوظيفة طباعة Python 3. استخدمه تمامًا كما لو كنت تستخدم الطباعة.

كمكافأة إضافية ، مقارنة بالإجابات الأخرى ، لن يطبع هذا النص الخاص بك على هيئة مصفوفة bytearray ('b "content"') ، ولكن كسلاسل عادية ('content') ، بسبب خطوة فك التشفير الأخيرة.

def uprint(*objects, sep=' ', end='\n', file=sys.stdout):
    enc = file.encoding
    if enc == 'UTF-8':
        print(*objects, sep=sep, end=end, file=file)
    else:
        f = lambda obj: str(obj).encode(enc, errors='backslashreplace').decode(enc)
        print(*map(f, objects), sep=sep, end=end, file=file)

uprint('foo')
uprint(u'Antonín Dvořák')
uprint('foo', 'bar', u'Antonín Dvořák')
jfs picture
في ٢٤ أغسطس ٢٠١٥
25

لأغراض التصحيح ، يمكنك استخدام print(repr(data)) .

لعرض النص ، قم دائمًا بطباعة Unicode. لا تقم بتشفير ترميز الأحرف لبيئتك مثل طريقة جيدة للحصول على مجموعة أحرف / ترميز استجابة HTTP في Python .

لطباعة Unicode إلى وحدة تحكم Windows ، يمكنك استخدام حزمة win-unicode-console .

leemonq picture
في ٩ مايو ٢٠١٧
21

لقد تعمقت في هذا ووجدت أفضل الحلول هنا.

http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python

في حالتي ، قمت بحل "خطأ UnicodeEncode: لا يمكن لبرنامج ترميز 'charmap' ترميز الحرف"

الكود الأصلي:

print("Process lines, file_name command_line %s\n"% command_line))

رمز جديد:

print("Process lines, file_name command_line %s\n"% command_line.encode('utf-8'))  
Željko Krnjić picture
في ٢٦ مايو ٢٠١٧
15

إذا كنت تستخدم سطر أوامر Windows لطباعة البيانات ، فيجب عليك استخدام

chcp 65001

لقد نجح هذا بالنسبة لي!

Solumyr picture
في ٢ مارس ٢٠١٧
1

إذا كنت تستخدم Python 3.6 (ربما 3.5 أو أحدث) ، فلن يعطيني هذا الخطأ بعد الآن. واجهت مشكلة مماثلة ، لأنني كنت أستخدم الإصدار 3.4 ، لكنه اختفى بعد أن قمت بإلغاء التثبيت وإعادة التثبيت.