JavaScript의 문자열 길이 (바이트)


104

내 JavaScript 코드에서 다음 형식으로 서버에 메시지를 작성해야합니다.

<size in bytes>CRLF
<data>CRLF

예:

3
foo

데이터에는 유니 코드 문자가 포함될 수 있습니다. UTF-8로 보내야합니다.

JavaScript에서 문자열 길이를 바이트 단위로 계산하는 가장 크로스 브라우저 방법을 찾고 있습니다.

내 페이로드를 구성하기 위해 이것을 시도했습니다.

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

그러나 이전 브라우저 (또는 UTF-16 브라우저의 문자열)에 대한 정확한 결과를 제공하지 않습니다.

단서가 있습니까?

최신 정보:

예 : ЭЭХ! Naïve?UTF-8 의 문자열 길이 (바이트) 는 15 바이트이지만 일부 브라우저에서는 대신 23 바이트를보고합니다.


1
중복 가능성이 있습니까? stackoverflow.com/questions/2219526/…
Eli

@Eli : 저를 위해 일하기 위해 연결 한 질문에 대한 답변이 없습니다.
Alexander Gladysh 2011

"ЭЭХ! Naïve?"에 대해 말할 때 당신은 그것을 특정한 정상 형태로 넣었습니까? unicode.org/reports/tr15
Mike Samuel

@Mike : 임의의 텍스트 편집기 (UTF-8 모드)에 입력하고 저장했습니다. 내 라이브러리의 모든 사용자가하는 것처럼. 그러나 무엇이 잘못되었는지 알아 낸 것 같습니다. 내 대답을 참조하십시오.
Alexander Gladysh 2011

답변:


89

기본적으로 JavaScript에서 수행 할 수있는 방법은 없습니다. ( 현대적인 접근 방식에 대해서는 Riccardo Galli의 답변 을 참조하십시오 .)


기록 참조 또는 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);
}

이는 UTF-8이 멀티 바이트 시퀀스를 인코딩하는 방식 때문에 작동합니다. 첫 번째 인코딩 된 바이트는 항상 단일 바이트 시퀀스의 경우 0의 상위 비트 또는 첫 번째 16 진수 숫자가 C, D, E 또는 F 인 바이트로 시작합니다. 두 번째 및 후속 바이트는 처음 두 비트가 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);
}

글쎄, 데이터의 문자 인코딩을 어떻게 알 수 있습니까? 내 JS 라이브러리에 제공된 문자열 사용자 (프로그래머)를 인코딩해야합니다.
Alexander Gladysh 2011

@Alexander, 서버에 메시지를 보낼 때 HTTP 헤더를 통해 메시지 본문의 콘텐츠 인코딩을 지정하고 있습니까?
Mike Samuel

1
@Alexander, 멋지다. 프로토콜을 설정하는 경우 UTF-8을 의무화하는 것은 텍스트 교환을위한 좋은 아이디어입니다. 불일치를 초래할 수있는 변수가 하나 적습니다. UTF-8은 문자 인코딩의 네트워크 바이트 순서 여야합니다.
Mike Samuel

4
@MikeSamuel :이 lengthInUtf8Bytes함수는 BMP가 아닌 문자에 str.length대해 5를 반환합니다 .이 함수는 2를 반환합니다.이 함수의 수정 된 버전을 작성하여 섹션에 답할 것입니다.
Lauri Oherd

1
이 솔루션은 멋지지만 utf8mb4는 고려되지 않습니다. 예를 들어, encodeURIComponent('🍀')입니다 '%F0%9F%8D%80'.
albert

117

몇 년이 지났고 요즘에는 기본적으로 할 수 있습니다.

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

IE (또는 Edge)에서는 아직 지원되지 않습니다 ( 폴리 필사용할있습니다 ).

MDN 문서

표준 사양


4
환상적이고 현대적인 접근 방식입니다. 감사!
Con Antonakos 2016 년

에 따른주의 사항 MDN 문서 의 TextEncoder 사파리에서 아직 지원되지 않습니다 (웹킷).
Maor

TextEncode지원에만 UTF-8 크롬 53부터
Jehong 안

1
길이 만 필요한 경우 새 문자열을 할당하고 실제 변환을 수행하고 길이를 취한 다음 문자열을 버리는 것이 과도 할 수 있습니다. 효율적인 방식으로 길이를 계산하는 함수에 대해서는 위의 대답을 참조하십시오.
lovasoa

66

다음은 정규 표현식을 사용하지 않고 encodeURIComponent ()를 사용하지 않는 훨씬 빠른 버전입니다 .

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;
}

다음은 성능 비교 입니다.

charCodeAt ()에 의해 반환 된 각 유니 코드 코드 포인트의 길이를 UTF8로 계산합니다 (위키 백과의 UTF8 설명 및 UTF16 대리 문자에 따라).

RFC3629 (UTF-8 문자의 길이는 최대 4 바이트)를 따릅니다 .


46

간단한 UTF-8 인코딩의 경우.보다 약간 더 호환성이 좋은 TextEncoderBlob이 트릭을 수행합니다. 하지만 아주 오래된 브라우저에서는 작동하지 않습니다.

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

29

이 함수는 전달한 UTF-8 문자열의 바이트 크기를 반환합니다.

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

출처


그것은 문자열로 작동하지 않습니다 'ユーザーコード', 기대 (14)의 길이 만 21
월 날씨 VN

1
@MayWeatherVN 잘못된 ユーザーコード바이트 길이는 항상 21입니다. 다른 도구에서 테스트했습니다. 귀하의 의견에 더 친절하게;)
Capitex

PHP에서 테스트 한 것을 기억하는이 문자열은 14
May Weather VN

23

Buffer(NodeJS 전용)을 사용하는 또 다른 매우 간단한 접근 방식 :

Buffer.byteLength(string, 'utf8')

Buffer.from(string).length

1
를 사용하여 버퍼 생성을 건너 뛸 수 있습니다 Buffer.byteLength(string, 'utf8').
Joe

1
@Joe 제안 해 주셔서 감사합니다. 방금 수정했습니다.
Iván Pérez

5

React Native에 대한 솔루션을 찾는 데 시간이 걸리므로 여기에 넣겠습니다.

먼저 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
unescape자바 스크립트 함수는 안된다 유니폼 리소스 식별자 (URI)를 디코딩하는데 이용 될 수있다.
Lauri Oherd

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 바이트 일 수있는 가변 너비 인코딩입니다. 그렇기 때문에 사용되는 인코딩과 함께 특별한 방법이 필요합니다.
Boaz

예를 들어, 4 문자의 UTF-8 문자열은 각 문자가 1 바이트 인 경우 "긴"4 바이트 이상일 수 있습니다. 각 문자가 4 바이트 인 경우 최대 16 바이트 "long"입니다. 두 경우 모두 문자 수 는 여전히 4 개이므로 바이트 길이에 대한 신뢰할 수없는 측정입니다 .
Boaz

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)); 
}

그것은 나를 위해 작동합니다.


크롬에서 "A"에 대한 반환 1

첫 번째 문제는 \ xff를 \ x7f로 변경하여 수정할 수 있지만 0x800-0xFFFF 사이의 코드 포인트가 3을 취할 때 2 바이트로보고된다는 사실은 수정되지 않습니다.
Rick
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.