सीधे कंटेंट पर जाएं

एक कैरेक्टर किसे गिना जाता है?

चार अलग सिस्टम एक ही टेक्स्ट को चार अलग तरीक़ों से गिनते हैं। अंग्रेज़ी टेक्स्ट पर वे सहमत रहते हैं, इसीलिए समस्या तब तक दिखती नहीं जब तक एक दिन सहमति टूट न जाए।

आख़िरी समीक्षा

एक इमोजी, चार जवाब

स्किन टोन वाला थम्स-अप लें, या परिवार वाला इमोजी। इसे चार तरीक़ों से गिनें और चार अलग नंबर मिलेंगे, और उनमें से कोई ग़लत नहीं है।

पढ़ने वाले को एक कैरेक्टर दिखता है। एक चीज़, जिसे मिटाने के लिए बैकस्पेस एक बार दबाना पड़ता है।

रेगुलर एक्सप्रेशन को सात दिख सकते हैं। यह कई कोड पॉइंट से बना है जिन्हें अदृश्य जोड़ने वाले कैरेक्टर जोड़ते हैं — एक बेस इमोजी, एक मॉडिफ़ायर, एक जॉइनर, एक और इमोजी, वग़ैरह।

JavaScript ग्यारह बताता है। वह टेक्स्ट को 16-बिट यूनिट में रखता है, और बेसिक रेंज के बाहर की हर चीज़ दो यूनिट लेती है, इसलिए उनमें से हर कोड पॉइंट दोगुना गिना जा सकता है।

डेटाबेस कॉलम को पच्चीस बाइट दिखते हैं। स्टोरेज के लिए UTF-8 में एन्कोड होने पर हर कोड पॉइंट चार बाइट तक लेता है।

साधारण अंग्रेज़ी टेक्स्ट के लिए चारों नंबर एक जैसे होते हैं। ठीक इसीलिए समस्या तब तक नहीं दिखती जब तक कोई ऐक्सेंट वाला नाम, जापानी का कोई वाक्य या एक अकेला इमोजी न डाले — और तीन साल से ठीक चल रहा फ़ील्ड डेटा काटने लगे।

आपकी लिमिट किस नंबर की बात करती है

असली सवाल कभी “यह टेक्स्ट कितना लंबा है” नहीं होता। सवाल होता है “लिमिट कौन लगा रहा है”, और इसके चार आम जवाब हैं।

डेटाबेस कॉलम जिसे VARCHAR घोषित किया गया हो, ज़्यादातर इंजन में बाइट गिनता है। इसीलिए जो फ़ील्ड 255 अंग्रेज़ी कैरेक्टर आराम से ले लेता है, वह कहीं कम जापानी अक्षर ठुकरा देता है — हर एक तीन बाइट का — और इसीलिए ऐक्सेंट वाला नाम कभी-कभी नहीं समाता, जबकि उसका सादा रूप समा जाता था।

SMS अपने सात-बिट अल्फ़ाबेट में 160 कैरेक्टर का होता है, और जैसे ही एक भी कैरेक्टर उस अल्फ़ाबेट से बाहर का हो, प्रति मैसेज 70 पर आ जाता है। वर्ड प्रोसेसर से पेस्ट किया एक घुमावदार कोट, या एक इमोजी, एक हिस्से के मैसेज को तीन हिस्सों का बना सकता है और आपका ख़र्च तिगुना कर सकता है।

JavaScript में फ़ॉर्म वैलिडेटर लगभग हमेशा UTF-16 यूनिट गिनता है, इसीलिए जो फ़ील्ड 280 कैरेक्टर की बात कहता है, वह इमोजी से भरी ऐसी स्ट्रिंग ठुकरा सकता है जो देखने में कहीं छोटी है।

इंसान वही गिनता है जो उसे दिखता है, और हेडलाइन, टाइटल टैग या किसी भी ऐसी चीज़ के लिए जिसकी जगह दिखने से तय होती है, यही इकलौती परिभाषा मायने रखती है।

गिनतियां कहां अलग हो जाती हैं

सिद्धांत जानने से ज़्यादा काम का यह जानना है कि कौन-से इनपुट दिक़्क़त देते हैं, क्योंकि उनका अंदाज़ा पहले से लगाया जा सकता है।

इमोजी, ख़ास तौर पर जुड़कर बने हुए — स्किन टोन, परिवार, झंडे, पेशे। झंडा दो रीजनल-इंडिकेटर अक्षरों से बनता है; पेशे वाला इमोजी आम तौर पर एक व्यक्ति, एक जॉइनर और एक चीज़ से।

ऐक्सेंट वाले और जुड़ने वाले (कंबाइनिंग) कैरेक्टर। é को एक कोड पॉइंट के रूप में भी लिखा जा सकता है, और e के बाद एक कंबाइनिंग ऐक्यूट ऐक्सेंट के रूप में भी। दोनों एक जैसे दिखते हैं, पर अलग तरह से सॉर्ट होते हैं, तुलना में बराबर नहीं निकलते, और अलग गिने जाते हैं — और असली डेटा में दोनों रूप मिलते हैं, अक्सर एक ही कॉलम में।

ग़ैर-लैटिन लिपियां। चीनी, जापानी और कोरियाई अक्षर UTF-8 में तीन-तीन बाइट के होते हैं। हिंदी, थाई, तमिल और अरबी ऐसे समूह बनाती हैं जिन्हें पढ़ने वाला एक इकाई की तरह देखता है, पर जो कई कोड पॉइंट लंबे होते हैं।

गणित और संगीत के चिह्न, जो बेसिक रेंज के बाहर हैं और इसलिए UTF-16 में दोगुने गिने जाते हैं।

इसके बारे में क्या करें

वही गिनें जो आपकी लिमिट गिनती है। चारों नंबर दिखाने वाला काउंटर सवाल का सीधा जवाब देता है, जो इस पर माथापच्ची करने से तेज़ है। कोड में मेल खाने वाला फ़ंक्शन इस्तेमाल करें: JavaScript में Intl.Segmenter वह गिनता है जो पढ़ने वाले को दिखता है, Python में len(s.encode("utf-8")) बाइट गिनता है, PHP में strlen की जगह mb_strlen।

फ़ॉर्म नहीं, कॉलम ठीक करें। अगर डेटाबेस फ़ील्ड ही रुकावट है, तो टिकाऊ हल उसी स्तर पर है: MySQL में उसे utf8mb4 घोषित करें या PostgreSQL में text। MySQL का पुराना “utf8” मशहूर तौर पर प्रति कैरेक्टर सिर्फ़ तीन बाइट का है और इमोजी स्टोर ही नहीं कर सकता — यह पुराना जाल है जिसने चुपचाप ढेर सारा असली डेटा काट दिया है। फ़ॉर्म पर सावधानी से गिनना उस कॉलम का जुगाड़ है जिसे अलग तरह से घोषित किया जाना चाहिए था।

आते ही नॉर्मलाइज़ करें। एंट्री के समय ही टेक्स्ट को एक नॉर्मल फ़ॉर्म में बदलने से é की दोनों वर्तनियां एक हो जाती हैं, और तुलना, सॉर्टिंग और गिनती सब सहमत होने लगते हैं। हर भाषा में इसके लिए फ़ंक्शन है, और सीमा पर एक बार यह कर लेना हर जगह दोनों रूप संभालने से कहीं आसान है।

मिलता-जुलता मामला: base64

इसका ज़िक्र इसलिए ज़रूरी है क्योंकि यह भी उसी तरह चौंकाता है। डेटा को base64 में एन्कोड करना एक बार में तीन बाइट लेकर उन्हें चार कैरेक्टर के रूप में लिखता है, इसलिए नतीजा लगभग एक-तिहाई बड़ा होता है — यह गणित है, कमी नहीं। 6 MB का अटैचमेंट लगभग 8 MB टेक्स्ट बन जाता है, इसीलिए जो फ़ाइल साइज़ लिमिट से आराम से नीचे है, वह भेजने के लिए एन्कोड होने के बाद ठुकराई जा सकती है।

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

अक्सर पूछे जाने वाले सवाल

मेरा 255 कैरेक्टर वाला फ़ील्ड छोटा टेक्स्ट भी क्यों ठुकरा देता है?

लगभग तय है कि वह कैरेक्टर की बजाय बाइट गिनता है। UTF-8 में ऐक्सेंट वाले अक्षर दो बाइट लेते हैं और CJK अक्षर तीन, इसलिए 255 बाइट में शायद 85 जापानी अक्षर ही आएं। कॉलम को utf8mb4 या text घोषित करने से यह ठीक से हल हो जाता है।

क्या सच में एक इमोजी से मुझे तीन SMS का ख़र्च पड़ता है?

पड़ सकता है। जिस मैसेज में सिर्फ़ GSM अल्फ़ाबेट के कैरेक्टर हों, उसे हर हिस्से में 160 मिलते हैं; उसके बाहर का एक भी कैरेक्टर पूरे मैसेज को 70 कैरेक्टर वाली एन्कोडिंग पर ले जाता है। इसलिए एक इमोजी वाला 150 कैरेक्टर का मैसेज एक की जगह तीन हिस्सों का हो जाता है।

टाइटल टैग के लिए कौन-सी गिनती इस्तेमाल करूं?

इनमें से कोई भी पूरी तरह सटीक नहीं — सर्च इंजन कैरेक्टर गिनती नहीं, पिक्सेल चौड़ाई के हिसाब से काटते हैं, इसलिए चौड़े कैपिटल अक्षरों वाला टाइटल पतले छोटे अक्षरों वाले से पहले कट जाता है। लगभग 60 कैरेक्टर आम मोटा नियम है, और यह एक दिशा-निर्देश है, लिमिट नहीं।

एक जैसी दिखने वाली दो स्ट्रिंग मेल क्यों नहीं खातीं?

आम तौर पर इसलिए कि कोई ऐक्सेंट वाला कैरेक्टर दो अलग तरीक़ों से लिखा गया है — एक में अकेले कोड पॉइंट के रूप में, दूसरे में बेस अक्षर और एक कंबाइनिंग मार्क के रूप में। तुलना से पहले दोनों को एक ही फ़ॉर्म में नॉर्मलाइज़ करने से वे मेल खाने लगती हैं, और यह डेटा के आपके सिस्टम में आते ही करना फ़ायदेमंद है।

क्या शब्दों की गिनती कैरेक्टर गिनती से ज़्यादा भरोसेमंद है?

अंग्रेज़ी के लिए, ज़्यादातर हां। हर जगह नहीं: चीनी और जापानी शब्दों के बीच स्पेस नहीं रखतीं, थाई भी नहीं, इसलिए उन लिपियों में शब्द गिनने के लिए सेगमेंटेशन चाहिए, और अलग-अलग टूल अलग जवाब देते हैं।