कैरेक्टर एन्कोडिंग और Unicode
Unicode दुनिया के characters की सूची है: हर अक्षर, अंक, चिह्न और emoji का एक नंबर होता है जिसे code point कहते हैं, जैसे Arabic अक्षर ع के लिए U+0639। Character encoding वह नियम है जो इन नंबरों को storage और transmission के लिए bytes में बदलता है और वापस भी। UTF-8 web की encoding है: यह हर code point को 1 से 4 bytes में लिखती है, इसलिए A एक byte है, ع दो, न तीन और 😀 चार।
Text defects के तीन आम कारण होते हैं:दो layers encoding पर सहमत नहीं होतीं, दो code point sequences एक जैसी दिखती हैं पर comparison में अलग निकलती हैं, या length एक component में bytes में और दूसरे में characters में गिनी जाती है। यह पाठ दिखाता है कि हर समस्या bytes में कैसी दिखती है और एक fixed multilingual dataset तीनों को कैसे test करता है।
यह क्यों ज़रूरी है
Saudi Arabia या India में इस्तेमाल होने वाला product text handling पर टिका है: customer का नाम علي के रूप में save होना, invoice PDF में address की जगह question marks, ऐसा search जो user को इसलिए नहीं ढूँढ पाता क्योंकि उसका नाम accent की दूसरी form में type हुआ था, 30-character की limit जो 30 अक्षरों का Arabic नाम इसलिए reject कर देती है क्योंकि उसे bytes में नापा गया। इनमें से हर एक ऐसा defect है जो user को पहले ही दिन मिल सकता है, अक्सर उसके अपने नाम वाले field में।
Tester के लिए encoding defects ढूँढने में सस्ते और छूट जाने पर महँगे होते हैं। ये English test data के पीछे छिपे रहते हैं, सिर्फ़ दो systems की सीमा पर दिखते हैं (browser से API, API से database, database से export), और garbled text एक बार वापस save हो जाए तो वह सही text की तरह store हो जाता है, इसलिए हर अगला reader वही नुकसान दोहराता है। एक छोटा dataset और इस बात की साफ़ तस्वीर कि bytes कहाँ characters बनते हैं, इन्हें release से पहले पकड़ लेते हैं।
Code points, UTF-8 और बाक़ी encodings
Unicode U+0000 से U+10FFFF तक code points परिभाषित करता है, और application जो text handle करता है वह इन्हीं का sequence है। Encoding हर code point को bytes से जोड़ती है, और bytes hexadecimal में लिखे जाते हैं, हर byte के लिए 00 से FF तक दो अंक। RFC 3629 में परिभाषित UTF-8 128 ASCII characters के लिए एक byte इस्तेमाल करती है, इसलिए जिस file में सिर्फ़ ASCII characters हों, वह पहले से valid UTF-8 है; accents वाले ज़्यादातर Latin अक्षरों और Greek, Cyrillic, Hebrew तथा Arabic के लिए दो bytes; Devanagari, Chinese, Japanese और आम इस्तेमाल की ज़्यादातर दूसरी scripts के लिए तीन; और ज़्यादातर emoji तथा U+FFFF से ऊपर की हर चीज़ के लिए चार।
UTF-16 ज़्यादातर characters को एक 16-bit unit में और U+FFFF से ऊपर के characters को दो units में रखती है, जिन्हें surrogate pair कहते हैं। JavaScript strings, Java और Windows APIs इन्हीं units में गिनते हैं, इसीलिए '😀'.length 2 है। पुरानी single-byte encodings, जैसे Western European भाषाओं के लिए Windows-1252 और Arabic के लिए Windows-1256 या ISO 8859-6, में ज़्यादा से ज़्यादा 256 characters आते हैं, इसलिए इनमें, उदाहरण के लिए, Arabic और Devanagari एक ही text में नहीं रखे जा सकते; ये legacy databases, CSV exports और e-mail systems में अब भी ज़िंदा हैं, और ज़्यादातर mojibake यहीं से शुरू होता है।
Aعन😀 में चार code points हैं पर दस UTF-8 bytes:A एक byte लेता है, ع दो, न तीन और emoji चार।String characters के रूप में: चार code points
भाग 1:Aभाग 2:عभाग 3:नभाग 4:í ½í¸€
वही string UTF-8 bytes के रूप में, hex में: दस bytes
भाग 1:41
- 1–44 code points, 10 UTF-8 bytes और 5 UTF-16 units
- 1Latin अक्षर A
U+0041: एक byte, अपनी ASCII value जैसा ही1 byte - 2Arabic अक्षर ऐन
U+0639: दो bytes, Greek, Cyrillic और Hebrew अक्षरों की तरह2 bytes - 3देवनागरी अक्षर न
U+0928: तीन bytes, ज़्यादातर दूसरी scripts की तरह3 bytes - 4मुस्कुराता चेहरा emoji (Grinning face)
U+1F600: चार bytes;U+FFFFसे ऊपर, इसलिए दो UTF-16 units भी4 bytes
पाठ संस्करण
String: A ع न 😀 4 code points
Char Code point UTF-8 bytes UTF-16 units
A U+0041 41 1
ع U+0639 D8 B9 1
न U+0928 E0 A4 A8 1
😀 U+1F600 F0 9F 98 80 2 (surrogate pair)
Total: 4 characters, 10 UTF-8 bytes, 5 UTF-16 unitsMojibake: bytes सही, decoder गलत
Mojibake वह garbled text है जो तब दिखता है जब एक encoding में लिखे bytes दूसरी encoding में पढ़े जाते हैं। Bytes आम तौर पर सही-सलामत होते हैं; पढ़ने वाले ने गलत अंदाज़ा लगाया। UTF-8 में store किया café है 63 61 66 C3 A9; जो component इन bytes को Windows-1252 मानकर पढ़ता है वह café दिखाता है, क्योंकि उस table में C3 है à और A9 है ©। नुकसान तब फैल जाता है जब garbled text वापस save हो जाए: तब café valid UTF-8 के रूप में फिर से encode हो जाता है, हर अगला reader उसे वैसा ही दिखाता है, और original सिर्फ़ गलत conversion को record-दर-record उलटकर ही वापस मिल सकता है, जो हमेशा सफल नहीं होता।
हर layer कोई encoding declare करती है या मान लेती है: Content-Type: text/html; charset=utf-8 header और HTML में <meta charset="utf-8">; JSON, जिसे RFC 8259 किसी closed ecosystem के बाहर exchange होने पर UTF-8 में होना अनिवार्य करता है, और जिसका application/json media type कोई charset parameter परिभाषित नहीं करता; URLs, जहाँ UTF-8 bytes percent-encode होते हैं (ع %D8%B9 बनकर जाता है); database columns और connections; byte order mark (EF BB BF) वाली या बिना वाली CSV files; e-mail templates और log files। Mojibake वहीं पैदा होता है जहाँ इनमें से दो layers असहमत हों।
Garbled text का रूप बताता है कि सामने defect किस तरह का है:
- Latin text में
éऔर€, ArabicØऔरÙकी लड़ियों के रूप में (سلامबन जाता हैسلام), Devanagarià¤औरà¥की लड़ियों के रूप में: UTF-8 bytes किसी Western single-byte encoding में पढ़े गए। - अक्षरों की जगह
?: text ऐसी encoding में convert हुआ जिसमें उन characters की जगह ही नहीं, और वे हमेशा के लिए खो गए। �, यानी replacement characterU+FFFD: decoder को ऐसा byte sequence मिला जो UTF-8 में invalid है, अक्सर UTF-8 मानकर पढ़ा गया Windows-1256 text या multi-byte character के बीच में कटी हुई string।- खाली boxes, या dotted circle पर बना vowel sign: bytes सही decode हुए हैं, पर font में उस character का glyph नहीं है, या shaping engine (renderer का वह हिस्सा जो अक्षरों को जोड़ता है और उन पर marks लगाता है) को ऐसा mark मिला जिसके पहले कोई अक्षर नहीं, जैसे text अक्षर और उसकी मात्रा के बीच कट गया हो। तब encoding नहीं, rendering और truncation की जाँच करें।
Normalization और characters की गिनती
एक जैसा दिखने वाला text अलग-अलग code points में store हो सकता है। é एक code point (U+00E9) है या दो (e और उसके बाद combining acute accent U+0301); fatha जैसे short-vowel mark वाला Arabic अक्षर हमेशा दो होता है, और أ एक (U+0623) या दो (ا और उसके बाद hamza above U+0654) हो सकता है। UAX #15 में परिभाषित Unicode normalization text को एक तय form में बदलती है: NFC ऐसे sequences को compose करती है और NFD उन्हें decompose। Compatibility forms NFKC और NFKD और आगे जाती हैं और ligature fi को fi, full-width A को A और Arabic presentation form ﻻ को ل और ا में भी fold कर देती हैं; search के लिए यह सही है, पर जैसा type हुआ वैसा नाम store करने के लिए गलत। Normalization एक digit system को दूसरे में नहीं बदलती: Arabic-Indic ٣ NFKC में भी ٣ ही रहता है; यह locale का मामला है, अगले पाठ के लिए।
जब तक product अपनी boundaries पर normalize नहीं करता, दो users José दो बार register कर सकते हैं, एक keyboard से type किए नाम का search दूसरे keyboard से type किए उसी नाम को miss कर देता है, और एक form में set किया password दूसरी form में fail हो जाता है। पूछें कि product कौन-सी form इस्तेमाल करता है और कहाँ लागू करता है; W3C की internationalization guidance web के text के लिए NFC की सिफ़ारिश करती है।
Length के चार जवाब हैं। User grapheme clusters गिनता है, यानी UAX #29 में परिभाषित user-perceived characters; database column bytes गिन सकता है; JavaScript का length UTF-16 units गिनता है; Python code points गिनता है, जबकि Go और Rust bytes गिनते हैं। चारों सिर्फ़ सादे ASCII पर सहमत होते हैं:
String Graphemes Code points UTF-16 units UTF-8 bytes
Ali 3 3 3 3
علي 3 3 3 6
مُحَمَّد 4 8 8 16
José (NFC) 4 4 4 5
José (NFD) 4 5 5 6
हिंदी 2 5 5 15
👍🏽 1 2 4 8आज़माएँ: स्ट्रिंग लेंथ काउंटर आज़माएँ: Unicode डेटासेट जनरेटर
Worked example: name field की end-to-end testing
Requirement: "Display name, 2 से 30 characters, profile पर, e-mails में और user list के CSV export में दिखता है।" Encoding को round trip की तरह test किया जाता है: एक fixed dataset enter करें, save करें, reload करें, API से वापस पढ़ें, export करें, और हर पड़ाव की तुलना original से करें, आँख से नहीं, bytes से।
- Dataset एक बार बनाएँ और सँभालकर रखें: accents वाली Latin, diacritics के साथ और बिना Arabic, vowel signs वाली Devanagari, skin tone वाला emoji, और वही नाम NFC और NFD में। Unicode dataset generator हर तरह की strings उनके counts के साथ बनाता है, और string length counter आपकी अपनी जोड़ी किसी भी string के चारों counts देता है।
- हर string enter करें, save करें, profile reload करें और दिखे text की तुलना input से diff tool में करें; फिर वही user API से fetch करें और JSON value के hex bytes की तुलना original के hex से करें।
- Limit जाँचें:
عليदस बार दोहराने पर 30 अक्षर और 60 bytes बनते हैं। अगर यह reject हो जबकि 30 Latin अक्षर accept हों, तो limit bytes गिनती है; अगर👍🏽4 गिना जाए, तो UTF-16 units। दर्ज करें कि कौन-सा है, और पूछें कि requirement का मतलब user-perceived characters था या नहीं। - NFC वाला
Josésave करने के बाद NFD वाला search करें, और उल्टा भी; फिर CSV export को spreadsheet और plain text editor दोनों में खोलें औरÃ,Ø,?और�ढूँढें। - नाम छापने वाली एक e-mail और एक PDF trigger करें, और server log पढ़ें, जहाँ Arabic अक्सर
\u0639escapes के रूप में दिखती है: यह सही है, बशर्ते log viewer उन्हें decode करे, और Unicode decoder उन्हें वापस text में बदल देता है।
POST /api/v1/profile HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"displayName":"علي"}
GET /api/v1/profile HTTP/1.1
Host: api.example.com
HTTP/1.1 200 OK
Content-Type: application/json
{"displayName":"علي"} OK: value bytes D8 B9 D9 84 D9 8A
{"displayName":"علي"} mojibake: read once as Windows-1252, saved again
{"displayName":"???"} lost: converted to an encoding without Arabicआज़माएँ: Unicode डेटासेट जनरेटर आज़माएँ: स्ट्रिंग लेंथ काउंटर आज़माएँ: टेक्स्ट डिफ़ आज़माएँ: Unicode डिकोडर
आम गलतियाँ
- सिर्फ़ English या ASCII data से test करना, जिसे हर encoding सही निकालती है।
- Characters और bytes को एक ही चीज़ मानना, जिससे 30-character requirement, 30-byte column और 30-unit JavaScript check, तीनों को "30" कह दिया जाता है।
- Decoder गलत हो तो data या font को दोष देना:
عليका मतलब है कि bytes तब तक ठीक थे जब तक किसी layer ने उन्हें Windows-1252 मानकर नहीं पढ़ा। - Four-byte characters भूल जाना: जो column Arabic और Hindi store करता है वह फिर भी emoji reject कर सकता है (MySQL का
utf8ज़्यादा से ज़्यादा तीन bytes रखता है;utf8mb4पूरा Unicode)। - तय normalization form के बिना strings compare या search करना, फिर "search टूटा है" report करना, बिना उन code points के जो इसे साबित करें।
- Screen पर भरोसा करना: profile page नाम सही दिखा सकता है जबकि e-mail, PDF और CSV export हर एक उसे अपने-अपने तरीके से बिगाड़ दे।
QA का नज़रिया
Encoding design review में तय होती है, bug के बाद नहीं। जब कोई नया field, import, export या integration आए, तो पूछें कि हर पड़ाव कौन-सी encoding इस्तेमाल करता है, कौन-सी normalization form लागू है और length limit कैसे गिनी जाती है। Developer से column character sets और database connection के बारे में पूछें, और DevOps engineer से servers और containers के locale के बारे में, क्योंकि UTF-8 न होने वाला server default logs और generated files में mojibake का classic source है।
Defect report में exact input code points और bytes के रूप में दें (U+0639 U+0644 U+064A, D8 B9 D9 84 D9 8A), वह layer जहाँ यह टूटा, क्या वापस आया, और आपके अनुमान से bytes किस encoding में पढ़े गए। Dataset को regression suite में रखें: encoding defects तब-तब लौटते हैं जब कोई library, driver या export template बदलता है।
अभ्यास
Dataset नापें और बताएँ कि name field कहाँ टूटेगा
Profile form कहता है: "Display name: 2 से 30 characters।" Team ने नहीं बताया कि गिनती कैसे होती है, और नाम CSV export में भी छपता है। नीचे दिए dataset से हर string के चारों counts लिखें, फिर बताएँ कि bytes गिनने वाली limit, UTF-16 गिनने वाली limit और grapheme गिनने वाली limit, हर एक किन strings को accept करेगी।
आख़िर में लिखें कि CSV export में Arabic नाम का mojibake version कैसा दिखेगा, ताकि आप उसे देखते ही पहचान लें।
1 Ali
2 علي
3 مُحَمَّد (with diacritics)
4 José (é as U+00E9)
5 José (e + U+0301)
6 हिंदी
7 👍🏽 (thumbs up + skin tone)
8 علي x 10 (repeated, no spaces)- हर string के लिए graphemes, code points, UTF-16 units और UTF-8 bytes दर्ज करें (string length counter चारों दिखाता है)।
- 2 से 30 की range के लिए चिह्नित करें कि हर तरह की limit किन strings को accept करेगी।
- बताएँ कि CSV को Windows-1252 मानकर पढ़ने पर user क्या देखेगा।
मॉडल उत्तर दिखाएँ
Counts (graphemes / code points / UTF-16 / bytes): Ali 3/3/3/3; علي 3/3/3/6; مُحَمَّد 4/8/8/16; José NFC 4/4/4/5; José NFD 4/5/5/6; हिंदी 2/5/5/15; 👍🏽 1/2/4/8; علي × 10 30/30/30/60।
30 की byte-counting limit string 8 (60 bytes) को reject करती है हालाँकि वह 30 अक्षर है, और बाक़ी सब accept करती है। UTF-16 limit आठों accept करती है पर emoji को 4 और diacritics वाले नाम को 8 गिनती है, इसलिए user ऐसे ज़्यादा से ज़्यादा 7 emoji ही डाल सकता है। Grapheme-counting limit user की उम्मीद से मेल खाती है और string 7 को छोड़कर सब accept करती है: उसके हिसाब से 👍🏽 एक ही character है और minimum 2 पर fail होता है, जबकि code point या UTF-16 गिनती उसे accept कर लेती है, और हिंदी ठीक minimum पर है। Product owner से पूछें कि कौन-सी गिनती का इरादा था।
Strings 4 और 5 एक जैसी दिखती हैं; जब तक product normalize न करे, search और uniqueness के लिए ये दो अलग नाम हैं, और grapheme count यह फ़र्क़ छिपा देता है।
Windows-1252 मानकर पढ़ा गया CSV علي की जगह علي दिखाएगा: UTF-8 bytes D8 B9 D9 84 D9 8A Western table से एक-एक byte करके दिखाए गए। File के bytes अब भी सही हैं, इसलिए उसे UTF-8 में दोबारा खोलने पर علي फिर दिखेगा; नुकसान तभी फैलता है जब कोई garbled view को UTF-8 में save कर दे।
अक्सर पूछे जाने वाले सवाल
Unicode और UTF-8 में क्या फ़र्क़ है?
Unicode character set है: यह हर character को U+0639 जैसा code point देता है। UTF-8 उस set की एक encoding है: एक नियम जो हर code point को 1 से 4 bytes में लिखता है। UTF-16 और UTF-32 वही code points अलग तरह से encode करती हैं; Windows-1256 एक अलग, बहुत छोटा character set है जिसकी अपनी byte values हैं।
Text screen पर सही पर export या e-mail में गलत क्यों दिखता है?
क्योंकि हर रास्ता अपनी encoding declare करता है। Web page अपने headers में charset=utf-8 ले जाता है; CSV file, PDF generator या e-mail template कोई और encoding मान सकते हैं या वह byte order mark छोड़ सकते हैं जो कुछ spreadsheet programs को चाहिए। हर output channel को उसी dataset से अलग-अलग test करें।
Application असामान्य input को reject करे या normalize?
User names, e-mails और search terms जैसे identifiers को boundary पर एक form (आम तौर पर NFC) में normalize करें, फिर normalized text store और compare करें; message body जैसे free text को कभी चुपचाप न बदलें। Arabic diacritics या emoji को इसलिए reject करना कि column उन्हें store नहीं कर सकता, column का defect है, input का नहीं।
क्या UTF-8 files में byte order mark ज़रूरी है?
नहीं। Unicode Standard UTF-8 में byte order mark की अनुमति देता है पर न इसे अनिवार्य करता है, न इसकी सिफ़ारिश; यह file की शुरुआत में EF BB BF bytes हैं। कुछ spreadsheet programs इससे UTF-8 CSV पहचानते हैं, जबकि कुछ parsers इसे पहले field का हिस्सा मान लेते हैं। Files को दोनों तरह test करें और बताएँ कि consumer इसके साथ क्या करता है।
संदर्भ
- The Unicode Standardसार्वभौमिक कैरेक्टर सेट और उसके एन्कोडिंग रूप (UTF-8, UTF-16, UTF-32), नॉर्मलाइज़ेशन और हर कोड पॉइंट के गुण।
- RFC 3629: UTF-8, a transformation format of ISO 10646UTF-8 में कोड पॉइंट एक से चार बाइट में कैसे एन्कोड होते हैं, और कौन-सी बाइट सीक्वेंस अमान्य हैं।
- UAX #15: Unicode Normalization FormsNFC, NFD, NFKC और NFKD रूप — वजह कि दिखने में एक जैसी दो स्ट्रिंग नॉर्मलाइज़ किए बिना असमान निकल सकती हैं।
- UAX #29: Unicode Text Segmentationटेक्स्ट को ग्रैफ़ीम क्लस्टर, शब्दों और वाक्यों में कैसे बाँटा जाता है — "कैरेक्टर" की गिनती कोड-पॉइंट या बाइट की गिनती से क्यों अलग हो सकती है।
Mutqan tools से अभ्यास करें
स्ट्रिंग की लंबाई कैरेक्टर, कोड पॉइंट, UTF-8 बाइट और UTF-16 यूनिट में मापें।
टेक्स्ट को हेक्साडेसिमल बाइट (UTF-8) में बदलें, मनचाहे सेपरेटर और केसिंग के साथ।
टेक्स्ट को यूनिकोड एस्केप अनुक्रम (\u0041), कोड पॉइंट (U+0041) या HTML न्यूमेरिक एंटिटी में बदलें।
\uXXXX एस्केप, U+ कोड पॉइंट और न्यूमेरिक एंटिटी को पढ़ने योग्य टेक्स्ट में डिकोड करें।
कई लिपियों (अरबी, देवनागरी, CJK, सिरिलिक), emoji, संयोजक चिह्न और normalisation रूपों में Unicode टेस्ट स्ट्रिंग जनरेट करें।
दो टेक्स्ट की लाइन-दर-लाइन या शब्द-दर-शब्द तुलना करें और अंतर हाइलाइट करें।
और गहराई से
इस topic में आपकी प्रगति
प्रगति ट्रैक करने के लिए साइन इन करें
Topics पूर्ण चिह्नित करें, जहाँ छोड़ा था वहीं से जारी रखें और specialisation परीक्षाएँ खोलें।