在我的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个字节。
答案 0 :(得分:79)
本机无法在JavaScript中执行此操作。
如果你知道字符编码,你可以自己计算。
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开始,或者以第一个十六进制数字为C,D,E或F的字节开始。第二个和后续字节是前两个字节为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);
}
答案 1 :(得分:68)
多年过去了,现在你可以原生地做到这一点
(new TextEncoder('utf-8').encode('foo')).length
请注意,IE(或Edge)尚未支持它(您可能会use a polyfill)。
答案 2 :(得分:56)
这是一个更快的版本,它不使用正则表达式,也不使用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返回的每个unicode代码点的UTF8长度(基于维基百科对UTF8和UTF16代理字符的描述)。
它遵循 RFC3629 (其中UTF-8字符最多为4个字节长)。
答案 3 :(得分:36)
对于简单的UTF-8编码,兼容性略高于TextEncoder
,Blob可以解决这个问题。但是,在非常老的浏览器中工作不会。
new Blob([""]).size; // -> 4
答案 4 :(得分:27)
此函数将返回传递给它的任何UTF-8字符串的字节大小。
function byteCount(s) {
return encodeURI(s).split(/%..|./).length - 1;
}
答案 5 :(得分:13)
使用Buffer
的另一种非常简单的方法(仅适用于NodeJS):
Buffer.from(string).length
答案 6 :(得分:3)
实际上,我发现了什么是错的。要使代码生效,页面<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就是巧合。
答案 7 :(得分:2)
这是一种计算字符串的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));
&#13;
注意如果输入字符串是UCS-2格式错误,该方法可能会抛出错误
答案 8 :(得分:2)
花了一些时间找到 React Native 的解决方案,所以我会把它放在这里:
首先安装buffer
包:
npm install --save buffer
然后使用节点方法:
const { Buffer } = require('buffer');
const length = Buffer.byteLength(string, 'utf-8');
答案 9 :(得分:1)
你可以试试这个:
function getLengthInBytes(str) {
var b = str.match(/[^\x00-\xff]/g);
return (str.length + (!b ? 0: b.length));
}
它对我有用。
答案 10 :(得分: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.
答案 11 :(得分:1)
我比较了Firefox中为提高速度而建议的一些方法。
我使用的字符串包含以下字符: ?œ´®†¥¨ˆøπ¬˚∆˙©ƒ∂ßåΩ≈ç√∫〜µ≤
所有结果均为3次运行的平均值。时间以毫秒为单位。请注意,所有URIEncoding方法的行为都相似,并且具有极端的结果,因此我只包括其中一种。
虽然基于字符串的大小会有一些波动,但是charCode方法(lovasoa和fuweichin)的执行效果相似且总体上最快,而fuweichin的charCode方法最快。 Blob和TextEncoder方法的执行彼此相似。通常,charCode方法比Blob和TextEncoder方法快约75%。 URIEncoding方法基本上是不可接受的。
这是我得到的结果:
大小6.4 * 10 ^ 6字节:
Lauri Oherd – URIEncoding: 6400000 et: 796
lovasoa – charCode: 6400000 et: 15
fuweichin – charCode2: 6400000 et: 16
simap – Blob: 6400000 et: 26
Riccardo Galli – TextEncoder: 6400000 et: 23
大小19.2 * 10 ^ 6字节: Blob在这里有点奇怪。
Lauri Oherd – URIEncoding: 19200000 et: 2322
lovasoa – charCode: 19200000 et: 42
fuweichin – charCode2: 19200000 et: 45
simap – Blob: 19200000 et: 169
Riccardo Galli – TextEncoder: 19200000 et: 70
大小64 * 10 ^ 6字节:
Lauri Oherd – URIEncoding: 64000000 et: 12565
lovasoa – charCode: 64000000 et: 138
fuweichin – charCode2: 64000000 et: 133
simap – Blob: 64000000 et: 231
Riccardo Galli – TextEncoder: 64000000 et: 211
大小192 * 10 ^ 6字节: URIEncoding方法此时会冻结浏览器。
lovasoa – charCode: 192000000 et: 754
fuweichin – charCode2: 192000000 et: 480
simap – Blob: 192000000 et: 701
Riccardo Galli – TextEncoder: 192000000 et: 654
大小640 * 10 ^ 6字节:
lovasoa – charCode: 640000000 et: 2417
fuweichin – charCode2: 640000000 et: 1602
simap – Blob: 640000000 et: 2492
Riccardo Galli – TextEncoder: 640000000 et: 2338
大小1280 * 10 ^ 6字节: Blob和TextEncoder方法在这里开始碰壁。
lovasoa – charCode: 1280000000 et: 4780
fuweichin – charCode2: 1280000000 et: 3177
simap – Blob: 1280000000 et: 6588
Riccardo Galli – TextEncoder: 1280000000 et: 5074
大小1920 * 10 ^ 6字节:
lovasoa – charCode: 1920000000 et: 7465
fuweichin – charCode2: 1920000000 et: 4968
JavaScript error: file:///Users/xxx/Desktop/test.html, line 74: NS_ERROR_OUT_OF_MEMORY:
代码如下:
function byteLengthURIEncoding(str) {
return encodeURI(str).split(/%..|./).length - 1;
}
function byteLengthCharCode(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;
}
function byteLengthCharCode2(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;
}
function byteLengthBlob(str) {
return new Blob([str]).size;
}
function byteLengthTE(str) {
return (new TextEncoder().encode(str)).length;
}
var sample = "?œ´®†¥¨ˆøπ¬˚∆˙©ƒ∂ßåΩ≈ç√∫˜µ≤i";
var string = "";
// Adjust multiplier to change length of string.
let mult = 1000000;
for (var i = 0; i < mult; i++) {
string += sample;
}
let t0;
try {
t0 = Date.now();
console.log("Lauri Oherd – URIEncoding: " + byteLengthURIEncoding(string) + " et: " + (Date.now() - t0));
} catch(e) {}
t0 = Date.now();
console.log("lovasoa – charCode: " + byteLengthCharCode(string) + " et: " + (Date.now() - t0));
t0 = Date.now();
console.log("fuweichin – charCode2: " + byteLengthCharCode2(string) + " et: " + (Date.now() - t0));
t0 = Date.now();
console.log("simap – Blob: " + byteLengthBlob(string) + " et: " + (Date.now() - t0));
t0 = Date.now();
console.log("Riccardo Galli – TextEncoder: " + byteLengthTE(string) + " et: " + (Date.now() - t0));
答案 12 :(得分:0)
在NodeJS中,Buffer.byteLength
是专门用于此目的的方法:
let strLengthInBytes = Buffer.byteLength(str); // str is UTF-8
请注意,默认情况下,该方法假定字符串采用UTF-8编码。如果需要其他编码,请将其作为第二个参数传递。
答案 13 :(得分:0)
遇到这个How to emulate mb_strlen in javascript with strings containing HTML
该字符串与之前的答案不匹配的地方。
我在这里得到了 8 的预期长度:
const str = 'X "FUEL"'
const div = document.createElement("div");
div.innerHTML = str
console.log(div.textContent.length)