Policz liczbę dopasowań wyrażenia regularnego w JavaScript


100

Chciałem napisać wyrażenie regularne, aby policzyć liczbę spacji / tabulatorów / nowej linii w kawałku tekstu. Więc naiwnie napisałem: -

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

Z nieznanych powodów zawsze wraca 1. Jaki jest problem z powyższym stwierdzeniem? Od tego czasu rozwiązałem problem, wykonując następujące czynności: -

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

Odpowiedzi:


196

tl; dr: Ogólny licznik wzorców

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

Dla tych, którzy przybyli tutaj i szukają ogólnego sposobu zliczania liczby wystąpień wzorca wyrażenia regularnego w ciągu znaków i nie chcą, aby to się nie powiodło, jeśli wystąpi zero wystąpień, ten kod jest tym, czego potrzebujesz. Oto demonstracja:

/*
 *  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'`)

Oryginalna odpowiedź

Problem z kodem początkowym polega na tym, że brakuje identyfikatora globalnego :

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

Bez gczęści wyrażenia regularnego dopasuje tylko pierwsze wystąpienie i na tym zakończy.

Pamiętaj też, że Twoje wyrażenie regularne będzie liczyć kolejne spacje dwukrotnie:

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

Jeśli to nie jest pożądane, możesz zrobić to:

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

5
Działa to tak długo, jak masz co najmniej jedną spację na wejściu. W przeciwnym razie match () irytująco zwraca null.
sfink

3
sfink ma rację, na pewno chcesz sprawdzić, czy match () zwróciło null:var result = text.match(/\s/g); return result ? result.length : 0;
Gras Double

37
Możesz również zabezpieczyć się przed zerową, używając tej konstrukcji:( str.match(...) || [] ).length
a'r 3.11

11

Jak wspomniałem w mojej wcześniejszej odpowiedzi , możesz użyć RegExp.exec()do iteracji po wszystkich dopasowaniach i zliczenia każdego wystąpienia; Zaleta ogranicza się tylko do pamięci, ponieważ w sumie jest około 20% wolniejsza niż przy użyciu String.match().

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

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

return count;


2

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


1
Myślę, że umieściłeś || []w złym miejscu, powinno być('my string'.match(/\s/g) || []).length
woojoo666

0

Jest to z pewnością coś, co ma wiele pułapek. Pracowałem z odpowiedzią Paolo Bergantino i zdałem sobie sprawę, że nawet to ma pewne ograniczenia. Zauważyłem, że praca z reprezentacjami dat w postaci ciągów znaków to dobre miejsce na szybkie znalezienie niektórych głównych problemów. Zacznij od ciągu wejściowego, takiego jak ten: '12-2-2019 5:1:48.670'

i skonfiguruj funkcję Paolo w ten sposób:

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

Chciałem, aby wyrażenie regularne zostało przekazane, aby funkcja była bardziej wielokrotnego użytku, po drugie, chciałem, aby parametr był ciągiem, aby klient nie musiał tworzyć wyrażenia regularnego, ale po prostu dopasować do ciągu, na przykład standardowa metoda klasy narzędziowej typu string.

Teraz widać, że mam do czynienia z problemami z danymi wejściowymi. Z następującymi:

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

Mam zapewnienie, że wejście nie jest coś jak dosłowne 0, false, undefined, lub null, z których żaden nie są łańcuchami. Ponieważ tych literałów nie ma w ciągu wejściowym, nie powinno być żadnych dopasowań, ale powinny one być zgodne'0' , co jest ciągiem.

Z następującymi:

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

Mam do czynienia z faktem, że konstruktor RegExp zinterpretuje (myślę, że błędnie) ciąg '.'jako dopasowujący wszystkie znaki\.\

Wreszcie, ponieważ używam konstruktora RegExp, muszę nadać mu 'g'flagę globalną, aby zliczał wszystkie dopasowania, a nie tylko pierwsze, podobnie jak sugestie w innych postach.

Zdaję sobie sprawę, że to bardzo późna odpowiedź, ale może być pomocna dla kogoś, kto się tu potyka. BTW, oto wersja 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

co powiesz na to

function isint(str){
    if(str.match(/\d/g).length==str.length){
        return true;
    }
    else {
         return false
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.