Contare il numero di corrispondenze di una regex in Javascript


98

Volevo scrivere una regex per contare il numero di spazi / tabulazioni / nuova riga in una porzione di testo. Quindi ho scritto ingenuamente quanto segue: -

numSpaces : function(text) { 
    return text.match(/\s/).length; 
}

Per alcune ragioni sconosciute ritorna sempre 1. Qual è il problema con l'affermazione di cui sopra? Da allora ho risolto il problema con quanto segue: -

numSpaces : function(text) { 
    return (text.split(/\s/).length -1); 
}

Risposte:


191

tl; dr: Generic Pattern Counter

// THIS IS WHAT YOU NEED
const count = (str) => {
  const re = /YOUR_PATTERN_HERE/g
  return ((str || '').match(re) || []).length
}

Per coloro che sono arrivati ​​qui alla ricerca di un modo generico per contare il numero di occorrenze di un pattern regex in una stringa e non vogliono che fallisca se ci sono zero occorrenze, questo codice è ciò di cui hai bisogno. Ecco una dimostrazione:

/*
 *  Example
 */

const count = (str) => {
  const re = /[a-z]{3}/g
  return ((str || '').match(re) || []).length
}

const str1 = 'abc, def, ghi'
const str2 = 'ABC, DEF, GHI'

console.log(`'${str1}' has ${count(str1)} occurrences of pattern '/[a-z]{3}/g'`)
console.log(`'${str2}' has ${count(str2)} occurrences of pattern '/[a-z]{3}/g'`)

Risposta originale

Il problema con il tuo codice iniziale è che ti manca l' identificatore globale :

>>> 'hi there how are you'.match(/\s/g).length;
4

Senza la gparte della regex, corrisponderà solo alla prima occorrenza e si fermerà lì.

Nota inoltre che la tua regex conterà due volte gli spazi successivi:

>>> 'hi  there'.match(/\s/g).length;
2

Se ciò non è desiderabile, potresti farlo:

>>> 'hi  there'.match(/\s+/g).length;
1

5
Funziona finché hai almeno uno spazio nel tuo input. Altrimenti, match () restituisce fastidiosamente null.
sfink

3
sfink ha ragione, vuoi assolutamente controllare se match () ha restituito null:var result = text.match(/\s/g); return result ? result.length : 0;
Gras Double

37
È inoltre possibile proteggere contro il nulla utilizzando questa costruzione:( str.match(...) || [] ).length
a'r

11

Come accennato nella mia risposta precedente , puoi usare RegExp.exec()per iterare su tutte le partite e contare ogni occorrenza; il vantaggio è limitato alla sola memoria, perché nel complesso è circa il 20% più lento rispetto all'utilizzo String.match().

var re = /\s/g,
count = 0;

while (re.exec(text) !== null) {
    ++count;
}

return count;


2

('my string'.match(/\s/g) || []).length;


1
Penso che tu abbia messo || []nel posto sbagliato, dovrebbe essere('my string'.match(/\s/g) || []).length
woojoo666

0

Questo è certamente qualcosa che ha molte trappole. Stavo lavorando con la risposta di Paolo Bergantino e mi sono reso conto che anche quella ha dei limiti. Ho trovato che lavorare con le rappresentazioni di stringa delle date è un buon posto per trovare rapidamente alcuni dei problemi principali. Inizia con una stringa di input come questa: '12-2-2019 5:1:48.670'

e impostare la funzione di Paolo in questo modo:

function count(re, str) {
    if (typeof re !== "string") {
        return 0;
    }
    re = (re === '.') ? ('\\' + re) : re;
    var cre = new RegExp(re, 'g');
    return ((str || '').match(cre) || []).length;
}

Volevo che fosse passata l'espressione regolare, in modo che la funzione fosse più riutilizzabile, in secondo luogo, volevo che il parametro fosse una stringa, in modo che il client non dovesse creare la regex, ma semplicemente corrispondere sulla stringa, come un metodo di classe di utilità stringa standard.

Ora, qui puoi vedere che ho a che fare con problemi con l'input. Con quanto segue:

if (typeof re !== "string") {
    return 0;
}

Sto assicurando che l'ingresso non è nulla di simile letterale 0, false, undefined, o null, nessuno dei quali sono stringhe. Poiché questi letterali non sono nella stringa di input, non dovrebbero esserci corrispondenze, ma dovrebbe corrispondere '0', che è una stringa.

Con quanto segue:

re = (re === '.') ? ('\\' + re) : re;

Ho a che fare con il fatto che il costruttore RegExp interpreterà (credo, a torto) la stringa '.'come il combinatore di tutti i caratteri\.\

Infine, poiché sto utilizzando il costruttore RegExp, devo assegnargli il 'g'flag globale in modo che conti tutte le corrispondenze, non solo la prima, in modo simile ai suggerimenti in altri post.

Mi rendo conto che questa è una risposta estremamente tardiva, ma potrebbe essere utile a qualcuno che inciampa qui. A proposito, ecco la versione TypeScript:

function count(re: string, str: string): number {
    if (typeof re !== 'string') {
        return 0;
    }
    re = (re === '.') ? ('\\' + re) : re;
    const cre = new RegExp(re, 'g');    
    return ((str || '').match(cre) || []).length;
}

-2

che ne dici di questo

function isint(str){
    if(str.match(/\d/g).length==str.length){
        return true;
    }
    else {
         return false
    }
}
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.