Comment puis-je diviser une chaîne en segments de n caractères?


201

Comme le titre l'indique, j'ai une chaîne et je veux diviser en segments de n caractères.

Par exemple:

var str = 'abcdefghijkl';

après un peu de magie avec n=3, il deviendra

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

Y a-t-il un moyen de faire cela?

Réponses:


359

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

Remarque: utilisez {1,3}au lieu de simplement {3}pour inclure le reste pour les longueurs de chaîne qui ne sont pas un multiple de 3, par exemple:

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


Quelques subtilités supplémentaires:

  1. Si votre chaîne peut contenir des sauts de ligne ( que vous souhaitez compter comme un caractère plutôt que de fractionner la chaîne ), alors .ne les capturera pas. Utilisez /[\s\S]{1,3}/plutôt. (Merci @Mike).
  2. Si votre chaîne est vide, puis match()reviendra nulllorsque vous vous attendez à un tableau vide. Protégez-vous contre cela en ajoutant || [].

Vous pouvez donc vous retrouver avec:

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) || []);


C'est techniquement la meilleure réponse car il récupérera tout le texte d'une chaîne qui n'est pas divisible par 3 (il récupérera les 2 ou 1 derniers caractères).
Erik

6
Utilisez [\s\S]plutôt .que pour ne pas échouer sur les nouvelles lignes.
Mike Samuel

2
Vous voudrez peut-être commencer un nouveau cycle sur chaque ligne. Si vous avez vraiment des sauts de ligne, ils indiquent probablement un certain type de transition. str.match (/. {1,3} / gm) peut être un meilleur choix.
kennebec

+1 Attention: ''.match(/.{1,3}/g) et ''.match(/.{3}/g)revenez nullau lieu d'un tableau vide.
Web_Designer

4
Est-il possible d'avoir une variable à la place du chiffre 3?
Ana Claudia

46

Si vous ne vouliez pas utiliser une expression régulière ...

var chunks = [];

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

jsFiddle .

... sinon la solution regex est plutôt bonne :)


1
+1 cos Je préfère cela si la 3variable est comme suggéré par l'OP. C'est plus lisible que de concaténer une chaîne d'expression régulière.
David Tang

si seulement vous pouviez en faire une fonction utile prête à l'emploi
mmm

1
C'est plus de 10 fois plus rapide que l'option regex, donc j'irais avec cela (à l'intérieur d'une fonction) jsbench.github.io/#9cb819bf1ce429575f8535a211f72d5a
Job

1
Ma déclaration précédente s'applique à Chromium (aussi, j'étais trop tard pour éditer le commentaire précédent, donc le nouveau). Sur Firefox, il est actuellement "seulement" 30% plus rapide sur ma machine, mais c'est toujours mieux.
Job

est-ce durable sur d'énormes longueurs de ficelle?
Jacob Schneider

22
str.match(/.{3}/g); // => ['abc', 'def', 'ghi', 'jkl']

Cela fonctionne pour 3moi mais revient nullavec 250. 🤔
Jim

9

S'appuyant sur les réponses précédentes à cette question; la fonction suivante divisera une chaîne ( str) n-nombre ( size) de caractères.

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

Démo

(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>


2

Ma solution (syntaxe ES6):

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

Nous pourrions même créer une fonction avec ceci:

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

Ensuite, vous pouvez appeler la fonction facilement de manière réutilisable:

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

À votre santé


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, []);

Solution propre sans REGEX


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

La fonction ci-dessus est ce que j'utilise pour le découpage en Base64. Cela créera un saut de ligne de 75 caractères.


Pourrait aussi faire replace(/.{1,75}/g, '$&\n').
alex

1

Ici, nous entrecoupons une chaîne avec une autre chaîne tous les n caractères:

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 nous utilisons ce qui précède ainsi:

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

on a:

aag | aag | aeg | eag | e

et ici nous faisons de même, mais poussons vers un tableau:

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;

};

puis exécutez-le:

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

on a:

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

si quelqu'un connaît un moyen de simplifier le code ci-dessus, lmk, mais cela devrait fonctionner correctement pour les chaînes.


votre premier extrait ne fonctionnait pas comme prévu. J'ai modifié ici: jsfiddle.net/omarojo/ksvx2txb/261
omarojo

0

Une solution propre sans utiliser d'expressions régulières:

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

Exemple d'utilisation - https://jsfiddle.net/maciejsikora/b6xppj4q/ .

J'ai également essayé de comparer ma solution à l'expression rationnelle qui a été choisie comme bonne réponse. Un test peut être trouvé sur jsfiddle - https://jsfiddle.net/maciejsikora/2envahrk/ . Les tests montrent que les deux méthodes ont des performances similaires, peut-être qu'à première vue, la solution d'expression régulière est un peu plus rapide, mais jugez-en vous-même.


0

Avec .split:

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

et .replacesera:

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



/(?!$)/est d'arrêter avant la fin /$/, sans est:

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

ignorer le groupe /(?:... )/n'est pas nécessaire dans .replacemais .splitajoute des groupes à arr:

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

0

Voici un moyen de le faire sans expressions régulières ni boucles explicites, bien qu'il étende un peu la définition d'une ligne:

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

Il fonctionne en divisant la chaîne en un tableau de caractères individuels, puis en utilisant Array.reducepour itérer sur chaque caractère. Normalement, reduceil retournerait une seule valeur, mais dans ce cas, la seule valeur se trouve être un tableau, et lorsque nous passons sur chaque caractère, nous l'ajoutons au dernier élément de ce tableau. Une fois que le dernier élément du tableau atteint la longueur cible, nous ajoutons un nouvel élément de tableau.


0

Venant un peu plus tard à la discussion, mais voici une variation un peu plus rapide que le push sous-chaîne + tableau.

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

Le pré-calcul de la valeur finale dans le cadre de la boucle for est plus rapide que le calcul en ligne à l'intérieur de la sous-chaîne. Je l'ai testé à la fois dans Firefox et Chrome et ils montrent tous les deux une accélération.

Vous pouvez l'essayer ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.