जावास्क्रिप्ट में बाइट्स में स्ट्रिंग की लंबाई


104

मेरे जावास्क्रिप्ट कोड में मुझे इस प्रारूप में सर्वर पर एक संदेश लिखने की आवश्यकता है:

<size in bytes>CRLF
<data>CRLF

उदाहरण:

3
foo

डेटा में यूनिकोड वर्ण हो सकते हैं। मुझे उन्हें UTF-8 के रूप में भेजने की आवश्यकता है।

मैं जावास्क्रिप्ट में बाइट्स में स्ट्रिंग की लंबाई की गणना करने के लिए सबसे क्रॉस-ब्राउज़र तरीके की तलाश कर रहा हूं।

मैंने अपना पेलोड बनाने के लिए यह कोशिश की है:

return unescape(encodeURIComponent(str)).length + "\n" + str + "\n"

लेकिन यह मुझे पुराने ब्राउज़रों के लिए सटीक परिणाम नहीं देता है (या, शायद यूटीएफ -16 में उन ब्राउज़रों में तार।)।

कोई सुराग?

अपडेट करें:

उदाहरण: ЭЭХ! Naïve?UTF-8 में स्ट्रिंग के बाइट्स में लंबाई 15 बाइट्स है, लेकिन कुछ ब्राउज़र 23 बाइट्स के बजाय रिपोर्ट करते हैं।


1
संभव डुप्लिकेट? stackoverflow.com/questions/2219526/…
एली

@ एलि: मेरे द्वारा काम करने के लिए आपके द्वारा लिंक किए गए प्रश्न के उत्तर में से कोई भी नहीं।
अलेक्जेंडर ग्लैडीश

जब आप "ЭЭХ! Naïve" के बारे में बात करते हैं? क्या आपने इसे एक विशेष सामान्य रूप में रखा है? unicode.org/reports/tr15
माइक सैमुअल

@ माइक: मैंने इसे रैंडम टेक्स्ट एडिटर (UTF-8 मोड में) टाइप किया और इसे सेव किया। जैसा कि मेरे पुस्तकालय का कोई भी उपयोगकर्ता करेगा। हालांकि, ऐसा लगता है कि मुझे लगा कि क्या गलत है - मेरा जवाब देखें।
अलेक्जेंडर ग्लैडीश

जवाबों:


89

इसे मूल रूप से जावास्क्रिप्ट में करने का कोई तरीका नहीं है। ( आधुनिक दृष्टिकोण के लिए रिकार्डो गली का जवाब देखें ।)


ऐतिहासिक संदर्भ के लिए या जहां TextEncoder API अभी भी अनुपलब्ध हैं

यदि आप वर्ण एन्कोडिंग जानते हैं, तो आप इसे स्वयं गणना कर सकते हैं।

encodeURIComponent UTF-8 को चरित्र एन्कोडिंग के रूप में मानता है, इसलिए यदि आपको उस एन्कोडिंग की आवश्यकता है, तो आप कर सकते हैं,

function lengthInUtf8Bytes(str) {
  // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
  var m = encodeURIComponent(str).match(/%[89ABab]/g);
  return str.length + (m ? m.length : 0);
}

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

विकिपीडिया की तालिका इसे स्पष्ट करती है

Bits        Last code point Byte 1          Byte 2          Byte 3
  7         U+007F          0xxxxxxx
 11         U+07FF          110xxxxx        10xxxxxx
 16         U+FFFF          1110xxxx        10xxxxxx        10xxxxxx
...

यदि इसके बजाय आपको पृष्ठ एन्कोडिंग को समझने की आवश्यकता है, तो आप इस ट्रिक का उपयोग कर सकते हैं:

function lengthInPageEncoding(s) {
  var a = document.createElement('A');
  a.href = '#' + s;
  var sEncoded = a.href;
  sEncoded = sEncoded.substring(sEncoded.indexOf('#') + 1);
  var m = sEncoded.match(/%[0-9a-f]{2}/g);
  return sEncoded.length - (m ? m.length * 2 : 0);
}

खैर, मुझे डेटा के चरित्र एन्कोडिंग का पता कैसे चलेगा? मुझे अपने जेएस लाइब्रेरी को जो भी स्ट्रिंग उपयोगकर्ता (प्रोग्रामर) प्रदान किया गया है, उसे एनकोड करना होगा।
अलेक्जेंडर ग्लैडीश

@Alexander, जब आप सर्वर को संदेश भेज रहे हैं, तो क्या आप एक HTTP हेडर के माध्यम से संदेश शरीर की सामग्री-एन्कोडिंग निर्दिष्ट कर रहे हैं?
माइक सैमुअल

1
@Alexander, शांत। यदि आप एक प्रोटोकॉल स्थापित कर रहे हैं, तो UTF-8 को अनिवार्य करना टेक्स्ट-इंटरचेंज के लिए एक बढ़िया विचार है। एक कम चर जो एक बेमेल में परिणाम कर सकता है। UTF-8 को चरित्र एनकोडिंग का नेटवर्क-बाइट-क्रम होना चाहिए।
माइक सैमुअल

4
@ मायकेसमुएल: lengthInUtf8Bytesफ़ंक्शन str.lengthइन रिटर्न के लिए गैर-बीएमपी वर्णों के लिए 5 रिटर्न देता है। मैं खंड का जवाब देने के लिए इस फ़ंक्शन का एक संशोधित संस्करण लिखूंगा।
लॉरी ऑहरड

1
यह समाधान ठंडा है लेकिन utf8mb4 नहीं माना जाता है। उदाहरण के लिए, encodeURIComponent('🍀')है '%F0%9F%8D%80'
अल्बर्ट

117

वर्षों बीत गए और आजकल आप इसे मूल रूप से कर सकते हैं

(new TextEncoder().encode('foo')).length

ध्यान दें कि यह अभी तक IE (या एज) द्वारा समर्थित नहीं है (आप इसके लिए एक पॉलीफ़िल का उपयोग कर सकते हैं )।

MDN प्रलेखन

मानक विनिर्देशों


4
क्या शानदार, आधुनिक तरीका है। धन्यवाद!
कोन एंटोनकोस

ध्यान दें कि MDN प्रलेखन के अनुसार , TextEncoder अभी तक Safari (WebKit) द्वारा समर्थित नहीं है।
मूर

TextEncodeChrome 53 के बाद से केवल utf-8 का समर्थन करता है ।
Jehong Ahn

1
यदि आपको केवल लंबाई की आवश्यकता है, तो एक नया स्ट्रिंग आवंटित करने के लिए ओवरकिल हो सकता है, वास्तविक रूपांतरण कर सकता है, लंबाई ले सकता है, और फिर स्ट्रिंग को त्याग सकता है। एक फ़ंक्शन के लिए मेरा जवाब ऊपर देखें जो एक कुशल तरीके से लंबाई की गणना करता है।
lovasoa

66

यहाँ एक बहुत तेज़ संस्करण है, जो नियमित रूप से अभिव्यक्ति का उपयोग नहीं करता है, और न ही एन्कोडर्कोम्पोनेंट () :

function byteLength(str) {
  // returns the byte length of an utf8 string
  var s = str.length;
  for (var i=str.length-1; i>=0; i--) {
    var code = str.charCodeAt(i);
    if (code > 0x7f && code <= 0x7ff) s++;
    else if (code > 0x7ff && code <= 0xffff) s+=2;
    if (code >= 0xDC00 && code <= 0xDFFF) i--; //trail surrogate
  }
  return s;
}

यहाँ एक प्रदर्शन तुलना है

यह सिर्फ यूनिकोड कोड की यूटीएफ 8 में लंबाई की गणना चारकोड () ( यूटीएफ 8 के विकिपीडिया के विवरण के आधार पर ) द्वारा लौटाता है , और सरोगेट वर्ण के ।

यह RFC3629 (जहां UTF-8 वर्ण सबसे अधिक 4-बाइट्स लंबे हैं) का अनुसरण करता है ।


46

साधारण UTF-8 एन्कोडिंग के लिए TextEncoder, बूँद की तुलना में थोड़ी बेहतर संगतता के साथ , बूँद चाल करता है। हालांकि बहुत पुराने ब्राउज़रों में काम नहीं करेगा।

new Blob(["😀"]).size; // -> 4  

29

यह फ़ंक्शन आपके द्वारा पास किए गए किसी भी UTF-8 स्ट्रिंग के बाइट का आकार लौटाएगा।

function byteCount(s) {
    return encodeURI(s).split(/%..|./).length - 1;
}

स्रोत


यह स्ट्रिंग ユ ユ ー work コ ー ド, expected के साथ काम नहीं करता है, 14 लंबाई की उम्मीद है लेकिन 21
मई मौसम VN

1
@MayWeatherVN आपको ユーザーコードबाइट्स में गलत लंबाई हमेशा 21 होती है, मैंने इसे विभिन्न उपकरणों पर परीक्षण किया; अपनी टिप्पणियों के साथ अधिक विनम्र हो;)
कैपिटेक्स

यह स्ट्रिंग मुझे याद है कि php पर परीक्षण 14
मई का मौसम VN

24

Buffer(केवल NodeJS के लिए) का उपयोग कर एक और बहुत ही सरल तरीका :

Buffer.byteLength(string, 'utf8')

Buffer.from(string).length

1
आप के साथ एक बफर बनाना छोड़ सकते हैं Buffer.byteLength(string, 'utf8')
जो

1
@ जो सुझाव के लिए धन्यवाद, मैंने इसे शामिल करने के लिए सिर्फ एक संपादन किया है।
इवान पेरेज़

5

मुझे प्रतिक्रियाशील मूल के लिए एक समाधान खोजने में थोड़ा समय लगा, इसलिए मैं इसे यहां डालूंगा:

पहले bufferपैकेज स्थापित करें :

npm install --save buffer

फिर उपयोगकर्ता नोड विधि:

const { Buffer } = require('buffer');
const length = Buffer.byteLength(string, 'utf-8');

4

दरअसल, मुझे लगा कि क्या गलत है। कोड को काम करने के लिए पृष्ठ <head>में यह टैग होना चाहिए:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

या, जैसा कि टिप्पणियों में सुझाया गया है, अगर सर्वर HTTP भेजता है Content-Encoding हेडर इसे भी काम करना चाहिए।

फिर विभिन्न ब्राउज़रों के परिणाम सुसंगत हैं।

यहाँ एक उदाहरण है:

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  <title>mini string length test</title>
</head>
<body>

<script type="text/javascript">
document.write('<div style="font-size:100px">' 
    + (unescape(encodeURIComponent("ЭЭХ! Naïve?")).length) + '</div>'
  );
</script>
</body>
</html>

नोट: मुझे संदेह है कि किसी भी (सटीक) एन्कोडिंग को निर्दिष्ट करना एन्कोडिंग समस्या को ठीक करेगा। यह सिर्फ एक संयोग है कि मुझे UTF-8 की आवश्यकता है।


2
unescapeJavaScript फ़ंक्शन नहीं करना चाहिए यूनिफ़ॉर्म रिसोर्स पहचानकर्ता (URI) को डिकोड करने के लिए इस्तेमाल किया जा।
लॉरी ओहरड

1
@ LauriOherd unescapeको वास्तव में URI को डिकोड करने के लिए उपयोग नहीं किया जाना चाहिए। हालाँकि, पाठ को UTF-8 में बदलने के लिए यह ठीक
TS

unescape(encodeURIComponent(...)).lengthहमेशा सही लंबाई के साथ या उसके बिना गणना करता है meta http-equiv ... utf8। एन्कोडिंग विनिर्देश के बिना कुछ ब्राउज़रों में बस एक अलग पाठ (वास्तविक HTML पाठ में दस्तावेज़ के बाइट्स को एन्कोडिंग के बाद) हो सकता है, जिनकी लंबाई की उन्होंने गणना की। न केवल लंबाई, बल्कि पाठ को भी मुद्रित करके यह आसानी से परीक्षण कर सकता है।
TS

3

यहां एक स्ट्रिंग के UTF-8 बाइट्स की गणना करने के लिए एक स्वतंत्र और कुशल तरीका है।

//count UTF-8 bytes of a string
function byteLengthOf(s){
	//assuming the String is UCS-2(aka UTF-16) encoded
	var n=0;
	for(var i=0,l=s.length; i<l; i++){
		var hi=s.charCodeAt(i);
		if(hi<0x0080){ //[0x0000, 0x007F]
			n+=1;
		}else if(hi<0x0800){ //[0x0080, 0x07FF]
			n+=2;
		}else if(hi<0xD800){ //[0x0800, 0xD7FF]
			n+=3;
		}else if(hi<0xDC00){ //[0xD800, 0xDBFF]
			var lo=s.charCodeAt(++i);
			if(i<l&&lo>=0xDC00&&lo<=0xDFFF){ //followed by [0xDC00, 0xDFFF]
				n+=4;
			}else{
				throw new Error("UCS-2 String malformed");
			}
		}else if(hi<0xE000){ //[0xDC00, 0xDFFF]
			throw new Error("UCS-2 String malformed");
		}else{ //[0xE000, 0xFFFF]
			n+=3;
		}
	}
	return n;
}

var s="\u0000\u007F\u07FF\uD7FF\uDBFF\uDFFF\uFFFF";
console.log("expect byteLengthOf(s) to be 14, actually it is %s.",byteLengthOf(s));

ध्यान दें कि यदि इनपुट स्ट्रिंग UCS-2 विकृत है तो विधि त्रुटि फेंक सकती है


3

NodeJS में, Buffer.byteLengthइस उद्देश्य के लिए विशेष रूप से एक विधि है:

let strLengthInBytes = Buffer.byteLength(str); // str is UTF-8

ध्यान दें कि डिफ़ॉल्ट रूप से विधि मानती है कि स्ट्रिंग UTF-8 एन्कोडिंग में है। यदि एक अलग एन्कोडिंग की आवश्यकता है, तो इसे दूसरे तर्क के रूप में पास करें।


क्या strLengthInBytesस्ट्रिंग के भीतर वर्णों की 'गिनती' को जानकर ही गणना संभव है ? यानी var text = "Hello World!; var text_length = text.length; // pass text_length as argument to some method?। और, सिर्फ संदर्भ के लिए, Bufferमैं फिर से इस जवाब पर आया हूं जो चर्चा करता है new Blob(['test string']).sizeऔर, नोड में Buffer.from('test string').length। शायद ये कुछ लोगों की भी मदद करेंगे?
user1063287

1
@ user1063287 समस्या यह है कि वर्णों की संख्या हमेशा बाइट्स की संख्या के बराबर नहीं होती है। उदाहरण के लिए, सामान्य UTF-8 एन्कोडिंग एक चर चौड़ाई एन्कोडिंग है, जिसमें एक एकल वर्ण आकार में 1 बाइट से 4 बाइट हो सकता है। इसलिए एक विशेष विधि की आवश्यकता होती है और साथ ही साथ एन्कोडिंग का उपयोग किया जाता है।
बोअज

उदाहरण के लिए, 4 वर्णों वाला एक UTF-8 स्ट्रिंग, कम से कम 4 बाइट्स "लंबा" हो सकता है, यदि प्रत्येक वर्ण सिर्फ 1 बाइट है; और यदि प्रत्येक वर्ण 4 बाइट्स हो तो अधिकतम 16 बाइट्स "लंबी" होती हैं। नोट या तो मामले में वर्ण अभी भी 4 है और इसलिए बाइट्स की लंबाई के लिए एक अविश्वसनीय उपाय है ।
बोआज

1

यह BMP और SIP / SMP वर्णों के लिए काम करेगा।

    String.prototype.lengthInUtf8 = function() {
        var asciiLength = this.match(/[\u0000-\u007f]/g) ? this.match(/[\u0000-\u007f]/g).length : 0;
        var multiByteLength = encodeURI(this.replace(/[\u0000-\u007f]/g)).match(/%/g) ? encodeURI(this.replace(/[\u0000-\u007f]/g, '')).match(/%/g).length : 0;
        return asciiLength + multiByteLength;
    }

    'test'.lengthInUtf8();
    // returns 4
    '\u{2f894}'.lengthInUtf8();
    // returns 4
    'سلام علیکم'.lengthInUtf8();
    // returns 19, each Arabic/Persian alphabet character takes 2 bytes. 
    '你好,JavaScript 世界'.lengthInUtf8();
    // returns 26, each Chinese character/punctuation takes 3 bytes. 

0

आप यह कोशिश कर सकते हैं:

function getLengthInBytes(str) {
  var b = str.match(/[^\x00-\xff]/g);
  return (str.length + (!b ? 0: b.length)); 
}

इससे मेरा काम बनता है।


क्रोम में "एक" के लिए 1 रिटर्न
रिक

पहले मुद्दे को \ xff से बदलकर x7f किया जा सकता है, लेकिन यह इस तथ्य को ठीक नहीं करता है कि 0x800-0xFFFF के बीच कोडपॉइंट्स 2 बाइट्स लेते समय रिपोर्ट किए जाएंगे, जब वे 3 लेते हैं
रिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.