Sanyanggae LogoSanyanggae

💻 वेब और फ्रंटएंड विकास

Unicode और UTF-8 में क्या अंतर है? बिगड़ा कोरियाई पाठ, बाइट, स्ट्रिंग की लंबाई और नॉर्मलाइज़ेशन

कोरियाई अक्षरों और इमोजी के कोड बिंदुओं, UTF-8 बाइटों और JavaScript स्ट्रिंग की लंबाई की तुलना करें; गलत डिकोडिंग, फ़ॉन्ट, नॉर्मलाइज़ेशन और मूल पाठ के संरक्षण को अलग समझें।

사냥개 (Sanyanggae)प्रकाशित अद्यतन 14 मिनट

यह कोरियाई मूल पाठ का अनुवाद है। कोरियाई मूल पाठ · अनुवाद समीक्षा: कृत्रिम बुद्धिमत्ता से समीक्षा

अक्षर और कोड बिंदु, UTF-8 बाइट तथा प्रदर्शन और नॉर्मलाइज़ेशन का संबंध दिखाता तीन-खानों वाला व्याख्यात्मक आरेख
Unicode अक्षर-नंबर, UTF-8 भंडारण बाइट और प्रदर्शन व तुलना-नियमों को अलग करता स्वनिर्मित आरेख; वास्तविक खराबी की स्क्रीन या माप का परिणाम नहीं।

1. अक्षर का नंबर और सहेजे जाने वाले बाइट अलग हैं

मान लें कि आपने कोरियाई दस्तावेज़ निर्यात करके दोबारा खोला और उसका शीर्षक अजीब अक्षरों में दिखा। लेखक की भाषा नहीं बदली; संभव है कि सहेजी गई संख्याएँ पढ़ने के नियम मेल न खाए हों। Unicode एक मानक है जो अक्षरों और चिह्नों को कोड बिंदु नामक नंबर देता है, जबकि UTF-8 उनके संबंधित मानों को बाइटों के क्रम में दर्शाने वाली एन्कोडिंग है। उदाहरण के लिए कोरियाई शब्दांश ‘가’ का कोड बिंदु U+AC00 है। U+ के बाद का लेखन षोडशाधारी है; यह फ़ाइल में क्रमांक या मेमोरी का पता नहीं बताता। यह नंबर फ़ॉन्ट में अक्षर की आकृति या स्क्रीन पर पिक्सेल की स्थिति भी तय नहीं करता। वही कोड बिंदु अलग फ़ॉन्ट और पाठ-सज्जा परिवेश में अलग दिख सकता है। इसलिए Unicode समर्थन, UTF-8 में सहेजना और कोरियाई फ़ॉन्ट लगाना जुड़े होने पर भी अलग-अलग जाँचने योग्य बातें हैं। Unicode की UTF व्याख्या अक्षर और भंडारण रूप का अंतर समझने की शुरुआत है।

UTF-8 में ‘가’ षोडशाधारी EA B0 80, यानी तीन बाइट बनता है। इसके विपरीत लैटिन A का कोड U+0041 है और UTF-8 में एक बाइट 41 है। बाइट आठ बिट की इकाई है; एक ही दस्तावेज़ में अलग अक्षरों की बाइट लंबाई अलग हो सकती है। केवल .txt या .csv विस्तार से वास्तविक एन्कोडिंग भी नहीं पता चलती। UTF-8 मान्य Unicode स्केलर मानों को एक से चार बाइट में दर्शाता है, इसलिए दिखाई देने वाले अक्षरों की संख्या को किसी स्थिर संख्या से गुणा करके फ़ाइल का सटीक आकार नहीं निकाल सकते। इस कोरियाई शब्दांश के उदाहरण को हर कोरियाई चिह्न या हर भाषा पर लागू भी नहीं करना चाहिए। एन्कोडिंग अक्षर-सूचना को बाइट में बदलने का नियम है, संपीड़न या गोपनीयता के लिए एन्क्रिप्शन नहीं। उसी वाक्य का कुल आकार फ़ाइल प्रारूप की अतिरिक्त जानकारी, पंक्ति-अंत और प्रसारण संपीड़न से बदल सकता है। WHATWG Encoding Standard के बाइट-अक्षर परिवर्तन को पूरी फ़ाइल के आकार और गोपनीयता से अलग रखने पर समस्या का दायरा घटता है।

2. बाइट, कोड इकाई और दिखने वाले अक्षरों की संख्या

JavaScript में स्ट्रिंग की length पढ़ने पर UTF-16 कोड इकाइयों की संख्या मिलती है। यह UTF-8 फ़ाइल के बाइट या व्यक्ति को महसूस होने वाले अक्षरों की संख्या नहीं है। उदाहरण के लिए ‘😀’ चेहरा एक कोड बिंदु U+1F600 है, लेकिन दो UTF-16 इकाइयाँ लेता है, इसलिए '😀'.length का मान 2 है। UTF-8 में इसका रूप F0 9F 98 80 है, यानी चार बाइट। codePointAt(0) से मिला दशमलव 128512 वही नंबर है जो U+1F600, न कि अक्षर-संख्या या फ़ाइल आकार। ‘가’ के लिए एक कोड बिंदु, एक UTF-16 इकाई और तीन UTF-8 बाइट हैं। एक स्ट्रिंग से अलग संख्याएँ इसलिए मिलती हैं कि माप की इकाइयाँ अलग हैं। MDN की स्ट्रिंग लंबाई संबंधी व्याख्या length की इकाई स्पष्ट करती है। इस संख्या से यह भी तय नहीं कर सकते कि JavaScript इंजन वास्तव में कितने बाइट मेमोरी इस्तेमाल करता है।

कोड बिंदुओं को अलग-अलग गिनना भी हमेशा उपयोगकर्ता को दिखाई देने वाले अक्षरों की गिनती नहीं है। मूल अक्षर के साथ संयोजक चिह्न जुड़ सकते हैं या कई कोड बिंदु एक इमोजी बना सकते हैं। Unicode पाठ-विभाजन नियम ऐसे विस्तारित ग्रैफीम समूहों की सीमाएँ बताते हैं। Array.from(text) जैसी कोड बिंदु के अनुसार स्ट्रिंग फैलाने वाली विधि कई बिंदुओं के समूह स्वतः नहीं गिनती। ब्राउज़र में परिवेश का समर्थन जाँचकर Intl.Segmenter की granularity को 'grapheme' रखने पर विचार किया जा सकता है। फिर भी ग्रैफीम समूह का फ़ॉन्ट की एक आकृति से एक-से-एक मेल निश्चित नहीं है और संपादन की अपेक्षाएँ भाषा के अनुसार बदल सकती हैं। परिचय लिखने के खाने में उपयोगकर्ता को समझ आने वाली अक्षर-गिनती रखें, जबकि भंडारण और संचार के लिए बाइट की सीमा अलग हो। स्क्रीन पर ‘20 अक्षर’ लिखकर भीतर केवल UTF-16 इकाइयाँ काटने से इमोजी या संयुक्त अक्षर बीच में टूट सकता है।

3. कोरियाई पाठ बिगड़े तो भंडारण, भेजना और दिखाना अलग देखें

बाइट लिखते समय की एन्कोडिंग और पढ़ते समय की डिकोडिंग अलग होने से पाठ बिगड़ सकता है। UTF-8 फ़ाइल को दूसरी एन्कोडिंग में पढ़ने पर फ़ाइल स्वयं न बदली हो, तब भी गलत अक्षर दिख सकते हैं। HTML में वास्तविक भंडारण प्रारूप, HTTP के Content-Type में charset और दस्तावेज़ के आरंभ का meta charset एक साथ जाँचें। HTML का lang='ko' सामग्री की भाषा बताता है; यह कोरियाई बाइट पढ़ने की एन्कोडिंग चुनने वाले charset से अलग है। W3C का एन्कोडिंग घोषणा मार्गदर्शन कहता है कि केवल घोषणा UTF-8 न करें, बल्कि फ़ाइल सचमुच UTF-8 में सहेजें और सर्वर व दस्तावेज़ की घोषणाएँ मिलाएँ। एन्कोडिंग का पहचान-चिह्न चुनाव की प्राथमिकता को भी प्रभावित कर सकता है; meta को हर दूसरी सूचना पर हमेशा हावी न मानें। फ़ाइल सीधे खोलने और सर्वर से पाने पर परिणाम अलग हों तो भेजने वाले चरण की सेटिंग महत्त्वपूर्ण सुराग है।

फ़ॉन्ट में आवश्यक आकृति न होने से दिखते चौकोर डिब्बे गलत डिकोडिंग से अलग समस्या हो सकते हैं। अक्षर-सूचना सही हो पर बनाने की आकृति न मिले तो भंडारण एन्कोडिंग बदलने से समाधान नहीं होगा। उलटे, पहले ही दूसरे अक्षरों की तरह पढ़े पाठ को नए फ़ॉन्ट में दिखाने से मूल अक्षर वापस नहीं आते। प्रभावित फ़ाइल की प्रतिलिपि सुरक्षित रखें और संपादक की एन्कोडिंग, सर्वर की घोषणा, ब्राउज़र के समझे अक्षर तथा फ़ॉन्ट क्रम से मिलाएँ। केवल दृश्य रूप देखकर कारण तय न करें; उसी अंश को कॉपी करने पर मिलने वाले कोड बिंदु भी देखें। दर्ज करें कि अंतर केवल एक ब्राउज़र में है या दूसरे संपादक भी फ़ाइल गलत पढ़ते हैं; इससे प्रदर्शन और डेटा-परिवर्तन अलग होंगे। सही अक्षर दिखने तक बार-बार सहेजने से मूल बाइट खो सकते हैं। सारी घोषणाएँ सही हों, लेकिन कोई विशेष पोस्ट खराब हो तो देखें कि डेटाबेस में जाने से पहले वह किस चरण में बदली।

4. TextEncoder और TextDecoder से वही मान जाँचें

सरल प्रयोग अपनी बनाई छोटी स्ट्रिंग से शुरू करें। TextEncoder JavaScript स्ट्रिंग को UTF-8 बाइट ऐरे में बदलता है। new TextEncoder().encode('가') का षोडशाधारी परिणाम EA B0 80 है और ऐरे की length 3 है। डेवलपर उपकरण वही मान दशमलव 234, 176, 128 दिखा सकते हैं; यह दूसरा डेटा नहीं, संख्या लिखने के आधार का अंतर है। उसी स्ट्रिंग की सामान्य length, जो 1 है, से तुलना करने पर इकाइयाँ स्पष्ट होती हैं। ऐरे को new TextDecoder('utf-8').decode(bytes) से पढ़ने पर ‘가’ मिलता है; यहाँ bytes पहले बनाया बाइट ऐरे है। एन्कोडर UTF-8 बनाता है, मनचाही पुरानी एन्कोडिंग में सहेजने वाला सार्वभौमिक परिवर्तक नहीं। वास्तविक फ़ाइल जाँचते समय पहले उसके मूल बाइट इस्तेमाल करें। गलत पढ़ी स्क्रीन की पंक्ति को दोबारा UTF-8 में एन्कोड करना यह सिद्ध नहीं करता कि पहली फ़ाइल में कौन-से बाइट थे।

TextDecoder के कंस्ट्रक्टर में 'utf-8' और { fatal: true } देने से नियम-विरुद्ध इनपुट को चुपचाप बदलने के बजाय त्रुटि बनाया जा सकता है। उदाहरण के लिए अकेला FF बाइट वैध UTF-8 नहीं है, इसलिए इस मोड में डिकोडिंग विफल होती है। सामान्य प्रतिस्थापन मोड में गलत हिस्से की जगह U+FFFD प्रतिस्थापन अक्षर आ सकता है। लेकिन त्रुटि न आने का अर्थ मूल एन्कोडिंग का पता लगना या सामग्री की सटीकता सत्यापित होना नहीं है। कुछ बाइट कई एन्कोडिंग में साझा होते हैं; पहले गलत अक्षरों में बदला परिणाम भी फिर वैध UTF-8 के रूप में सहेजा जा चुका हो सकता है। यह जाँच केवल चुने डिकोडर द्वारा इनपुट स्वीकार करने की है। यह दस्तावेज़ का स्रोत, मूल से मिलान या बीच में फ़ाइल के न बदले जाने की स्वतः पुष्टि नहीं करती।

5. नॉर्मलाइज़ेशन समान प्रतिनिधित्व मिलाने की अलग प्रक्रिया है

सही पढ़ी दो स्ट्रिंगों के अंदर भी कोड बिंदुओं का क्रम अलग हो सकता है। ‘가’ को एक U+AC00 से या जुड़ने वाले शुरुआती व्यंजन U+1100 और स्वर U+1161 के क्रम से दर्शाया जा सकता है। ये दोनों मान सामान्य संगतता जामो ‘ㄱ’ और ‘ㅏ’ को साथ लिखने से अलग हैं, इसलिए नंबर भी देखें। दोनों रूप सामान्यतः एक ही शब्दांश की आकृति बनाते हैं, पर JavaScript की कच्ची स्ट्रिंग तुलना में अलग हो सकते हैं। Unicode नॉर्मलाइज़ेशन नियमों के अनुसार दोनों पर NFC लगाने से यह कैनॉनिकली समतुल्य उदाहरण एक रूप में तुलना योग्य होता है। NFD कैनॉनिकल विघटन करता है। यहाँ NFC का UTF-8 रूप तीन बाइट है, जबकि NFD के दो जामो छह बाइट हैं; समान दिखने पर भी भंडारण आकार अलग हो सकता है। खोज और सहेजा नाम देखने में समान हों, पर मेल न खाएँ तो यह प्रतिनिधित्व-अंतर जाँच सकते हैं; इसे बाइट गलत पढ़ने से हुए अक्षर-विकार से अलग रखें।

नॉर्मलाइज़ेशन का रूप भी उद्देश्य के अनुसार चुनते हैं। NFC कैनॉनिकल समतुल्यता संभालता है, जबकि NFKC में कम्पैटिबिलिटी विघटन भी शामिल है, जो पूर्ण-चौड़ाई या गोले में बने अंकों जैसे अलग दर्ज रूपों को अधिक व्यापक ढंग से बदल सकता है। खोज की तुलना-प्रतिलिपि बनाने में यह उपयोगी हो सकता है, लेकिन गणितीय संकेत या मूल सामग्री में फर्क अर्थपूर्ण हो सकता है। इसलिए मूल को सुरक्षित रखकर आवश्यक तुलना-प्रति के लिए रूप तय करना उपयोगी है। नॉर्मलाइज़ेशन बड़े-छोटे लैटिन अक्षरों की तुलना, भाषा-आधारित क्रमबद्धता या समान उच्चारण वाले शब्द ढूँढ़ने का भी विकल्प नहीं है। पुराने फ़ाइल नामों या खाते के पहचानकर्ताओं को एक साथ बदलने से अलग प्रविष्टियाँ एक तुलना-मान में मिल सकती हैं; पहले नामकरण और टकराव-निपटान के नियम देखें। देखने में मिलते सभी अक्षरों को एक मान में सहेजना नॉर्मलाइज़ेशन का सामान्य लक्ष्य नहीं है।

6. मूल सुरक्षित रखें और परिवर्तन की सीमाएँ दर्ज करें

क्षति का कारण खोजते हुए पहली फ़ाइल पर लिखने से पहले मूल बाइट और मिलने का रास्ता बचाएँ। प्रतिलिपि को अपेक्षित एन्कोडिंग में पढ़ें और सही दिखते परिणाम को फिर सहेजने पर भी वही सामग्री रहने की जाँच करें। W3C की BOM व्याख्या के अनुसार UTF-8 का शुरुआती हस्ताक्षर EF BB BF है और ये बाइट अनिवार्य नहीं हैं। UTF-8 में बाइट क्रम चुनना नहीं पड़ता, इसलिए इसका काम UTF-16 के बाइट-क्रम चयन जैसा न समझें। BOM जोड़ना या हटाना और मुख्य बाइट दूसरी एन्कोडिंग में बदलना भी अलग काम हैं। यदि अपठनीय मान प्रतिस्थापन अक्षर या प्रश्नचिह्न बनकर सहेज दिए गए हों, तो कई मूल मान एक में मिल चुके हो सकते हैं; स्वतः बहाली की गारंटी नहीं है। मूल एन्कोडिंग और परिवर्तन-इतिहास ज्ञात होने पर ही उलटने की संभावना परखें; नॉर्मलाइज़ेशन खोए बाइट वापस लाने का साधन नहीं है।

बहुभाषी सुविधा को केवल एक अंग्रेज़ी पंक्ति पर न जाँचें। छोटी उदाहरण-सूची में कोरियाई शब्दांश, संयोजक चिह्न वाला लैटिन अक्षर, पूरक तल का इमोजी और अरबी या हिब्रू जैसा दाएँ से बाएँ पाठ शामिल करके इनपुट, भंडारण, दोबारा पढ़ना और निर्यात की तुलना करें। हर चरण में दृश्य रूप, कोड बिंदु, UTF-8 बाइट संख्या और उत्पाद द्वारा तय अक्षर-गिनती की इकाई देखें। पढ़ने की दिशा UTF-8 को किसी दूसरी एन्कोडिंग में बदलकर ठीक होने वाली विशेषता नहीं है; स्क्रीन की भाषा और दिशा भी अलग जाँचें। इनपुट सीमा या खोज तुलना बदलते समय भी मूल सुरक्षित रखें और नया मानदंड समझाएँ। ब्राउज़र का दिखाया अंक और सर्वर की स्वीकृति सीमा एक इकाई में हैं या नहीं, देखें; अलग सीमाएँ चाहिए तो हर सीमा दिखाएँ। यहाँ संख्याएँ और कोड छोटी उदाहरण स्ट्रिंगों का परिवर्तन दिखाते हैं, किसी खास सेवा की खराबी की माप नहीं। अक्षर का नंबर, सहेजे बाइट, स्ट्रिंग इकाई, दृश्य रूप और तुलना के नियम क्रम से जाँचकर हर सीमा पर हुए परिवर्तन को दोहराया जा सकता है।

साक्ष्य और संदर्भ

यह लेख सार्वजनिक स्रोतों की जानकारी समझाता है। स्रोत देना संबंधित संस्था की समीक्षा या प्रमाणन का संकेत नहीं है।

प्रकाशक का परिचय और संपर्क

इसी विषय के अन्य लेख