在 JavaScript 中将大字符串拆分为 n 大小的块

Posted

技术标签:

【中文标题】在 JavaScript 中将大字符串拆分为 n 大小的块【英文标题】:Split large string in n-size chunks in JavaScript 【发布时间】:2011-10-25 09:46:07 【问题描述】:

我想将一个非常大的字符串(比如说,10,000 个字符)拆分成 N 大小的块。

就性能而言,这样做的最佳方式是什么?

例如: "1234567890" 除以 2 将变为 ["12", "34", "56", "78", "90"]

使用String.prototype.match 是否可以实现这样的事情,如果可以,这是否是性能方面的最佳方法?

【问题讨论】:

【参考方案1】:

你可以这样做:

"1234567890".match(/.1,2/g);
// Results in:
["12", "34", "56", "78", "90"]

该方法仍然适用于大小不是块大小的精确倍数的字符串:

"123456789".match(/.1,2/g);
// Results in:
["12", "34", "56", "78", "9"]

一般来说,对于您要从中提取最多 n 个子字符串的任何字符串,您会这样做:

str.match(/.1,n/g); // Replace n with the size of the substring

如果你的字符串可以包含换行符或回车,你会这样做:

str.match(/(.|[\r\n])1,n/g); // Replace n with the size of the substring

就性能而言,我尝试了大约 10k 个字符,但在 Chrome 上花了一秒钟多一点的时间。 YMMV。

这也可以用于可重用函数:

function chunkString(str, length) 
  return str.match(new RegExp('.1,' + length + '', 'g'));

【讨论】:

由于这个答案现在已经快3年了,我想再次尝试@Vivin所做的性能测试。所以仅供参考,在 Chrome v33 上,使用给定的正则表达式将 100k 个字符两两分割是即时的。 @Fmstrat “如果你的字符串包含空格,它不计入长度”是什么意思?是的,. 根本不匹配换行符。我将更新答案,以便将\n\r 考虑在内。 类似var chunks = str.split("").reverse().join().match(/.1, 4/).map(function(s) return s.split("").reverse().join(); );。这以 4 个为一组。我不确定您所说的“更少或更多”是什么意思。请记住,这通常不起作用,尤其是对于包含组合字符并且也会破坏 Unicode 字符串的字符串。 根据developer.mozilla.org/en-US/docs/Web/javascript/Reference/…,您可以将任何字符(包括换行符)与[^] 匹配。有了这个你的例子将导致str.match(/[^]1,n/g) 对于在 jsperf 上通过性能基准寻找真正快速的字符串分块的任何人,请参阅我的 answer。使用正则表达式是最慢的分块方法。【参考方案2】:

我创建了几个更快的变体,您可以see on jsPerf。我最喜欢的是这个:

function chunkSubstr(str, size) 
  const numChunks = Math.ceil(str.length / size)
  const chunks = new Array(numChunks)

  for (let i = 0, o = 0; i < numChunks; ++i, o += size) 
    chunks[i] = str.substr(o, size)
  

  return chunks

【讨论】:

所以这在长字符串(大约 800k - 9m 字符)except 上非常有效,当我出于某种原因将大小设置为 20 时,最后一个块没有返回......非常奇怪的行为。 @DavidAnderton 很好。我修复了它,有趣的是它似乎运行得更快。当它应该执行Math.ceil() 以确定正确的块数时,它正在四舍五入。 谢谢!我把他作为一个 NPM 模块放在一起,带有可选的 Unicode 支持 - github.com/vladgolubev/fast-chunk-string【参考方案3】: comparison of match, slice, substr and substring comparison of match and slice for different chunk sizes comparison of match and slice with small chunk size

底线:

match 效率很低,slice 更好,在 Firefox 上 substr/substring 更好 match 对于短字符串效率更低(即使使用缓存的正则表达式 - 可能是由于正则表达式解析设置时间) match 对于大块大小的效率更低(可能是由于无法“跳转”) 对于具有非常小的块大小的较长字符串,match 在旧版 IE 上的性能优于 slice,但在所有其他系统上仍然失败 jsperf岩石

【讨论】:

jsperf 链接已损坏【参考方案4】:

这是一个快速而直接的解决方案 -

function chunkString (str, len) 
  const size = Math.ceil(str.length/len)
  const r = Array(size)
  let offset = 0
  
  for (let i = 0; i < size; i++) 
    r[i] = str.substr(offset, len)
    offset += len
  
  
  return r


console.log(chunkString("helloworld", 3))
// => [ "hel", "low", "orl", "d" ]

// 10,000 char string
const bigString = "helloworld".repeat(1000)
console.time("perf")
const result = chunkString(bigString, 3)
console.timeEnd("perf")
console.log(result)
// => perf: 0.385 ms
// => [ "hel", "low", "orl", "dhe", "llo", "wor", ... ]

【讨论】:

你必须使用substr() 而不是substring() 我很好奇,为什么变量名中有下划线? @FelipeValdes 我假设不会将它们与全局/参数变量混淆或将它们表示为私有范围。【参考方案5】:

惊喜!可以使用split进行拆分。

var parts = "1234567890 ".split(/(.2)/).filter(O=>O)

[ '12', '34', '56', '78', '90', ' ' ] 中的结果

【讨论】:

filter (o=&gt;o) 是干什么用的? 当前正则表达式在块之间创建空数组元素。 filter(x=&gt;x) 用于过滤掉那些空元素 简短而聪明,但会多次迭代输入。这个答案比这个线程中的其他解决方案慢 4 倍以上。 @BenCarp 是摩托车司机。它使它运行得更快。 ;)【参考方案6】:
var str = "123456789";
var chunks = [];
var chunkSize = 2;

while (str) 
    if (str.length < chunkSize) 
        chunks.push(str);
        break;
    
    else 
        chunks.push(str.substr(0, chunkSize));
        str = str.substr(chunkSize);
    


alert(chunks); // chunks == 12,34,56,78,9

【讨论】:

【参考方案7】:

你绝对可以做类似的事情

let pieces = "1234567890 ".split(/(.2)/).filter(x => x.length == 2);

得到这个:

[ '12', '34', '56', '78', '90' ]

如果你想动态输入/调整块大小,使块大小为 n,你可以这样做:

n = 2;
let pieces = "1234567890 ".split(new RegExp("(."+n.toString()+")")).filter(x => x.length == n);

要在原始字符串中找到所有可能大小为 n 的块,试试这个:

let subs = new Set();
let n = 2;
let str = "1234567890 ";
let regex = new RegExp("(."+n.toString()+")");     //set up regex expression dynamically encoded with n

for (let i = 0; i < n; i++)               //starting from all possible offsets from position 0 in the string
    let pieces = str.split(regex).filter(x => x.length == n);    //divide the string into chunks of size n...
    for (let p of pieces)                 //...and add the chunks to the set
        subs.add(p);
    str = str.substr(1);    //shift the string reading frame

你应该得到:

[ '12', '23', '34', '45', '56', '67', '78', '89', '90', '0 ' ]

【讨论】:

【参考方案8】:

我写了一个扩展函数,所以chunk长度也可以是一个数字数组,比如[1,3]

String.prototype.chunkString = function(len) 
    var _ret;
    if (this.length < 1) 
        return [];
    
    if (typeof len === 'number' && len > 0) 
        var _size = Math.ceil(this.length / len), _offset = 0;
        _ret = new Array(_size);
        for (var _i = 0; _i < _size; _i++) 
            _ret[_i] = this.substring(_offset, _offset = _offset + len);
        
    
    else if (typeof len === 'object' && len.length) 
        var n = 0, l = this.length, chunk, that = this;
        _ret = [];
        do 
            len.forEach(function(o) 
                chunk = that.substring(n, n + o);
                if (chunk !== '') 
                    _ret.push(chunk);
                    n += chunk.length;
                
            );
            if (n === 0) 
                return undefined; // prevent an endless loop when len = [0]
            
         while (n < l);
    
    return _ret;
;

代码

"1234567890123".chunkString([1,3])

将返回:

[ '1', '234', '5', '678', '9', '012', '3' ]

【讨论】:

【参考方案9】:
var l = str.length, lc = 0, chunks = [], c = 0, chunkSize = 2;
for (; lc < l; c++) 
  chunks[c] = str.slice(lc, lc += chunkSize);

【讨论】:

【参考方案10】:

它将大字符串拆分为给定单词的小字符串。

function chunkSubstr(str, words) 
  var parts = str.split(" ") , values = [] , i = 0 , tmpVar = "";
  $.each(parts, function(index, value) 
      if(tmpVar.length < words)
          tmpVar += " " + value;
      else
          values[i] = tmpVar.replace(/\s+/g, " ");
          i++;
          tmpVar = value;
      
  );
  if(values.length < 1 &&  parts.length > 0)
      values[0] = tmpVar;
  
  return values;

【讨论】:

【参考方案11】:

我会使用正则表达式...

var chunkStr = function(str, chunkLength) 
    return str.match(new RegExp('[\\s\\S]1,' + +chunkLength + '', 'g'));

【讨论】:

【参考方案12】:
const getChunksFromString = (str, chunkSize) => 
    var regexChunk = new RegExp(`.1,$chunkSize`, 'g')   // '.' represents any character
    return str.match(regexChunk)

根据需要调用它

console.log(getChunksFromString("Hello world", 3))   // ["Hel", "lo ", "wor", "ld"]

【讨论】:

【参考方案13】:

包括带有预分配的左右版本。 这与小块的 RegExp impl 一样快,但它随着块大小的增长而变得更快。而且它的内存效率很高。

function chunkLeft (str, size = 3) 
  if (typeof str === 'string') 
    const length = str.length
    const chunks = Array(Math.ceil(length / size))
    for (let i = 0, index = 0; index < length; i++) 
      chunks[i] = str.slice(index, index += size)
    
    return chunks
  


function chunkRight (str, size = 3) 
  if (typeof str === 'string') 
    const length = str.length
    const chunks = Array(Math.ceil(length / size))
    if (length) 
      chunks[0] = str.slice(0, length % size || size)
      for (let i = 1, index = chunks[0].length; index < length; i++) 
        chunks[i] = str.slice(index, index += size)
      
    
    return chunks
  


console.log(chunkRight())  // undefined
console.log(chunkRight(''))  // []
console.log(chunkRight('1'))  // ["1"]
console.log(chunkRight('123'))  // ["123"]
console.log(chunkRight('1234'))  // ["1", "234"]
console.log(chunkRight('12345'))  // ["12", "345"]
console.log(chunkRight('123456'))  // ["123", "456"]
console.log(chunkRight('1234567'))  // ["1", "234", "567"]

【讨论】:

附注我发现 slice 比 substr 快一点【参考方案14】:

经过一番试验,我想出了一个用于模板字符串的解决方案:

用法:

chunkString(5)`testing123`

function chunkString(nSize) 
    return (strToChunk) => 
        let result = [];
        let chars = String(strToChunk).split('');

        for(let i = 0; i < (String(strToChunk).length / nSize); i++) 
            result = result.concat(chars.slice(i*nSize,(i+1)*nSize).join(''));
        
        return result
    


document.write(chunkString(5)`testing123`);
// returns: testi,ng123

document.write(chunkString(3)`testing123`);
// returns: tes,tin,g12,3

【讨论】:

【参考方案15】:

您可以使用reduce() 而不使用任何正则表达式:

(str, n) => 
  return str.split('').reduce(
    (acc, rec, index) => 
      return ((index % n) || !(index)) ? acc.concat(rec) : acc.concat(',', rec)
    ,
    ''
  ).split(',')

【讨论】:

我认为,如果您能提供有关如何使用 reduce 方法的示例,将会有很大帮助。【参考方案16】:

以原型函数的形式:

String.prototype.lsplit = function()
    return this.match(new RegExp('.1,'+ ((arguments.length==1)?(isFinite(String(arguments[0]).trim())?arguments[0]:false):1) +'', 'g'));

【讨论】:

【参考方案17】:

这是我正在使用的代码,它使用String.prototype.slice。

是的,答案很长,因为它试图尽可能接近当前标准,当然包含合理数量的JSDOC cmets。但是,压缩后的代码只有 828 字节,压缩传输后只有 497 字节。

这添加到String.prototype(使用Object.defineProperty,如果可用)的1 方法是:

    到大块

已包含许多测试来检查功能。

担心代码长度会影响性能?不用担心,http://jsperf.com/chunk-string/3

许多额外的代码是为了确保代码在多个 javascript 环境中响应相同。

/*jslint maxlen:80, browser:true, devel:true */

/*
 * Properties used by toChunks.
 */

/*property
    MAX_SAFE_INTEGER, abs, ceil, configurable, defineProperty, enumerable,
    floor, length, max, min, pow, prototype, slice, toChunks, value,
    writable
*/

/*
 * Properties used in the testing of toChunks implimentation.
 */

/*property
    appendChild, createTextNode, floor, fromCharCode, getElementById, length,
    log, pow, push, random, toChunks
*/

(function () 
    'use strict';

    var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1;

    /**
     * Defines a new property directly on an object, or modifies an existing
     * property on an object, and returns the object.
     *
     * @private
     * @function
     * @param Object object
     * @param string property
     * @param Object descriptor
     * @return Object
     * @see https://goo.gl/CZnEqg
     */
    function $defineProperty(object, property, descriptor) 
        if (Object.defineProperty) 
            Object.defineProperty(object, property, descriptor);
         else 
            object[property] = descriptor.value;
        

        return object;
    

    /**
     * Returns true if the operands are strictly equal with no type conversion.
     *
     * @private
     * @function
     * @param * a
     * @param * b
     * @return boolean
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4
     */
    function $strictEqual(a, b) 
        return a === b;
    

    /**
     * Returns true if the operand inputArg is undefined.
     *
     * @private
     * @function
     * @param * inputArg
     * @return boolean
     */
    function $isUndefined(inputArg) 
        return $strictEqual(typeof inputArg, 'undefined');
    

    /**
     * The abstract operation throws an error if its argument is a value that
     * cannot be converted to an Object, otherwise returns the argument.
     *
     * @private
     * @function
     * @param * inputArg The object to be tested.
     * @throws TypeError If inputArg is null or undefined.
     * @return * The inputArg if coercible.
     * @see https://goo.gl/5GcmVq
     */
    function $requireObjectCoercible(inputArg) 
        var errStr;

        if (inputArg === null || $isUndefined(inputArg)) 
            errStr = 'Cannot convert argument to object: ' + inputArg;
            throw new TypeError(errStr);
        

        return inputArg;
    

    /**
     * The abstract operation converts its argument to a value of type string
     *
     * @private
     * @function
     * @param * inputArg
     * @return string
     * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tostring
     */
    function $toString(inputArg) 
        var type,
            val;

        if (inputArg === null) 
            val = 'null';
         else 
            type = typeof inputArg;
            if (type === 'string') 
                val = inputArg;
             else if (type === 'undefined') 
                val = type;
             else 
                if (type === 'symbol') 
                    throw new TypeError('Cannot convert symbol to string');
                

                val = String(inputArg);
            
        

        return val;
    

    /**
     * Returns a string only if the arguments is coercible otherwise throws an
     * error.
     *
     * @private
     * @function
     * @param * inputArg
     * @throws TypeError If inputArg is null or undefined.
     * @return string
     */
    function $onlyCoercibleToString(inputArg) 
        return $toString($requireObjectCoercible(inputArg));
    

    /**
     * The function evaluates the passed value and converts it to an integer.
     *
     * @private
     * @function
     * @param * inputArg The object to be converted to an integer.
     * @return number If the target value is NaN, null or undefined, 0 is
     *                   returned. If the target value is false, 0 is returned
     *                   and if true, 1 is returned.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
     */
    function $toInteger(inputArg) 
        var number = +inputArg,
            val = 0;

        if ($strictEqual(number, number)) 
            if (!number || number === Infinity || number === -Infinity) 
                val = number;
             else 
                val = (number > 0 || -1) * Math.floor(Math.abs(number));
            
        

        return val;
    

    /**
     * The abstract operation ToLength converts its argument to an integer
     * suitable for use as the length of an array-like object.
     *
     * @private
     * @function
     * @param * inputArg The object to be converted to a length.
     * @return number If len <= +0 then +0 else if len is +INFINITY then
     *                   2^53-1 else min(len, 2^53-1).
     * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
     */
    function $toLength(inputArg) 
        return Math.min(Math.max($toInteger(inputArg), 0), MAX_SAFE_INTEGER);
    

    if (!String.prototype.toChunks) 
        /**
         * This method chunks a string into an array of strings of a specified
         * chunk size.
         *
         * @function
         * @this string The string to be chunked.
         * @param Number chunkSize The size of the chunks that the string will
         *                           be chunked into.
         * @returns Array Returns an array of the chunked string.
         */
        $defineProperty(String.prototype, 'toChunks', 
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (chunkSize) 
                var str = $onlyCoercibleToString(this),
                    chunkLength = $toInteger(chunkSize),
                    chunked = [],
                    numChunks,
                    length,
                    index,
                    start,
                    end;

                if (chunkLength < 1) 
                    return chunked;
                

                length = $toLength(str.length);
                numChunks = Math.ceil(length / chunkLength);
                index = 0;
                start = 0;
                end = chunkLength;
                chunked.length = numChunks;
                while (index < numChunks) 
                    chunked[index] = str.slice(start, end);
                    start = end;
                    end += chunkLength;
                    index += 1;
                

                return chunked;
            
        );
    
());

/*
 * Some tests
 */

(function () 
    'use strict';

    var pre = document.getElementById('out'),
        chunkSizes = [],
        maxChunkSize = 512,
        testString = '',
        maxTestString = 100000,
        chunkSize = 0,
        index = 1;

    while (chunkSize < maxChunkSize) 
        chunkSize = Math.pow(2, index);
        chunkSizes.push(chunkSize);
        index += 1;
    

    index = 0;
    while (index < maxTestString) 
        testString += String.fromCharCode(Math.floor(Math.random() * 95) + 32);
        index += 1;
    

    function log(result) 
        pre.appendChild(document.createTextNode(result + '\n'));
    

    function test() 
        var strLength = testString.length,
            czLength = chunkSizes.length,
            czIndex = 0,
            czValue,
            result,
            numChunks,
            pass;

        while (czIndex < czLength) 
            czValue = chunkSizes[czIndex];
            numChunks = Math.ceil(strLength / czValue);
            result = testString.toChunks(czValue);
            czIndex += 1;
            log('chunksize: ' + czValue);
            log(' Number of chunks:');
            log('  Calculated: ' + numChunks);
            log('  Actual:' + result.length);
            pass = result.length === numChunks;
            log(' First chunk size: ' + result[0].length);
            pass = pass && result[0].length === czValue;
            log(' Passed: ' + pass);
            log('');
        
    

    test();
    log('');
    log('Simple test result');
    log('abcdefghijklmnopqrstuvwxyz'.toChunks(3));
());
&lt;pre id="out"&gt;&lt;/pre&gt;

【讨论】:

【参考方案18】:

使用 slice() 方法:

function returnChunksArray(str, chunkSize) 
  var arr = [];
  while(str !== '') 
    arr.push(str.slice(0, chunkSize));
    str = str.slice(chunkSize);
  
  return arr;

同样可以使用 substring() 方法完成。

function returnChunksArray(str, chunkSize) 
  var arr = [];
  while(str !== '') 
    arr.push(str.substring(0, chunkSize));
    str = str.substring(chunkSize);
  
  return arr;

【讨论】:

由于使用了 push()、slice()、substring(),这会做一些相对昂贵的数组内存读/写。 @Justin Warkentin 的回答效率更高,同时保持与此解决方案相同的可读性。【参考方案19】:

那一小段代码呢:

function splitME(str, size) 
    let subStr = new RegExp('.1,' + size + '', 'g');
    return str.match(subStr);
;

【讨论】:

【参考方案20】:

我对上述解决方案的问题是,无论句子中的位置如何,它都会将字符串分成正式大小的块。

我认为以下是更好的方法;虽然它需要一些性能调整:

 static chunkString(str, length, size,delimiter='\n' ) 
        const result = [];
        for (let i = 0; i < str.length; i++) 
            const lastIndex = _.lastIndexOf(str, delimiter,size + i);
            result.push(str.substr(i, lastIndex - i));
            i = lastIndex;
        
        return result;
    

【讨论】:

【参考方案21】:

使用这个 npm 库“chkchars” 但请记住确保给定字符串的长度完全除以“数字”参数。

const phrase = "1110010111010011100101110100010000011100101110100111001011101001011101001110010111010001000001110010111010011100101110100"
const number = 7

chkchars.splitToChunks(phrase, number)

// result => ['1110010', '1110100','1110010', '1110100','0100000', '1110010','1110100', '1110010','1110100', '1011101','0011100', '1011101','0001000','0011100','1011101', '0011100','1011101']

// perf => 0.287ms

【讨论】:

【参考方案22】:
    window.format = function(b, a) 
        if (!b || isNaN(+a)) return a;
        var a = b.charAt(0) == "-" ? -a : +a,
            j = a < 0 ? a = -a : 0,
            e = b.match(/[^\d\-\+#]/g),
            h = e && e[e.length - 1] || ".",
            e = e && e[1] && e[0] || ",",
            b = b.split(h),
            a = a.toFixed(b[1] && b[1].length),
            a = +a + "",
            d = b[1] && b[1].lastIndexOf("0"),
            c = a.split(".");
        if (!c[1] || c[1] && c[1].length <= d) a = (+a).toFixed(d + 1);
        d = b[0].split(e);
        b[0] = d.join("");
        var f = b[0] && b[0].indexOf("0");
        if (f > -1)
            for (; c[0].length < b[0].length - f;) c[0] = "0" + c[0];
        else +c[0] == 0 && (c[0] = "");
        a = a.split(".");
        a[0] = c[0];
        if (c = d[1] && d[d.length -
                1].length) 
            for (var d = a[0], f = "", k = d.length % c, g = 0, i = d.length; g < i; g++) f += d.charAt(g), !((g - k + 1) % c) && g < i - c && (f += e);
            a[0] = f
        
        a[1] = b[1] && a[1] ? h + a[1] : "";
        return (j ? "-" : "") + a[0] + a[1]
    ;

var str="1234567890";
var formatstr=format( "##,###.", str);
alert(formatstr);


This will split the string in reverse order with comma separated after 3 char's. If you want you can change the position.

【讨论】:

【参考方案23】:
function chunkString(str, length = 10) 
    let result = [],
        offset = 0;
    if (str.length <= length) return result.push(str) && result;
    while (offset < str.length) 
        result.push(str.substr(offset, length));
        offset += length;
    
    return result;

【讨论】:

您的答案没有添加任何新内容(与其他答案相比),并且缺少与其他答案一样的任何描述。

以上是关于在 JavaScript 中将大字符串拆分为 n 大小的块的主要内容,如果未能解决你的问题,请参考以下文章

在javascript中将字符串拆分为匹配和不匹配的组

在Javascript中将PDF拆分为单独的文件

如何在 JavaScript 中将长正则表达式拆分为多行?

如何在 Rust 中将字符串拆分为块以插入空格

在javascript中将单词拆分为音节

如何在标准sql-大查询中将一列拆分为多列