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バイトを代わりに報告します。
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);
}
これははるかに高速なバージョンで、正規表現も 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バイト長)に続きます。
TextEncoder
よりわずかに優れた互換性を持つ単純なUTF-8エンコードの場合、Blobがそのトリックを行います。ただし、非常に古いブラウザーでは機能しません。
new Blob(["????"]).size; // -> 4
この関数は、渡されたUTF-8文字列のバイトサイズを返します。
function byteCount(s) {
return encodeURI(s).split(/%..|./).length - 1;
}
Buffer
を使用した別の非常に単純なアプローチ(NodeJSのみ):
Buffer.from(string).length
実際、何が間違っているのかがわかりました。ページを機能させるコードの場合<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が必要なのは偶然です。
React Nativeの解決策を見つけるのに少し時間がかかったので、ここに置きます:
最初にbuffer
パッケージをインストールします。
npm install --save buffer
次に、nodeメソッドを使用します。
const { Buffer } = require('buffer');
const length = Buffer.byteLength(string, 'utf-8');
これは、文字列の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の不正な形式である場合、メソッドはエラーをスローする可能性がある
NodeJSでは、 Buffer.byteLength
は、この目的専用のメソッドです。
let strLengthInBytes = Buffer.byteLength(str); // str is UTF-8
デフォルトでは、メソッドは文字列がUTF-8エンコーディングであると想定していることに注意してください。別のエンコードが必要な場合は、2番目の引数として渡します。
これは、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.
これを試すことができます:
function getLengthInBytes(str) {
var b = str.match(/[^\x00-\xff]/g);
return (str.length + (!b ? 0: b.length));
}
わたしにはできる。