34 पॉइंट द्वारा GN⁺ 2025-09-13 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • UTF-8 एक variable-length encoding तरीका है, जो लाखों अक्षरों को दर्शाते हुए भी ASCII के साथ backward compatibility बनाए रखता है
  • ASCII के समान 7-बिट क्षेत्र (U+0000~U+007F) को वही 1 byte में इस्तेमाल किया जाता है, इसलिए ASCII फ़ाइल अपने आप एक वैध UTF-8 फ़ाइल भी होती है
  • बाकी अक्षरों को 2~4 byte sequence में दर्शाया जाता है, और leading byte का bit pattern उसकी लंबाई बताता है, जबकि बाद के byte 10 से शुरू होकर continuation byte होने का संकेत देते हैं
  • इसी डिज़ाइन की वजह से UTF-8, universal character set को संभालते हुए भी मौजूदा ASCII सिस्टम के साथ पूरी तरह compatible रहता है और सबसे व्यापक रूप से इस्तेमाल किया जाने वाला character encoding बन गया
  • UTF-16, UTF-32 जैसे अन्य Unicode encoding ऐसे ASCII compatibility नहीं देते

UTF-8 डिज़ाइन की उत्कृष्टता

  • जब पहली बार UTF-8 encoding को देखा, तो इस बात ने बहुत प्रभावित किया कि यह अलग-अलग भाषाओं और अक्षरों के लाखों प्रकार को एक ही सिस्टम में समेटते हुए भी मौजूदा ASCII के साथ compatible structure बनाए रखता है
  • मूल रूप से UTF-8 अधिकतम 32 बिट तक का उपयोग करता है, लेकिन ASCII केवल 7 बिट का उपयोग करता है
  • UTF-8 के डिज़ाइन सिद्धांत इस प्रकार हैं
    • हर ASCII encoded फ़ाइल एक वैध UTF-8 फ़ाइल है
    • केवल ASCII अक्षरों वाली हर UTF-8 फ़ाइल एक वैध ASCII फ़ाइल है
  • केवल 128 अक्षरों तक सीमित पुराने सिस्टम और लाखों अक्षरों को समेटने वाली व्यवस्था को एक साथ जोड़ने का विचार बेहद नवोन्मेषी है

UTF-8 की मूल अवधारणा

  • UTF-8 एक variable-width character encoding है, जिसे Unicode character set के सभी अक्षरों को दर्शाने के लिए डिज़ाइन किया गया है
  • हर अक्षर को 1~4 byte में encode किया जाता है
  • पहले 128 अक्षर (U+0000~U+007F) single byte में store होते हैं, जिससे ASCII के साथ backward compatibility मिलती है
  • बाकी अक्षर 2, 3 या 4 byte में encode होते हैं
  • पहले byte के leading bit यह तय करते हैं कि encoding में कुल कितने byte होंगे
1-byte pattern byte की संख्या पूरा byte sequence pattern
0xxxxxxx 1 0xxxxxxx (सामान्य ASCII)
110xxxxx 2 110xxxxx 10xxxxxx
1110xxxx 3 1110xxxx 10xxxxxx 10xxxxxx
11110xxx 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  • multi-byte sequence के दूसरे, तीसरे और चौथे byte हमेशा 10 से शुरू होते हैं, जिससे यह स्पष्ट रूप से continuation byte के रूप में पहचाने जाते हैं
  • मुख्य byte और continuation byte के बाकी bit को जोड़कर एक code point बनाया जाता है
    • code point एक unique Unicode character identifier है, जिसे "U+" prefix और hexadecimal में लिखा जाता है
    • उदाहरण: "A" का code point U+0041 है
  • UTF-8 encoding byte से अक्षर को decode करने की प्रक्रिया इस प्रकार है
    • 1. byte पढ़ें; अगर शुरुआत 0 से हो, तो उसे single-byte character (ASCII) मानें, बाकी 7 bit से अक्षर दर्शाएँ और अगले byte पर जाएँ
    • 2. अगर 0 से शुरू नहीं होता, तो
      • 110 हो तो 2-byte character मानकर अगला 1 byte पढ़ें
      • 1110 हो तो 3-byte character मानकर अगले 2 byte पढ़ें
      • 11110 हो तो 4-byte character मानकर अतिरिक्त 3 byte पढ़ें
    • 3. तय किए गए byte से leading bit हटाकर बचे bit को जोड़ें और code point की binary value बनाएँ
    • 4. Unicode character set में उस code point को खोजकर स्क्रीन पर दिखाएँ
    • 5. अगले byte के लिए यही प्रक्रिया दोहराएँ

उदाहरण: हिंदी अक्षर "अ"

  • UTF-8 representation: 11100000 10100100 10000101 (3 byte)
  • पहला byte (11100000) → यह 3-byte character होने का संकेत देता है
  • तीनों byte के valid bit मिलाकर → 00001001 00000101 = hexadecimal 0x0905
  • code point U+0905 देवनागरी अक्षर "अ" को दर्शाता है

फ़ाइल उदाहरण

  • 1. Hey👋 Buddy

    • कुल 13 byte से बना है
      • ASCII अक्षर (H, e, y, B, u, d, d, y, स्पेस) → 1 byte each
      • 👋 (U+1F44B) → 4 byte 11110000 10011111 10010001 10001011
    • यह फ़ाइल एक वैध UTF-8 फ़ाइल है, लेकिन इसमें non-ASCII अक्षर (emoji) शामिल है, इसलिए यह ASCII के साथ backward compatible नहीं है
  • 2. Hey Buddy

    • कुल 9 byte, और सभी ASCII range में
    • इसलिए यह फ़ाइल एक साथ वैध ASCII फ़ाइल और वैध UTF-8 फ़ाइल दोनों है

अन्य encoding की तुलना

  • ASCII compatibility देने वाले कुछ अन्य encoding भी हैं, लेकिन UTF-8 जितने व्यापक रूप से इस्तेमाल नहीं होते
  • GB18030 (चीनी मानक) आदि भी ASCII compatibility देते हैं, लेकिन व्यापक नहीं हैं
  • ISO/IEC 8859 series single-byte extension (अधिकतम 256 अक्षर) है, इसलिए इसकी सीमाएँ हैं
  • UTF-16/UTF-32 में ASCII compatibility नहीं है
    • 'A' (U+0041): UTF-16 में 00 41, UTF-32 में 00 00 00 41

बोनस: UTF-8 Playground

  • UTF-8 encoding प्रक्रिया को visual तरीके से explore करने के लिए एक interactive tool
  • https://utf8-playground.netlify.app/

1 टिप्पणियां

 
GN⁺ 2025-09-13
Hacker News राय
  • UTF-8 में continuation bytes हमेशा 10 से शुरू होते हैं, इसलिए अगर आप किसी भी byte position पर jump करें तो तुरंत पता चल सकता है कि वह character की शुरुआत है या continuation byte. इसी वजह से अगले या पिछले character start को ढूंढना आसान होता है. अगर encoding EBML की variable-length integer encoding की तरह होती, जिसमें single-byte ASCII compatibility बनाए रखने के लिए 1/0 को उलटने वाला तरीका है, तो किसी arbitrary position से character start तुरंत पहचानना मुश्किल होता. अधिक जानकारी के लिए RFC8794 section 4.4 देखें

    • सही है, यह UTF-8 का बड़ा फायदा है. UTF-8 string को शुरुआत से पढ़े बिना भी उसमें आगे-पीछे स्वतंत्र रूप से move किया जा सकता है. Python में string indexing को character unit पर संभव बनाने के लिए CPython wide characters का उपयोग करता है. एक समय 2-byte या 4-byte characters में से चुनना संभव था, बाद में runtime पर अपने आप switch होने लगा. लेकिन वह फिर भी wide character है, UTF-8 नहीं. उदाहरण के लिए सिर्फ एक emoji भी string का size चार गुना बढ़ा सकता है. मैं तो इसके बजाय internal representation UTF-8 में रखकर index type को एक opaque object बनाने और उसमें छोटे integer जोड़ने-घटाने पर string के भीतर आगे-पीछे move कराने के implementation के बारे में सोचता रहा हूँ. वास्तव में integer में convert करने या सीधे subscript करने पर string index की गणना की जा सकती है. ऐसे approach में regular expressions जैसी चीजें भी opaque index object का उपयोग करके UTF-8 representation पर ठीक से काम कर सकती हैं

    • मुझे लगता है LEB128/VLQ, EBML variable-length integer scheme से बेहतर है. यह byte के भीतर MSB(सबसे ऊपरी bit) से distinguish करता है - 0 हो तो sequence समाप्त, अगला byte नई sequence, 1 हो तो पीछे तब तक scroll करो जब तक MSB 0 न मिले. इसके SIMD-optimized efficient implementations भी हैं. LEB128 और VLQ में फर्क सिर्फ endianness का है. ASCII 0xxxxxxx रहेगा, extended characters 1xxxxxxx 0xxxxxxx, 1xxxxxxx 1xxxxxxx 0xxxxxxx आदि के रूप में होंगे, और 3 bytes में अधिकतम 0x1FFFFF तक encode किया जा सकता है, जो Unicode की जरूरत से भी ज्यादा है. यह self-synchronizing नहीं होगा, लेकिन अधिक compact होगा. ASCII फिर भी 1 byte रहेगा, और गणितीय symbols या जापानी जैसे U+3FFF से नीचे के code points को 2 bytes में दिखाया जा सकता है, इसलिए code size कम करने में फायदेमंद हो सकता है

    • मेरा मानना है कि यह सिर्फ तभी संभव है जब text टूटा हुआ या maliciously tampered न हो. गलत UTF-8 sequences को parse या escape करते समय बहुत-सी security vulnerabilities सामने आ चुकी हैं. संबंधित उदाहरण CVE-2025-1094 PostgreSQL issue, और UTF-8 से संबंधित CVE सूची में देखे जा सकते हैं

    • यह हमेशा सही नहीं है. गलत UTF-8 होने पर continuation byte भी character में बदल सकता है. उदाहरण के लिए अगर input 0b01100001 0b10000000 0b01100001 हो, तो a�a जैसे तीन characters निकलते हैं. किसी output character की शुरुआत है या नहीं, यह जानने के लिए उससे पहले के 1~3 bytes देखने पड़ते हैं

    • अगर multibyte size अधिकतम 4 bytes है, तो पीछे अधिकतम 3 bytes देखकर तय किया जा सकता है कि current position continuation byte है या नहीं. अगर start byte न मिले तो समझ सकते हैं कि यह single-byte character है. मेरा अनुमान है कि यह design recovery के उद्देश्य से ऐसा रखा गया, ताकि library UTF-8 को सही तरह न भी पहचाने तो काटे गए slice के शुरू और अंत के invalid bytes को ignore करके फिर भी कुछ हद तक reasonable string निकाली जा सके

  • मुझे सच में लगता है कि UTF-8 शानदार है. इसकी कुंजी इस निर्णय में है कि ASCII ने सिर्फ 7 bits का इस्तेमाल किया. 1963 में भी 7 bits का चुनाव थोड़ा असामान्य था. सोचता हूँ कि यह सिर्फ ऐतिहासिक संयोग था या ASCII डिजाइन करने वालों ने एक और bit का उपयोग करके अतिरिक्त symbols जोड़ने का सोचा था, या फिर code pages और extensibility को ध्यान में रखा था

    • सटीक कारण तो नहीं पता, लेकिन पहले 8 bits हमेशा उपलब्ध नहीं होते थे. 7 bits + 1 parity या flag bit आम बात थी (इसीलिए e-mail आज भी quoted-printable के जरिए सिर्फ 7 bits से 8-bit data encode कर सकता है). 8 bits को बिना बदले पूरी तरह transmit कर पाने को 8-bit clean कहते हैं. इस संदर्भ में UTF-8 को ASCII के बचे हुए 8वें bit का बेहतरीन उपयोग भी कह सकते हैं. साथ में 8-bit clean का विवरण भी देख सकते हैं

    • मैं विशेषज्ञ नहीं हूँ, लेकिन ASCII का इतिहास पहले पढ़ा है. ASCII की जड़ teletypes code में है, जो telegraph code से विकसित हुआ था. Morse code variable-length था, इसलिए मशीन में लागू करना असुविधाजनक था. इसलिए 5-bit Baudot code आया. इसका उद्देश्य fixed-length code से मशीनों को सरल बनाना था, और operator की थकान कम करना भी था. Baudot code की वजह से आज भी symbol rate को baud कहते हैं. बाद में typewriter-आधारित punched tape input के कारण flexibility बढ़ी, तो Carriage Return और Line Feed जैसे special symbols जोड़े गए. शुरुआती computer industry ने punch cards को input के रूप में अपनाया, और IBM ने cards को तेज़ी से process करने के लिए नया 8-bit system बनाया, जो ASCII-आधारित था. आखिरकार तकनीकी प्रगति के साथ binary codes का विस्तार होता गया. ASCII भी 8-bit byte convention से पहले के संक्रमणकाल की उपज था

    • वास्तव में बची हुई bit को parity के लिए reuse करने का इरादा था

    • ASCII के 8-bit extensions (ISO 8859-x आदि) दशकों तक व्यापक रूप से इस्तेमाल हुए, और Windows standard code pages में आज भी मौजूद हैं. अगर ASCII शुरू से 8-bit होता, तब भी मुख्य characters पहले 128 positions में ही रहते, इसलिए UTF-8 के लिए फिर भी उपयुक्त रहता. अगर किसी चीज़ को ऐतिहासिक संयोग कहें, तो वह ASCII का 7-bit होना नहीं बल्कि यह है कि उस समय computer development मुख्यतः अंग्रेज़ी-भाषी दुनिया में हुआ, और अंग्रेज़ी 7 bits में पर्याप्त रूप से व्यक्त हो जाती थी

    • 7 bits अपने आप में कोई अजीब चीज़ नहीं थी. Baudot 5-bit था, फिर वह कम पड़ा तो 6-bit codes आए, और उसके बाद 7-bit ASCII बना. IBM ने System/360 में 8-bit byte (EBCDIC code) को standard किया, लेकिन दूसरे computer vendors के यहाँ byte length स्थिर नहीं था. 7 bits देखने में भले अटपटा लगे, पर characters और system word का neatly fit होना तब अनिवार्य नहीं था

  • मैं भी मानता हूँ कि UTF-8 अपेक्षा से कहीं बेहतर design है. लेकिन Unicode में scope बहुत फैल जाने की समस्या है. Unicode में आखिर क्या शामिल होना चाहिए, यह सवाल बना रहता है. सहज रूप से लगता है कि यह "मानव संचार में इस्तेमाल होने वाले सभी अलग-अलग printable characters" होने चाहिए, लेकिन वास्तव में ऐसा नहीं है.

    • distinction स्पष्ट नहीं है. कुछ code points combining use के लिए भी मौजूद हैं

    • यह specific भी नहीं है. एक ही character को कई तरीकों से लिखा जा सकता है. ऊपर से एक जैसे दिखने वाले characters भी अलग code points और अर्थ रख सकते हैं

    • सब printable नहीं हैं. control characters भी मौजूद हैं. ASCII compatibility के कारण उन्हें रखा गया, लेकिन इसके अपने control characters भी बढ़ते गए लगता है animated Unicode points अभी नहीं हैं. कम-से-कम printable चीज़ को कागज़ पर छापा जा सकता है. लेकिन नहीं पता कि भविष्य में भी यह invariance बनी रहेगी या नहीं. वैसे, लेखक ने जिस encoding का ज़िक्र नहीं किया वह utf-7 भी है. यह utf-8 जैसा है, लेकिन 80s के network environment में यह मानकर बनाया गया था कि आख़िरी bit का उपयोग सुरक्षित नहीं है. मुझे संयोग से कभी utf-7 encoded mail मिला था. आज तक समझ नहीं आया कि वह भेजा कैसे गया

    • UTF-7 मुख्यतः e-mail जैसे non-8-bit-clean transport environments के लिए बनाया गया था. अब यह पुराना पड़ चुका है, और supplemental plane encoding भी support नहीं करता (सिर्फ UTF-16 surrogate pair से संभव है). UTF-9 भी है, लेकिन वह April Fool RFC में पेश की गई parody है (PDP-10 जैसे 36-bit environments के लिए)

  • एक बात जो मैं हमेशा सोचता रहा हूँ: Unicode code point को अनावश्यक रूप से लंबे byte sequences से भी encode किया जा सकता है. UTF-8 इसे मना करता है और सिर्फ सबसे छोटा sequence ही मान्य करता है. उदाहरण के लिए 00000001 भी हो सकता है, और 11000000 10000001 भी वही अर्थ दे सकता है. तो क्या इसे शुरू से ऐसे design नहीं किया जा सकता था कि illegal encodings का अस्तित्व ही न रहे? उदाहरण के लिए 2-byte sequence की शुरुआत को सबसे आख़िरी valid value बना दें, ताकि 11000000 10000001 का अर्थ 128+1 हो, और 0-127 को 1 byte से रखा जाए. तब illegal codes भी नहीं होंगे और edge cases में strings थोड़ी छोटी भी होंगी. सोचता हूँ, शायद उस समय hardware cost की वजह से यह approach नहीं लिया गया. (अपडेट: असल bit sequence 10000001 होना चाहिए, उसे ठीक किया)

    • कई जवाब synchronization marker की बात करते हैं, लेकिन मूल सवाल यह है कि U+0080 को c2 80 क्यों encode किया जाता है, c0 80 क्यों नहीं, जो 7f के बाद पहली value होती. मुझे लगता है कारण ये हैं a) अगर overlong encoding की अनुमति दी जाए, तो कुछ systems केवल छोटे sequences check करेंगे और यह security hole बन जाएगा b) standard UTF-8 encoding/decoding सिर्फ masking(bitmask) और shifting(bitshift) से हो सकता है. प्रस्तावित तरीके में अतिरिक्त subtraction भी करनी पड़ेगी 1992 की e-mail discussion में इस पर बात हुई थी, और FSS-UTF में additive constants शामिल थे (नीचे देखें)

    2-byte sequence में 2^11 codes रखे जा सकते हैं, जिनमें 0-7f illegal हैं. शायद यह बिना किसी खास लाभ वाले additive constants की तुलना में बेहतर लगा होगा
    अधिक जानकारी के लिए utf-8-history.txt के अंत का भाग देखें

    • byte patterns की self-synchronicity बनाए रखना ही मुख्य बात है. अगर 11000000 10000001 जैसे sequence में continuation byte pattern न बना रहे, तो truncated UTF-8 stream में code point boundaries हमेशा खोज पाने की क्षमता खो जाएगी. और अगर इस scheme में addition/subtraction भी जोड़ दें, तो decoder performance घटेगी. अभी यह सिर्फ bit operations से हो जाता है

    • quectophoton की comment की तरह, continuation bytes का हमेशा 10 से शुरू होना ज़रूरी है ताकि parser किसी भी position से code point boundary ढूंढ सके. वास्तव में 90s की शुरुआत में UTF-8 design करते समय unreliable transport environments को ध्यान में रखा गया था

    • प्रस्तावित scheme में encoding/decoding की गणना अधिक जटिल और धीमी हो जाती. अभी कुछ bit shifts से काम हो जाता है, लेकिन उस समय (90s) के धीमे computers में यह महत्वपूर्ण था

  • अगर UTF-8 के design के बारे में और पढ़ना हो, तो Russ Cox का one-pager और Rob Pike का इतिहास-सार देखें

  • UTF-8 शानदार है और काश हर environment में इसका उपयोग होता (JavaScript की तरफ़ देख रहा हूँ). लेकिन इसकी एकमात्र कमी यह है कि invalid byte sequences को interpret करने का तरीका standard में साफ़ नहीं है. मुझे लगता है कि ऐसा design, जो "हर byte sequence के लिए interpretation अनिवार्य रूप से परिभाषित करे", और भी perfect होता. HTML5 spec जैसा approach सफलतापूर्वक काम कर सकता है

    • security के लिहाज़ से invalid UTF-8 को handle करने की कोशिश नहीं करनी चाहिए; उसे सीधे hazardous data की तरह reject करके error handle करना चाहिए. नहीं तो validation bypass वाले attacks के लिए रास्ता खुल जाता है
  • backward compatibility को लेकर मेरा रिश्ता मिला-जुला है. मुझे confusing चीज़ें पसंद नहीं, लेकिन सिर्फ इसलिए कुछ तोड़ देना कि "हम आगे बढ़ रहे हैं", यह भी मुझे अच्छा नहीं लगता. फिर भी UTF-8 या EAN जैसे उदाहरण, जो compatibility बनाए रखते हुए भी चतुराई से design किए गए हैं, बहुत संतोष देते हैं. सच कहूँ तो UTF-8 ने compatibility के लिए लगभग कुछ भी sacrifice नहीं किया

    • UTF-8 ने compatibility के लिए लगभग कुछ भी sacrifice नहीं किया
      21 bits से ऊपर की encoding को रोक दिया गया. यह UTF-16 compatibility की वजह से है (UTF-16 का surrogate mechanism 2^21-1 तक ही जा सकता है). संभव है कि कभी भविष्य में हमें इस सीमा पर पछताना पड़े. 21 bits से ऊपर code points रोकने का कोई और वास्तविक कारण नहीं दिखता

    • प्रगति के नाम पर ताकतवर लोग कुछ चीज़ों को ज़ोर से बदल देते हैं, यह तुम्हें पसंद है
      लेकिन जब कोई system, जिस पर आप निर्भर हैं, सिर्फ इसलिए टूट जाए कि किसी ने parameter name बदल दिया या standard library का कोई हिस्सा 'messy' दिखता था, तो वह मज़ेदार नहीं होता

    • अगर कुछ बदलना ही होता, तो शायद कुछ control characters को ज़्यादा आम characters से replace करके थोड़ी जगह बचाई जा सकती थी (अगर Unicode compatibility भी तोड़ने को तैयार हों). लेकिन multibyte character encoding format के रूप में इसे स्वतंत्र रूप से देखें तो भी यह लगभग optimal लगता है

  • मुझे UTF-8 playground का लिंक (utf8-playground.netlify.app) बहुत पसंद आया. अच्छा होगा अगर UI से सीधे code point input भी किया जा सके (अभी यह सिर्फ URL के जरिए संभव था). (अपडेट: यह पहले ही PR merge हो चुका है, इसलिए अब संभव है)

    • योगदान के लिए धन्यवाद, यह अब merge होकर तुरंत लागू हो चुका है
  • अगर आप इस विषय में और गहराई तक जाना चाहते हैं, और Advent of Code जैसी चीज़ें पसंद हैं, तो i18n-puzzles में text encoding से जुड़े कई puzzles हैं. यह UTF-8 और UTF-16 जैसी चीज़ों के काम करने के तरीके को पूरी तरह internalize करने में मदद करता है

  • अच्छा लेख था, धन्यवाद. मैं भी UTF-8 की सिफारिश करता हूँ, लेकिन मेरी राय में यह तभी अच्छा है जब इसे BOM के साथ इस्तेमाल किया जाए. नहीं तो application को पता नहीं चलता कि यह UTF-8 है, और यह भी छूट सकता है कि save भी UTF-8 में करना चाहिए. उदाहरण के लिए Windows में नया text document बनाते समय अगर file खाली हो और उसमें सिर्फ BOM हो, तो कोई भी app बाद में edit/save करते समय अपने आप पहचान लेगा कि इसे UTF-8 में save करना है. BOM न होने पर app encoding auto-detect करने की कोशिश करे भी, तो यह पूरी तरह भरोसेमंद नहीं होता, और accent जैसे special characters जुड़ते ही confusion बढ़ जाती है (editor language गलत अनुमान कर सकता है, या Notepad update के बाद default encoding बदल सकता है). इसलिए मैं UTF-8 के उपयोग से सहमत हूँ, लेकिन BOM को OS/app का default होना चाहिए