Come posso dividere una stringa in segmenti di n caratteri?


201

Come dice il titolo, ho una stringa e voglio dividere in segmenti di n caratteri.

Per esempio:

var str = 'abcdefghijkl';

dopo un po 'di magia n=3, diventerà

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

C'è un modo per fare questo?

Risposte:


359

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

Nota: utilizzare {1,3}invece di {3}includere solo il resto per lunghezze di stringa che non sono un multiplo di 3, ad esempio:

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


Un altro paio di sottigliezze:

  1. Se la tua stringa può contenere newline ( che vuoi contare come carattere anziché dividere la stringa ), allora .non li catturerà. Usa /[\s\S]{1,3}/invece. (Grazie @Mike).
  2. Se la stringa è vuota, match()verrà restituita nullquando ci si potrebbe aspettare un array vuoto. Proteggiti da questo aggiungendo || [].

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


Questa è tecnicamente la risposta migliore in quanto prenderà tutto il testo da una stringa che non è uniformemente divisibile per 3 (prenderà gli ultimi 2 o 1 caratteri).
Erik,

6
Utilizzare [\s\S]invece di .per non fallire su newline.
Mike Samuel,

2
Potresti voler iniziare un nuovo ciclo su ogni riga. Se hai davvero delle newline, probabilmente indicano un tipo di transizione. str.match (/. {1,3} / gm) potrebbe essere una scelta migliore.
Kennebec,

+1 Attento: ''.match(/.{1,3}/g) e ''.match(/.{3}/g)restituisce nullinvece di un array vuoto.
Web_Designer,

4
È possibile avere una variabile al posto del numero 3?
Ana Claudia,

46

Se non volevi usare un'espressione regolare ...

var chunks = [];

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

jsFiddle .

... altrimenti la soluzione regex è abbastanza buona :)


1
+1 perché lo preferirei se 3è variabile come suggerito dall'OP. È più leggibile che concatenare una stringa regexp.
David Tang,

se solo potessi avvolgerlo in un'utile funzione pronta per essere utilizzata
mmm,

1
Questo è più di 10 volte più veloce dell'opzione regex, quindi andrei con questo (all'interno di una funzione) jsbench.github.io/#9cb819bf1ce429575f8535a211f72d5a
Giobbe

1
La mia precedente affermazione si applica a Chromium (inoltre, ero in ritardo con la modifica del commento precedente, quindi di quello nuovo). Su Firefox è attualmente "solo" il 30% più veloce sulla mia macchina, ma è comunque costantemente migliore.
Giobbe

è sostenibile su lunghezze di filo enormi?
Jacob Schneider,

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

Questo funziona per 3me ma torna nullcon 250. 🤔
Jim

9

Basandosi sulle risposte precedenti a questa domanda; la seguente funzione dividerà una stringa ( str) n-number ( size) di caratteri.

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

dimostrazione

(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

La mia soluzione (sintassi ES6):

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

Potremmo persino creare una funzione con questo:

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

Quindi puoi chiamare facilmente la funzione in modo riutilizzabile:

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

Saluti


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

Soluzione pulita senza REGEX


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

La funzione sopra è quella che uso per il chunking Base64. Creerà un'interruzione di riga ogni 75 caratteri.


Potrebbe anche fare replace(/.{1,75}/g, '$&\n').
alex,

1

Qui intervalliamo una stringa con un'altra stringa ogni n caratteri:

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;

};

se utilizziamo quanto sopra in questo modo:

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

noi abbiamo:

AAG | AAG | AEG | eag | e

e qui facciamo lo stesso, ma spingiamo verso un array:

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;

};

e quindi eseguirlo:

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

noi abbiamo:

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

se qualcuno conosce un modo per semplificare il codice sopra, lmk, ma dovrebbe funzionare bene per le stringhe.


il tuo primo frammento non funzionava come previsto. Ho modificato qui: jsfiddle.net/omarojo/ksvx2txb/261
omarojo

0

Qualche soluzione pulita senza usare espressioni regolari:

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

Esempio di utilizzo - https://jsfiddle.net/maciejsikora/b6xppj4q/ .

Ho anche provato a confrontare la mia soluzione per regexp quella che è stata scelta come risposta giusta. Alcuni test sono disponibili su jsfiddle - https://jsfiddle.net/maciejsikora/2envahrk/ . I test stanno dimostrando che entrambi i metodi hanno prestazioni simili, forse a prima vista la soluzione regexp è un po 'più veloce, ma giudica te stesso.


0

Con .split:

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

e .replacesarà:

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



/(?!$)/è fermarsi prima della fine /$/, senza è:

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

ignorare il gruppo /(?:... )/non è necessario .replacema in .splitsta aggiungendo gruppi ad arr:

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

0

Ecco un modo per farlo senza espressioni regolari o loop espliciti, anche se sta allungando un po 'la definizione di una riga:

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

Funziona dividendo la stringa in una matrice di singoli caratteri, quindi usando Array.reduce per scorrere su ogni carattere. Normalmente reducerestituirebbe un singolo valore, ma in questo caso il singolo valore sembra essere un array, e mentre passiamo sopra ogni carattere lo aggiungiamo all'ultimo elemento in quell'array. Quando l'ultimo elemento dell'array raggiunge la lunghezza target, aggiungiamo un nuovo elemento dell'array.


0

Venendo un po 'più tardi alla discussione, ma qui una variazione che è un po' più veloce rispetto alla sottostringa + array 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));
}

Il pre-calcolo del valore finale come parte del ciclo for è più veloce rispetto alla matematica inline all'interno della sottostringa. L'ho provato sia in Firefox che in Chrome ed entrambi mostrano una maggiore velocità.

Puoi provarlo qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.