如何将字符串拆分为特定字节大小的块?

use*_*287 9 javascript node.js

我正在与一个接受最大 5KB 字符串的 api 交互。

我想将一个可能超过 5KB 的字符串分成小于 5KB 的块。

然后我打算将每个传递smaller-than-5kb-string给 api 端点,并在所有请求完成后执行进一步的操作,可能使用类似的东西:

await Promise.all([get_thing_from_api(string_1), get_thing_from_api(string_2), get_thing_from_api(string_3)])
Run Code Online (Sandbox Code Playgroud)

我读过字符串中的字符可以在 1 - 4 个字节之间。

因此,要计算以字节为单位的字符串长度,我们可以使用:

// in Node, string is UTF-8    
Buffer.byteLength("here is some text"); 

// in Javascript  
new Blob(["here is some text"]).size
Run Code Online (Sandbox Code Playgroud)

来源:
https : //stackoverflow.com/a/56026151
/sf/answers/3657785841/

我搜索"how to split strings into chunks of a certain size"与将字符串拆分为特定字符长度而不是字节长度的字符串相关的返回结果,例如:

// in Node, string is UTF-8    
Buffer.byteLength("here is some text"); 

// in Javascript  
new Blob(["here is some text"]).size
Run Code Online (Sandbox Code Playgroud)

来源:
https : //stackoverflow.com/a/7033662
/sf/answers/438168041/
https://gist.github.com/hendriklammers/5231994

有没有办法将字符串拆分为特定字节长度的字符串?

可以

  • 假设字符串每个字符只包含 1 个字节
  • 允许每个字符为 4 个字节的“最坏情况”

但更喜欢更准确的解决方案。

我很想知道 Node 和普通 JavaScript 解决方案,如果它们存在的话。

编辑

这种计算方法byteLength可能会有所帮助 - 通过迭代字符串中的字符,获取它们的字符代码并相应地递增byteLength

var my_string = "1234 5 678905";

console.log(my_string.match(/.{1,2}/g));
// ["12", "34", " 5", " 6", "78", "90", "5"]
Run Code Online (Sandbox Code Playgroud)

来源:https : //stackoverflow.com/a/23329386

这让我对Buffer底层数据结构进行了有趣的实验:

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;
}
Run Code Online (Sandbox Code Playgroud)

但正如@trincot 在评论中指出的那样,处理多字节字符的正确方法是什么?我怎样才能确保块在空格上分开(以免“分开”一个词?)

有关缓冲区的更多信息:https : //nodejs.org/api/buffer.html#buffer_buffer

编辑

如果它可以帮助其他人理解已接受答案中的绝妙逻辑,下面的代码片段是我制作的大量评论版本,因此我可以更好地理解它。

var buf = Buffer.from('Hey! ?');
// <Buffer 48 65 79 21 20 d1 84>  
buf.length // 7
buf.toString().charCodeAt(0) // 72
buf.toString().charCodeAt(5) // 1092  
buf.toString().charCodeAt(6) // NaN    
buf[0] // 72
for (let i = 0; i < buf.length; i++) {
  console.log(buf[i]);
}
// 72 101 121 33 32 209 132 undefined
buf.slice(0,5).toString() // 'Hey! '
buf.slice(0,6).toString() // 'Hey! ?'
buf.slice(0,7).toString() // 'Hey! ?'
Run Code Online (Sandbox Code Playgroud)

tri*_*cot 8

使用Buffer似乎确实是正确的方向。鉴于:

  • Buffer原型具有indexOflastIndexOf方法,以及
  • 32 是一个空格的 ASCII 码,并且
  • 32 永远不会作为多字节字符的一部分出现,因为构成多字节序列的所有字节总是设置最高有效位

...您可以按照以下步骤进行:

function chunk(s, maxBytes) {
    let buf = Buffer.from(s);
    const result = [];
    while (buf.length) {
        let i = buf.lastIndexOf(32, maxBytes+1);
        // If no space found, try forward search
        if (i < 0) i = buf.indexOf(32, maxBytes);
        // If there's no space at all, take the whole string
        if (i < 0) i = buf.length;
        // This is a safe cut-off point; never half-way a multi-byte
        result.push(buf.slice(0, i).toString());
        buf = buf.slice(i+1); // Skip space (if any)
    }
    return result;
}

console.log(chunk("Hey there! € 100 to pay", 12)); 
// -> [ 'Hey there!', '€ 100 to', 'pay' ]
Run Code Online (Sandbox Code Playgroud)

您可以考虑将其扩展为还查找 TAB、LF 或 CR 作为拆分字符。如果是这样,并且您的输入文本可能具有 CRLF 序列,您还需要检测这些序列以避免在块中出现孤立的 CR 或 LF 字符。

您可以将上述函数转换为生成器,以便您控制何时开始处理以获取下一个块:

function * chunk(s, maxBytes) {
    let buf = Buffer.from(s);
    while (buf.length) {
        let i = buf.lastIndexOf(32, maxBytes+1);
        // If no space found, try forward search
        if (i < 0) i = buf.indexOf(32, maxBytes);
        // If there's no space at all, take all
        if (i < 0) i = buf.length;
        // This is a safe cut-off point; never half-way a multi-byte
        yield buf.slice(0, i).toString();
        buf = buf.slice(i+1); // Skip space (if any)
    }
}

for (let s of chunk("Hey there! € 100 to pay", 12)) console.log(s);
Run Code Online (Sandbox Code Playgroud)

浏览器

Buffer特定于节点。然而,浏览器实现了TextEncoderandTextDecoder,这会导致类似的代码:

function chunk(s, maxBytes) {
    let buf = Buffer.from(s);
    const result = [];
    while (buf.length) {
        let i = buf.lastIndexOf(32, maxBytes+1);
        // If no space found, try forward search
        if (i < 0) i = buf.indexOf(32, maxBytes);
        // If there's no space at all, take the whole string
        if (i < 0) i = buf.length;
        // This is a safe cut-off point; never half-way a multi-byte
        result.push(buf.slice(0, i).toString());
        buf = buf.slice(i+1); // Skip space (if any)
    }
    return result;
}

console.log(chunk("Hey there! € 100 to pay", 12)); 
// -> [ 'Hey there!', '€ 100 to', 'pay' ]
Run Code Online (Sandbox Code Playgroud)