¿Cómo puedo dividir una cadena en segmentos de n caracteres?

201

Como dice el título, tengo una cadena y quiero dividirla en segmentos de n caracteres.

Por ejemplo:

var str = 'abcdefghijkl';

después de un poco de magia n=3, se convertirá

var arr = ['abc','def','ghi','jkl'];

¿Hay alguna forma de hacer esto?

Ben
fuente

Respuestas:

359

var str = 'abcdefghijkl';
console.log(str.match(/.{1,3}/g));

Nota: Use en {1,3}lugar de solo {3}incluir el resto para las longitudes de cadena que no son múltiplos de 3, por ejemplo:

console.log("abcd".match(/.{1,3}/g)); // ["abc", "d"]


Un par de sutilezas más:

  1. Si su cadena puede contener nuevas líneas ( que desea contar como un carácter en lugar de dividir la cadena ), entonces .no las capturará. Usar en su /[\s\S]{1,3}/lugar. (Gracias @ Mike).
  2. Si su cadena está vacía, match()volverá nullcuando espere una matriz vacía. Protéjase contra esto agregando || [].

Entonces puede terminar con:

var str = 'abcdef \t\r\nghijkl';
var parts = str.match(/[\s\S]{1,3}/g) || [];
console.log(parts);

console.log(''.match(/[\s\S]{1,3}/g) || []);

David Tang
fuente
Esta es técnicamente la mejor respuesta, ya que tomará todo el texto de una cadena que no sea divisible por 3 (tomará los últimos 2 o 1 caracteres).
Erik
66
Use en [\s\S]lugar de .no fallar en las nuevas líneas.
Mike Samuel
2
Es posible que desee comenzar un nuevo ciclo en cada línea. Si realmente tiene nuevas líneas, probablemente indiquen algún tipo de transición. str.match (/. {1,3} / gm) puede ser una mejor opción.
kennebec
+1 Cuidado: ''.match(/.{1,3}/g) y ''.match(/.{3}/g)regresar en nulllugar de una matriz vacía.
Web_Designer
44
¿Es posible tener una variable en el lugar del número 3?
Ana Claudia
46

Si no quieres usar una expresión regular ...

var chunks = [];

for (var i = 0, charsLength = str.length; i < charsLength; i += 3) {
    chunks.push(str.substring(i, i + 3));
}

jsFiddle .

... de lo contrario, la solución regex es bastante buena :)

alex
fuente
1
+1 porque preferiría esto si la 3variable es la sugerida por el OP. Es más legible que concatenar una cadena regexp.
David Tang
si tan solo pudieras envolver eso en una función útil lista para ser utilizada
mmm
1
Esto es más de 10 veces más rápido que la opción regex, así que iría con esto (dentro de una función) jsbench.github.io/#9cb819bf1ce429575f8535a211f72d5a
Job
1
Mi declaración anterior se aplica a Chromium (también, era demasiado tarde para editar el comentario anterior, de ahí el nuevo). En Firefox actualmente es "solo" un 30% más rápido en mi máquina, pero eso es aún mejor.
Trabajo
¿Es esto sostenible en grandes longitudes de cuerda?
Jacob Schneider
22
str.match(/.{3}/g); // => ['abc', 'def', 'ghi', 'jkl']
maerics
fuente
Esto funciona para 3mí pero vuelve nullcon 250. 🤔
Jim
9

Partiendo de las respuestas anteriores a esta pregunta; la siguiente función dividirá una cadena ( str) n-número ( size) de caracteres.

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

Manifestación

(function() {
  function chunk(str, size) {
    return str.match(new RegExp('.{1,' + size + '}', 'g'));
  }
  
  var str = 'HELLO WORLD';
  println('Simple binary representation:');
  println(chunk(textToBin(str), 8).join('\n'));
  println('\nNow for something crazy:');
  println(chunk(textToHex(str, 4), 8).map(function(h) { return '0x' + h }).join('  '));
  
  // Utiliy functions, you can ignore these.
  function textToBin(text) { return textToBase(text, 2, 8); }
  function textToHex(t, w) { return pad(textToBase(t,16,2), roundUp(t.length, w)*2, '00'); }
  function pad(val, len, chr) { return (repeat(chr, len) + val).slice(-len); }
  function print(text) { document.getElementById('out').innerHTML += (text || ''); }
  function println(text) { print((text || '') + '\n'); }
  function repeat(chr, n) { return new Array(n + 1).join(chr); }
  function textToBase(text, radix, n) {
    return text.split('').reduce(function(result, chr) {
      return result + pad(chr.charCodeAt(0).toString(radix), n, '0');
    }, '');
  }
  function roundUp(numToRound, multiple) { 
    if (multiple === 0) return numToRound;
    var remainder = numToRound % multiple;
    return remainder === 0 ? numToRound : numToRound + multiple - remainder;
  }
}());
#out {
  white-space: pre;
  font-size: 0.8em;
}
<div id="out"></div>

Mr. Polywhirl
fuente
2

Mi solución (sintaxis ES6):

const source = "8d7f66a9273fc766cd66d1d";
const target = [];
for (
    const array = Array.from(source);
    array.length;
    target.push(array.splice(0,2).join(''), 2));

Incluso podríamos crear una función con esto:

function splitStringBySegmentLength(source, segmentLength) {
    if (!segmentLength || segmentLength < 1) throw Error('Segment length must be defined and greater than/equal to 1');
    const target = [];
    for (
        const array = Array.from(source);
        array.length;
        target.push(array.splice(0,segmentLength).join('')));
    return target;
}

Luego puede llamar a la función fácilmente de manera reutilizable:

const source = "8d7f66a9273fc766cd66d1d";
const target = splitStringBySegmentLength(source, 2);

Salud

Jesus Gonzalez
fuente
2
const chunkStr = (str, n, acc) => {     
    if (str.length === 0) {
        return acc
    } else {
        acc.push(str.substring(0, n));
        return chunkStr(str.substring(n), n, acc);
    }
}
const str = 'abcdefghijkl';
const splittedString = chunkStr(str, 3, []);

Solución limpia sin REGEX

gravepat
fuente
1
function chunk(er){
return er.match(/.{1,75}/g).join('\n');
}

La función anterior es lo que uso para la fragmentación de Base64. Creará un salto de línea cada 75 caracteres.

Dave Brown
fuente
También podría hacer replace(/.{1,75}/g, '$&\n').
alex
1

Aquí entremezclamos una cadena con otra cadena cada n caracteres:

export const intersperseString = (n: number, intersperseWith: string, str: string): string => {

  let ret = str.slice(0,n), remaining = str;

  while (remaining) {
    let v = remaining.slice(0, n);
    remaining = remaining.slice(v.length);
    ret += intersperseWith + v;
  }

  return ret;

};

si usamos lo anterior así:

console.log(splitString(3,'|', 'aagaegeage'));

obtenemos:

aag | aag | aeg | eag | e

y aquí hacemos lo mismo, pero empujamos a una matriz:

export const sperseString = (n: number, str: string): Array<string> => {

  let ret = [], remaining = str;

  while (remaining) {
    let v = remaining.slice(0, n);
    remaining = remaining.slice(v.length);
    ret.push(v);
  }

  return ret;

};

y luego ejecutarlo:

console.log(sperseString(5, 'foobarbaztruck'));

obtenemos:

['fooba', 'rbazt', 'ruck']

si alguien conoce una manera de simplificar el código anterior, lmk, pero debería funcionar bien para las cadenas.

Alexander Mills
fuente
su primer fragmento no funcionó como se esperaba. Modifiqué
omarojo
0

Alguna solución limpia sin usar expresiones regulares:

/**
* Create array with maximum chunk length = maxPartSize
* It work safe also for shorter strings than part size
**/
function convertStringToArray(str, maxPartSize){

  const chunkArr = [];
  let leftStr = str;
  do {

    chunkArr.push(leftStr.substring(0, maxPartSize));
    leftStr = leftStr.substring(maxPartSize, leftStr.length);

  } while (leftStr.length > 0);

  return chunkArr;
};

Ejemplo de uso: https://jsfiddle.net/maciejsikora/b6xppj4q/ .

También intenté comparar mi solución para regexp una que fue elegida como respuesta correcta. Algunas pruebas se pueden encontrar en jsfiddle - https://jsfiddle.net/maciejsikora/2envahrk/ . Las pruebas muestran que ambos métodos tienen un rendimiento similar, tal vez a primera vista la solución regexp es un poco más rápida, pero juzgue usted mismo.

Maciej Sikora
fuente
0

Con .split:

var arr = str.split( /(?<=^(?:.{3})+)(?!$)/ )  // [ 'abc', 'def', 'ghi', 'jkl' ]

y .replaceserá:

var replaced = str.replace( /(?<=^(.{3})+)(?!$)/g, ' || ' )  // 'abc || def || ghi || jkl'



/(?!$)/es detenerse antes de finalizar /$/, sin es:

var arr      = str.split( /(?<=^(?:.{3})+)/ )        // [ 'abc', 'def', 'ghi', 'jkl' ]     // I don't know why is not [ 'abc', 'def', 'ghi', 'jkl' , '' ], comment?
var replaced = str.replace( /(?<=^(.{3})+)/g, ' || ')  // 'abc || def || ghi || jkl || '

ignorar el grupo /(?:... )/no es necesario, .replacepero en .splitagregar grupos al arr:

var arr = str.split( /(?<=^(.{3})+)(?!$)/ )  // [ 'abc', 'abc', 'def', 'abc', 'ghi', 'abc', 'jkl' ]
Денис Дечев
fuente
0

Aquí hay una manera de hacerlo sin expresiones regulares o bucles explícitos, aunque está ampliando un poco la definición de un trazador de líneas:

const input = 'abcdefghijlkm';

// Change `3` to the desired split length.
const output = input.split('').reduce((s, c) => {let l = s.length-1; (s[l] && s[l].length < 3) ? s[l] += c : s.push(c); return s;}, []);

console.log(output);  // output: [ 'abc', 'def', 'ghi', 'jlk', 'm' ]

Funciona al dividir la cadena en una matriz de caracteres individuales, luego se usa Array.reducepara iterar sobre cada carácter. Normalmente reducedevolvería un valor único, pero en este caso el valor único es una matriz y, a medida que pasamos por encima de cada carácter, lo agregamos al último elemento de esa matriz. Una vez que el último elemento de la matriz alcanza la longitud objetivo, agregamos un nuevo elemento de matriz.

Malvinoso
fuente
0

Llegando un poco más tarde a la discusión, pero aquí hay una variación que es un poco más rápida que la subcadena + matriz push one.

// substring + array push + end precalc
var chunks = [];

for (var i = 0, e = 3, charsLength = str.length; i < charsLength; i += 3, e += 3) {
    chunks.push(str.substring(i, e));
}

El cálculo previo del valor final como parte del ciclo for es más rápido que hacer las matemáticas en línea dentro de la subcadena. Lo probé tanto en Firefox como en Chrome y ambos muestran aceleración.

Puedes probarlo aquí

Dragonaire
fuente