web-dev-qa-db-ja.com

JavaScriptの文字列の長さ(バイト)

JavaScriptコードでは、次の形式でサーバーへのメッセージを作成する必要があります。

<size in bytes>CRLF
<data>CRLF

例:

3
foo

データにはUnicode文字が含まれる場合があります。 UTF-8として送信する必要があります。

JavaScriptで文字列の長さをバイト単位で計算する最もクロスブラウザーな方法を探しています。

私はこれを試してペイロードを作成しました:

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

しかし、古いブラウザー(または、おそらくこれらのブラウザーの文字列がUTF-16である場合)の正確な結果は得られません。

手がかりはありますか?

更新:

例:UTF-8の文字列ЭЭХ! Naïve?のバイト単位の長さは15バイトですが、一部のブラウザは23バイトを代わりに報告します。

79

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がマルチバイトシーケンスをエンコードする方法のために機能するはずです。最初のエンコードされたバイトは、常に単一バイトシーケンスのゼロの上位ビット、または最初の16進数字がC、D、E、またはFであるバイトで始まります。2番目以降のバイトは、最初の2ビットが10であるバイトです。 。これらは、UTF-8でカウントする余分なバイトです。

wikipedia の表はそれを明確にします

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);
}
79
Mike Samuel

数年が経過し、今日ではネイティブに実行できます

(new TextEncoder('utf-8').encode('foo')).length

IE(またはEdge)ではまだサポートされていないことに注意してください(そのために ポリフィルを使用 することができます)。

MDNドキュメント

標準仕様

78
Riccardo Galli

これははるかに高速なバージョンで、正規表現も 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;
}

performance比較 です。

charCodeAt() (ウィキペディアの TF8 およびUTF16サロゲート文字の説明に基づく)によって返される各UnicodeコードポイントのUTF8での長さを計算するだけです。

RFC3629(UTF-8文字は最大4バイト長)に続きます。

56
lovasoa

TextEncoderよりわずかに優れた互換性を持つ単純なUTF-8エンコードの場合、Blobがそのトリックを行います。ただし、非常に古いブラウザーでは機能しません。

new Blob(["????"]).size; // -> 4  
38
simap

この関数は、渡されたUTF-8文字列のバイトサイズを返します。

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

ソース

29
Lauri Oherd

Bufferを使用した別の非常に単純なアプローチ(NodeJSのみ):

Buffer.from(string).length
13
Iván Pérez

実際、何が間違っているのかがわかりました。ページを機能させるコードの場合<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>

注:any(正確な)エンコードを指定すると、エンコードの問題が修正されると思われます。 UTF-8が必要なのは偶然です。

4

React Nativeの解決策を見つけるのに少し時間がかかったので、ここに置きます:

最初にbufferパッケージをインストールします。

npm install --save buffer

次に、nodeメソッドを使用します。

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

これは、文字列の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
fuweichin

NodeJSでは、 Buffer.byteLength は、この目的専用のメソッドです。

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

デフォルトでは、メソッドは文字列がUTF-8エンコーディングであると想定していることに注意してください。別のエンコードが必要な場合は、2番目の引数として渡します。

1
Boaz

これは、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. 
1
chrislau

これを試すことができます:

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

わたしにはできる。

0
anh tran