JavaScript에서 큰 문자열을 n 크기 청크로 분할


208

매우 큰 문자열 (예 : 10,000 문자)을 N 크기 청크로 나누고 싶습니다.

이를 수행하기위한 성능 측면에서 가장 좋은 방법은 무엇입니까?

예를 들어 "1234567890", 2로 나누면이 ["12", "34", "56", "78", "90"]됩니다.

이것을 사용하여 가능 String.prototype.match합니까? 그렇다면 성능 측면에서 가장 좋은 방법입니까?

답변:


456

다음과 같이 할 수 있습니다 :

"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에서 1 초가 조금 걸렸습니다. YMMV.

재사용 가능한 함수에서도 사용할 수 있습니다.

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

8
이 답변은 이제 거의 3 년이되었으므로 @Vivin의 성능 테스트를 다시 시도하고 싶었습니다. 참고로 주어진 정규 표현식을 사용하여 100k 문자를 두 개씩 두 개씩 나누는 것이 Chrome v33에서 즉각적입니다.
aymericbeaumet

1
@Fmstrat "문자열에 공백이 있으면 길이에 포함되지 않습니다"는 무슨 뜻입니까? 예, .줄 바꿈과 전혀 일치하지 않습니다. 걸리는 그래서 나는 대답을 업데이트 \n하고 \r계정으로.
Vivin Paliath

2
같은 것 var chunks = str.split("").reverse().join().match(/.{1, 4}/).map(function(s) { return s.split("").reverse().join(); });. 이것은 4의 덩어리로 이루어집니다. "적어도 더"가 무엇을 의미하는지 잘 모르겠습니다. 이는 일반적으로 결합 문자를 포함하고 유니 코드 문자열을 손상시킬 수있는 문자열에서 작동하지 않습니다.
Vivin Paliath

2
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 에 따르면 새 줄을 포함하여 모든 문자를로 일치시킬 수 있습니다 [^]. 이 예제를 사용하면 다음과 같은 결과가 나타납니다str.match(/[^]{1,n}/g)
Francesc Rosas

1
jsperf의 성능 벤치 마크로 매우 빠른 문자열 청크를 찾는 사람은 내 대답을 참조하십시오 . 정규식을 사용하는 것이 가장 느린 청킹 방법입니다.
저스틴 워 켄틴

34

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
}

2
그래서 이것은 긴 문자열 (800k-9m 문자)에서 훌륭하게 작동했습니다. , 어떤 이유로 크기를 20으로 설정했을 때 마지막 덩어리가 반환되지 않았습니다 ... 매우 이상한 행동.
David

1
@DavidAnderton 잘 잡았습니다. 나는 그것을 고쳤고 흥미롭게도 더 빨리 달리는 것처럼 보인다. Math.ceil()올바른 청크 수를 결정하기 위해 수행해야했을 때 반올림되었습니다 .
Justin Warkentin

1
감사! 나는 옵션으로 유니 코드를 지원하는 NPM 모듈로 구성했다 -github.com/vladgolubev/fast-chunk-string
Vlad Holubiev

33

결론 :

  • match매우 비효율적이다 slice파이어 폭스, 더 substr/ substring더 나은 여전히
  • match 캐시 된 정규 표현식을 사용하더라도 정규 표현식 구문 분석 설정 시간으로 인해 짧은 문자열에 대해 훨씬 비효율적입니다.
  • match 큰 청크 크기에 대해서는 훨씬 더 비효율적입니다 (아마 "점프"할 수 없기 때문).
  • 청크 크기가 매우 작은 더 긴 문자열의 경우 이전 IE 에서는 match성능이 slice뛰어나지 만 다른 모든 시스템에서는 여전히 손실됩니다
  • jsperf 바위

19

이것은 빠르고 간단한 솔루션입니다.

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", ... ]


1
substr()대신 사용해야 합니다 substring().
Leif

2
변수 이름의 밑줄이 왜 궁금합니까?
Felipe Valdes

@FelipeValdes 전역 / 매개 변수와 혼동하지 않거나 개인 범위로 표시한다고 가정합니다.
Mr. Polywhirl 2019

15

놀라다! split 을 사용하여 분할 할 수 있습니다 .

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

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


이것은 가장 멋진 답변입니다.
galkin

2
무엇입니까 filter (o=>o)?
벤 잉어

2
현재 정규식은 청크 사이에 빈 배열 요소를 만듭니다. filter(x=>x)빈 요소를 걸러내는 데 사용됩니다
artemdev

짧고 영리하지만 입력을 여러 번 반복합니다. 이 답변은이 스레드의 다른 솔루션보다 4 배 이상 느립니다.
당신을 감사

6
@ BenCarp 오토바이 운전자입니다. 더 빨라집니다. ;)
Fozi

7
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

5

확장 된 함수를 작성 했으므로 청크 길이는 [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' ]

4

큰 문자열을 주어진 단어의 작은 문자열로 나눕니다 .

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;
}

3
var l = str.length, lc = 0, chunks = [], c = 0, chunkSize = 2;
for (; lc < l; c++) {
  chunks[c] = str.slice(lc, lc += chunkSize);
}

2

정규식을 사용합니다 ...

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

1
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"]

1

약간의 실험 후에 템플릿 문자열에 대해 생각해 낸 해결책은 다음과 같습니다.

용법:

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


1

reduce()정규 표현식없이 사용할 수 있습니다 .

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

방법을 사용하는 방법에 대한 예제를 제공하면 많은 도움이 될 것이라고 생각합니다 reduce.
kiatng

0

프로토 타입 함수의 형태로 :

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

0

다음은 내가 사용하는 코드이며 String.prototype.slice 사용합니다 .

그렇습니다. 가능한 한 현재 표준을 최대한 가깝게 따르려고 노력하고 있으며, 상당한 양의 JSDOC 의견이 포함되어 있습니다 . 그러나 일단 축소되면 코드는 828 바이트에 불과하고 전송을 위해 gzipped되면 497 바이트에 불과합니다.

이것이 추가되는 1 메소드 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));
}());
<pre id="out"></pre>


0

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;
}

0

위의 해결책에 대한 나의 문제는 문장의 위치에 관계없이 문자열을 공식적인 크기 청크로 나눕니다.

나는 다음과 같은 더 나은 접근 방식을 생각합니다. 성능 조정이 필요하지만 :

 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;
    }

0

당신은 확실히 같은 것을 할 수 있습니다

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 ' ]

-1
    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.

-1

이 작은 코드는 어떻습니까?

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

-2
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;
}

4
귀하의 답변은 다른 답변과 비교하여 새로운 것을 추가하지 않으며 다른 답변과 같은 설명이 부족합니다.
플럽
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.