Python str बनाम यूनिकोड प्रकार


101

पायथन 2.7 के साथ काम करते हुए, मैं सोच रहा हूं कि unicodeइसके बजाय टाइप का उपयोग करने में क्या वास्तविक लाभ है str, क्योंकि दोनों ही यूनिकोड के तारों को पकड़ने में सक्षम हैं। वहाँ अलग में यूनिकोड कोड सेट करने में सक्षम किया जा रहा से कोई विशेष कारण नहीं है unicodeभागने चार का उपयोग कर तार \?:

इसके साथ एक मॉड्यूल निष्पादित करना:

# -*- coding: utf-8 -*-

a = 'á'
ua = u'á'
print a, ua

परिणाम: á, á

संपादित करें:

पायथन शेल का उपयोग करके अधिक परीक्षण:

>>> a = 'á'
>>> a
'\xc3\xa1'
>>> ua = u'á'
>>> ua
u'\xe1'
>>> ua.encode('utf8')
'\xc3\xa1'
>>> ua.encode('latin1')
'\xe1'
>>> ua
u'\xe1'

तो, unicodeस्ट्रिंग का उपयोग latin1करने के बजाय एन्कोड किया गया लगता है utf-8और कच्चे स्ट्रिंग का उपयोग करके एन्कोड किया गया है utf-8? मैं अब और भी उलझन में हूँ! : एस


इसके लिए कोई एन्कोडिंग नहीं है unicode, यह केवल यूनिकोड चरित्र का एक अमूर्त हिस्सा है; कुछ एन्कोडिंग (जैसे ) के साथ unicodeपरिवर्तित किया जा सकता है । strutf-8
बिन

जवाबों:


178

unicodeपाठ को संभालने के लिए है । पाठ कोड बिंदुओं का एक क्रम है जो एक बाइट से बड़ा हो सकता है । टेक्स्ट को कच्चे बाइट्स (जैसे , ...) के रूप में प्रदर्शित करने के लिए एक विशिष्ट एन्कोडिंग में एन्कोड किया जा सकता है ।utf-8latin-1

ध्यान दें कि unicode एन्कोडेड नहीं है ! अजगर द्वारा उपयोग किया जाने वाला आंतरिक प्रतिनिधित्व एक कार्यान्वयन विवरण है, और आपको इसके बारे में तब तक परवाह नहीं करनी चाहिए, जब तक कि यह उन कोड बिंदुओं का प्रतिनिधित्व करने में सक्षम है जो आप चाहते हैं।

strअजगर 2 के विपरीत बाइट्स का एक सीधा क्रम है । यह पाठ का प्रतिनिधित्व नहीं करता है!

आप unicodeकुछ पाठ के सामान्य प्रतिनिधित्व के रूप में सोच सकते हैं , जिसे कई अलग-अलग तरीकों से द्विआधारी डेटा के अनुक्रम में एन्कोड किया जा सकता है str

नोट: पायथन 3 में, unicodeका नाम बदल दिया गया था strऔर bytesबाइट्स के एक सादे अनुक्रम के लिए एक नया प्रकार है।

कुछ अंतर जो आप देख सकते हैं:

>>> len(u'à')  # a single code point
1
>>> len('à')   # by default utf-8 -> takes two bytes
2
>>> len(u'à'.encode('utf-8'))
2
>>> len(u'à'.encode('latin1'))  # in latin1 it takes one byte
1
>>> print u'à'.encode('utf-8')  # terminal encoding is utf-8
à
>>> print u'à'.encode('latin1') # it cannot understand the latin1 byte

ध्यान दें कि strएक विशिष्ट एन्कोडिंग प्रतिनिधित्व के एकल बाइट्स पर आपके पास निचले स्तर का नियंत्रण है, जबकि उपयोग करने पर unicodeआप केवल कोड-पॉइंट स्तर पर नियंत्रण कर सकते हैं। उदाहरण के लिए आप कर सकते हैं:

>>> 'àèìòù'
'\xc3\xa0\xc3\xa8\xc3\xac\xc3\xb2\xc3\xb9'
>>> print 'àèìòù'.replace('\xa8', '')
à�ìòù

UTF-8 वैध होने से पहले, अब और नहीं है। एक यूनिकोड स्ट्रिंग का उपयोग आप इस तरह से संचालित नहीं कर सकते हैं जिसके परिणामस्वरूप स्ट्रिंग मान्य यूनिकोड पाठ नहीं है। आप एक कोड बिंदु को हटा सकते हैं, एक कोड बिंदु को एक अलग कोड बिंदु आदि के साथ बदल सकते हैं लेकिन आप आंतरिक प्रतिनिधित्व के साथ गड़बड़ नहीं कर सकते।


4
आपके उत्तर के लिए बहुत बहुत धन्यवाद, इसने बहुत मदद की! मेरे लिए सबसे स्पष्ट हिस्सा है: "यूनिकोड एन्कोडेड नहीं है! अजगर द्वारा उपयोग किया जाने वाला आंतरिक प्रतिनिधित्व एक कार्यान्वयन विवरण है, और आपको इसकी परवाह नहीं करनी चाहिए [...]"। इसलिए, जब unicodeवस्तुओं को क्रमबद्ध करने से मुझे लगता है कि हमें पहले encode()उन्हें उचित एन्कोडिंग प्रारूप में स्पष्ट रूप से प्रस्तुत करना होगा, क्योंकि हम नहीं जानते कि unicodeमूल्य का प्रतिनिधित्व करने के लिए आंतरिक रूप से किसका उपयोग किया जा रहा है ।
Caumons

10
हाँ। जब आप कुछ पाठ (जैसे फ़ाइल में) सहेजना चाहते हैं, तो आपको इसे बाइट्स के साथ प्रस्तुत करना होगा, अर्थात आपको इसे एनकोड करना होगा। उस सामग्री को पुनः प्राप्त करते समय आपको पता होना चाहिए कि एन्कोडिंग का उपयोग किया गया था, ताकि किसी वस्तु में बाइट्स को डिकोड करने में सक्षम हो सके unicode
बकुरीउ

मुझे खेद है, लेकिन जो बयान unicodeएनकोडेड नहीं है वह सादा गलत है। UTF-16 / UCS-2 और UTF-32 / UCS-4 भी एन्कोडिंग हैं ... और भविष्य में इनमें से अधिक संभवतः बनाए जाएंगे। बिंदु, सिर्फ इसलिए कि आपको कार्यान्वयन विवरण के बारे में परवाह नहीं करनी चाहिए (और, वास्तव में, आपको नहीं करना चाहिए!), अभी भी इसका मतलब यह नहीं है कि unicodeएन्कोडेड नहीं है। यह जरूर है। चाहे वह .decode()'घ' एक अलग कहानी हो सकती है।
0xC0000022L

1
@ 0xC0000022L शायद वाक्य स्पष्ट नहीं है। यह कहना चाहिए: unicodeवस्तु आंतरिक प्रतिनिधित्व जो कुछ भी चाहता है वह एक गैर-मानक एक सहित हो सकता है। Python3 में विशेष रूप से + unicode करता है एक गैर मानक आंतरिक प्रतिनिधित्व का उपयोग करने वाले भी डेटा के आधार पर परिवर्तन होता है। इस तरह यह एक मानक एन्कोडिंग नहीं है । एक पाठ मानक के रूप में यूनिकोड केवल कोडपॉइंट्स को परिभाषित करता है जो पाठ का एक सार प्रतिनिधित्व है, स्मृति में यूनिकोड को सांकेतिक शब्दों में बदलना करने के लिए टन हैं जिसमें मानक यूएफ-एक्स आदि शामिल हैं। पायथन दक्षता के लिए अपने तरीके का उपयोग करता है।
बकुरीउ

1
@ 0xC0000022L इसके अलावा तथ्य यह है UTF-16 एन्कोडिंग है कि कुछ भी नहीं CPython के साथ क्या करना है unicode, वस्तु के बाद से यह नहीं है UTF-16, और न ही UTF-32 का उपयोग करें। यह एक तदर्थ प्रतिनिधित्व का उपयोग करता है, और यदि आप वास्तविक बाइट्स में डेटा को एनकोड करना चाहते हैं तो आपको इसका उपयोग करना होगा encode। इसके अलावा: भाषा अनिवार्य नहीं है कि कैसे unicodeकार्यान्वित किया जाता है, इसलिए विभिन्न संस्करणों या अजगर के कार्यान्वयन अलग आंतरिक प्रतिनिधित्व कर सकते हैं (और करते हैं )।
बकुरीउ

38

यूनिकोड और एनकोडिंग पूरी तरह से अलग, असंबंधित चीजें हैं।

यूनिकोड

प्रत्येक वर्ण को एक संख्यात्मक आईडी असाइन करता है:

  • 0x41 → ए
  • 0xE1 → á
  • 0x414 → मित्र

तो, यूनिकोड 0x41 को A, 0xE1 को á, और 0x414 को Д.

यहां तक ​​कि छोटा तीर → मैंने इसका यूनिकोड नंबर इस्तेमाल किया है, यह 0x2192 है। और यहां तक ​​कि इमोजी में उनके यूनिकोड नंबर हैं, have 0x1F602 है।

आप इस तालिका में सभी वर्णों की यूनिकोड संख्या देख सकते हैं । विशेष रूप से, आप यहाँ ऊपर तीन अक्षर , यहाँ तीर , और यहाँ इमोजी पा सकते हैं

यूनिकोड द्वारा सभी वर्णों को सौंपे गए इन अंकों को कोड पॉइंट कहा जाता है ।

इन सभी का उद्देश्य प्रत्येक चरित्र को स्पष्ट रूप से संदर्भित करने के लिए एक साधन प्रदान करना है। उदाहरण के लिए, अगर मैं 😂 के बारे में बात कर रहा हूं, तो "आप जानते हैं, यह हंसी इमोजी आँसू के साथ" के बजाय , मैं बस यूनिकोड कोड बिंदु 0x1F602 कह सकता हूं । आसान है, है ना?

ध्यान दें कि यूनिकोड कोड पॉइंट्स को आमतौर पर एक अग्रणी के साथ स्वरूपित किया जाता है U+, फिर हेक्साडेसिमल न्यूमेरिक वैल्यू को कम से कम दो अंकों में विभाजित किया जाता है। तो, उपरोक्त उदाहरण U + 0041, U + 00E1, U + 0414, U + 2192, U + 1F602 होंगे।

यूनिकोड कोड पॉइंट U + 0000 से U + 10FFFF तक होता है। यानी 1,114,112 नंबर। इन नंबरों में से 2048 का उपयोग सरोगेट्स के लिए किया जाता है , इस प्रकार, 1,112,064 रह जाते हैं। इसका अर्थ है, यूनिकोड 1,112,064 अलग-अलग वर्णों को एक अद्वितीय आईडी (कोड बिंदु) प्रदान कर सकता है। इन सभी कोड बिंदुओं को अभी तक एक पात्र को नहीं सौंपा गया है, और यूनिकोड को लगातार बढ़ाया जाता है (उदाहरण के लिए, जब नए इमोजीस पेश किए जाते हैं)।

याद रखने वाली महत्वपूर्ण बात यह है कि सभी यूनिकोड आसान और असंदिग्ध संदर्भ के लिए प्रत्येक वर्ण के लिए एक संख्यात्मक आईडी, कोड बिंदु, कहते हैं।

एन्कोडिंग

बिट पैटर्न के लिए मानचित्र वर्ण।

कंप्यूटर मेमोरी या डिस्क पर वर्णों का प्रतिनिधित्व करने के लिए इन बिट पैटर्न का उपयोग किया जाता है।

कई अलग-अलग एनकोडिंग हैं जो वर्णों के विभिन्न सबसेट को कवर करते हैं। अंग्रेजी बोलने वाली दुनिया में, सबसे आम एनकोडिंग निम्नलिखित हैं:

ASCII

मैप्स 128 अक्षर (कोड अंक U + 0000 से U + 007F) लंबाई 7 के बिट पैटर्न के लिए।

उदाहरण:

  • एक → 1100001 (0x61)

आप इस तालिका में सभी मैपिंग देख सकते हैं ।

आईएसओ 8859-1 (उर्फ लैटिन -1)

मैप्स 191 अक्षर (कोड अंक U + 0020 से U + 007E और U + 00A0 से U + 00FF) लंबाई 8 के बिट पैटर्न के लिए।

उदाहरण:

  • एक → 01100001 (0x61)
  • á → 11100001 (0xE1)

आप इस तालिका में सभी मैपिंग देख सकते हैं ।

UTF-8

मैप्स 1,112,064 अक्षर (सभी मौजूदा यूनिकोड कोड बिंदु) या तो लंबाई 8, 16, 24, या 32 बिट्स (जो कि, 1, 2, 3, या 4 बाइट्स) के बिट पैटर्न के लिए हैं।

उदाहरण:

  • एक → 01100001 (0x61)
  • á → 11000011 10100001 (0xC3 0xA1)
  • ≠ → 11100010 10001001 10100000 (0xE2 0x89 0xA0)
  • 😂 → 11110000 10011111 10011000 10000010 (0xF0 0x9F 0x98 0x82)

जिस तरह से UTF-8 ने बिट स्ट्रिंग्स के लिए वर्णों को एनकोड किया है, वह यहाँ बहुत अच्छी तरह से वर्णित है

यूनिकोड और एनकोडिंग्स

उपरोक्त उदाहरणों को देखते हुए, यह स्पष्ट हो जाता है कि यूनिकोड कैसे उपयोगी है।

उदाहरण के लिए, यदि मैं लैटिन -1 हूं और मैं á के अपने एन्कोडिंग को समझाना चाहता हूं, तो मुझे यह कहने की आवश्यकता नहीं है:

"मैं समझता हूं कि 11100001 के रूप में एक ऐगू के साथ (या हालांकि आप उस बढ़ते बार को कॉल करते हैं)"

लेकिन मैं सिर्फ इतना कह सकता हूं:

"मैं 11100001 के रूप में U + 00E1 को एनकोड करता हूं"

और अगर मैं UTF-8 हूं, तो मैं कह सकता हूं:

"मी, बदले में, मैंने U + 00E1 को 11000011 10100001 के रूप में एन्कोड किया"

और यह स्पष्ट रूप से हर किसी के लिए स्पष्ट है कि हम किस चरित्र का मतलब है।

अब अक्सर उत्पन्न होने वाले भ्रम के लिए

यह सच है कि कभी-कभी एन्कोडिंग का बिट पैटर्न, यदि आप इसे एक द्विआधारी संख्या के रूप में व्याख्या करते हैं, तो इस चरित्र का यूनिकोड कोड बिंदु समान है।

उदाहरण के लिए:

  • ASCII encodes एक 1100001, जो आप हेक्साडेसिमल संख्या के रूप में व्याख्या कर सकते हैं के रूप में 0x61 , और के यूनिकोड कोड बिंदु एक है U + 0061
  • लैटिन -1 encodes á 11,100,001 है, जो आप के रूप में हेक्साडेसिमल संख्या व्याख्या कर सकते हैं के रूप में 0xE1 , और के यूनिकोड कोड बिंदु á है U + 00E1

बेशक, यह सुविधा के उद्देश्य से इस तरह की व्यवस्था की गई है। लेकिन आपको इसे एक शुद्ध संयोग के रूप में देखना चाहिए । स्मृति में एक चरित्र को दर्शाने के लिए उपयोग किया जाने वाला बिट पैटर्न किसी भी तरह से इस चरित्र के यूनिकोड कोड बिंदु से बंधा नहीं है।

कोई यह भी नहीं कहता है कि आपको 11100001 जैसे एक बिट स्ट्रिंग की व्याख्या बाइनरी नंबर के रूप में करनी होगी। इसे बिट्स के अनुक्रम के रूप में देखें जो लैटिन -1 चरित्र á को एनकोड करने के लिए उपयोग करता है ।

अपने सवाल पर वापस

आपके पायथन दुभाषिया द्वारा उपयोग की जाने वाली एन्कोडिंग UTF-8 है

यहाँ आपके उदाहरणों में क्या हो रहा है:

उदाहरण 1

निम्नलिखित UTF-8 में चरित्र á को एन्कोड करता है। यह बिट स्ट्रिंग 11000011 10100001 में परिणाम करता है, जिसे चर में सहेजा जाता है a

>>> a = 'á'

जब आप aइसके मूल्य को देखते हैं , तो इसकी सामग्री 11000011 10100001 को हेक्स संख्या 0xC3 0xA1 और आउटपुट के रूप में स्वरूपित किया जाता है '\xc3\xa1':

>>> a
'\xc3\xa1'

उदाहरण 2

निम्नलिखित चर में यूआईकोड कोड बिंदु को बचाता है, जो कि U + 00E1 है, चर में ua(हम नहीं जानते कि स्मृति में कोड बिंदु U + 00E1 का प्रतिनिधित्व करने के लिए पायथन आंतरिक रूप से किस डेटा प्रारूप का उपयोग करता है, और यह हमारे लिए महत्वहीन है):

>>> ua = u'á'

जब आप के मूल्य को देखते हैं ua, तो पायथन आपको बताता है कि इसमें कोड बिंदु U + 00E1 है।

>>> ua
u'\xe1'

उदाहरण 3

यूएनएफ -8 के साथ यूनिकोड कोड पॉइंट यू + 00 ई 1 (चरित्र á का प्रतिनिधित्व) निम्नलिखित एनकोड करता है, जिसके परिणामस्वरूप बिट पैटर्न 11000011 10100001 होता है। फिर से, आउटपुट के लिए इस बिट पैटर्न को हेक्स संख्या 0xC3 0xA1 के रूप में दर्शाया जाता है:

>>> ua.encode('utf-8')
'\xc3\xa1'

उदाहरण 4

निम्नलिखित एनकोड कोड लाट -1 के साथ यूनिकोड कोड पॉइंट U + 00E1 (वर्ण á का प्रतिनिधित्व करता है), जिसके परिणामस्वरूप बिट पैटर्न 11100001 है। आउटपुट के लिए, इस बिट पैटर्न को हेक्स संख्या 0xE1 के रूप में दर्शाया गया है, जो कि संयोग से प्रारंभिक के समान है। कोड बिंदु U + 00E1:

>>> ua.encode('latin1')
'\xe1'

यूनिकोड ऑब्जेक्ट uaऔर लैटिन -1 एन्कोडिंग के बीच कोई संबंध नहीं है । Á का कोड बिंदु U + 00E1 है और á का लैटिन -1 एन्कोडिंग 0xE1 है (यदि आप द्विआधारी संख्या के रूप में एन्कोडिंग के बिट पैटर्न की व्याख्या करते हैं) एक शुद्ध संयोग है।


31

आपका टर्मिनल UTF-8 में कॉन्फ़िगर किया जाना है।

तथ्य यह है कि मुद्रण aकार्य एक संयोग है; आप टर्मिनल पर कच्चे UTF-8 बाइट्स लिख रहे हैं। aलंबाई दो का मान है , जिसमें दो बाइट्स हैं, हेक्स मान C3 और A1 हैं, जबकि uaलंबाई एक का यूनिकोड मान है , एक कोडपॉइंट U + 00E1 हैं।

लंबाई में यह अंतर यूनिकोड मूल्यों का उपयोग करने का एक प्रमुख कारण है; आप आसानी से बाइट स्ट्रिंग में पाठ वर्णों की संख्या को माप नहीं सकते हैं ; len()एक बाइट स्ट्रिंग की है कि कितने बाइट्स इस्तेमाल किया गया, कैसे नहीं कई अक्षर इनकोडिंग किया गया।

आप अंतर देख सकते हैं जब आप सांकेतिक शब्दों में बदलना अलग उत्पादन एन्कोडिंग को यूनिकोड मूल्य:

>>> a = 'á'
>>> ua = u'á'
>>> ua.encode('utf8')
'\xc3\xa1'
>>> ua.encode('latin1')
'\xe1'
>>> a
'\xc3\xa1'

ध्यान दें कि यूनिकोड मानक का पहला 256 कोडपॉइंट लैटिन 1 मानक से मेल खाता है, इसलिए U + 00E1 कोडपॉइंट को हेक्स मान E1 के साथ बाइट के रूप में लैटिन 1 में एन्कोड किया गया है।

इसके अलावा, पायथन यूनिकोड और बाइट स्ट्रिंग्स के अभ्यावेदन में एस्केप कोड का उपयोग करता है, और निम्न कोड पॉइंट जो प्रिंट करने योग्य ASCII नहीं हैं, वे \x..एस्केप वैल्यू के रूप में भी उपयोग किए जाते हैं। यही कारण है कि 128 और 255 दिखता है जो कोड बिंदु के साथ एक यूनिकोड स्ट्रिंग सिर्फ लैटिन 1 एन्कोडिंग की तरह। यदि आपके पास एक यूनिकोड स्ट्रिंग है जो U + 00FF से परे कोडपॉइंट्स के साथ एक अलग एस्केप सीक्वेंस है, तो \u....इसका उपयोग चार अंकों के xx मान के साथ किया जाता है।

ऐसा लगता है कि आप अभी तक पूरी तरह से नहीं समझते हैं कि यूनिकोड और एन्कोडिंग के बीच क्या अंतर है। कृपया जारी रखने से पहले कृपया निम्नलिखित लेख पढ़ें:


मैंने अपने प्रश्न को आगे के परीक्षण के साथ संपादित किया है। मैं थोड़ी देर के लिए यूनिकोड और अलग-अलग एनकोडिंग के लिए पढ़ रहा हूं और मुझे लगता है कि मैं सिद्धांत को समझता हूं, लेकिन जब वास्तव में पायथन कोड का परीक्षण कर रहा हूं तो मैं नहीं पकड़ता कि क्या हो रहा है
Caumons

1
लैटिन -1 एन्कोडिंग यूनिकोड मानक के पहले 256 कोडपॉइंट से मेल खाता है। यही कारण है कि करने के लिए U + 00E1 encodes \xe1लैटिन 1 में
मार्टिन पीटर्स

2
यह यूनिकोड का सबसे महत्वपूर्ण पहलू है। यह एन्कोडिंग नहीं है । यह पाठ है। यूनिकोड एक मानक है जिसमें बहुत अधिक, बहुत अधिक शामिल हैं, जैसे कोडपॉइंट्स, या व्हाट्सएप या अन्य श्रेणियों की जानकारी, बाएं से दाएं या बाएं से दाएं, आदि आदि प्रदर्शित की जानी चाहिए
मार्टीजन पीटरर्स

1
यह कहना ठीक है कि यूनिकोड एक "इंटरफ़ेस" जैसा है और एन्कोडिंग वास्तविक "कार्यान्वयन" जैसा है।
कौमन्स

2
@ वरुण: आपको पाइथन 2 नैरो बिल्ड का उपयोग करना चाहिए, जो UCS-2 का आंतरिक रूप से उपयोग करता है और U + FFFF की लंबाई दो के रूप में किसी भी चीज का गलत उपयोग करता है। अजगर 3 और एक यूसीएस -2 (विस्तृत) का निर्माण आप लंबाई दिखाएगा वास्तव में 1. है
मार्टिन पीटर्स

2

जब आप यूनिकोड के रूप में परिभाषित करते हैं, तो चार्ट ए और á बराबर होते हैं। अन्यथा á दो वर्णों के रूप में गिना जाता है। Len (a) और len (au) आज़माएँ। इसके अतिरिक्त, आपको एन्कोडिंग की आवश्यकता हो सकती है जब आप अन्य वातावरण के साथ काम करते हैं। उदाहरण के लिए यदि आप md5 का उपयोग करते हैं, तो आपको a और ua के लिए अलग-अलग मान मिलते हैं

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.