Hitung jumlah pencocokan ekspresi reguler dalam Javascript

98

Saya ingin menulis regex untuk menghitung jumlah spasi / tab / baris baru dalam sebuah potongan teks. Jadi saya dengan naif menulis yang berikut: -

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

Untuk beberapa alasan yang tidak diketahui itu selalu kembali 1. Apa masalahnya dengan pernyataan di atas? Saya telah memecahkan masalah dengan yang berikut: -

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

Jawaban:

192

tl; dr: Penghitung Pola Umum

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

Bagi mereka yang tiba di sini mencari cara umum untuk menghitung jumlah kemunculan pola regex dalam sebuah string, dan tidak ingin gagal jika tidak ada kemunculan nol, kode inilah yang Anda butuhkan. Berikut peragaannya:

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

Jawaban Asli

Masalah dengan kode awal Anda adalah Anda kehilangan pengenal global :

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

Tanpa gbagian dari regex, ini hanya akan cocok dengan kemunculan pertama dan berhenti di situ.

Perhatikan juga bahwa regex Anda akan menghitung spasi berturut-turut dua kali:

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

Jika itu tidak diinginkan, Anda dapat melakukan ini:

>>> 'hi  there'.match(/\s+/g).length;
1
Paolo Bergantino
sumber
5
Ini berfungsi selama Anda memiliki setidaknya satu ruang dalam masukan Anda. Jika tidak, match () mengembalikan null secara mengganggu.
sfink
3
sfink benar, Anda pasti ingin memeriksa apakah match () mengembalikan null:var result = text.match(/\s/g); return result ? result.length : 0;
Gras Double
37
Anda juga dapat melindungi terhadap null dengan menggunakan konstruksi ini:( str.match(...) || [] ).length
a'r
11

Seperti yang disebutkan dalam jawaban saya sebelumnya , Anda dapat menggunakan RegExp.exec()untuk mengulang semua kecocokan dan menghitung setiap kemunculan; keuntungannya terbatas pada memori saja, karena secara keseluruhan ini sekitar 20% lebih lambat daripada penggunaan String.match().

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

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

return count;
Mendongkrak
sumber
2

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

Weston Ganger
sumber
1
Saya pikir Anda menempatkan || []di tempat yang salah, seharusnya('my string'.match(/\s/g) || []).length
woojoo666
0

Ini tentunya sesuatu yang memiliki banyak jebakan. Saya bekerja dengan jawaban Paolo Bergantino, dan menyadari bahwa itu pun memiliki beberapa keterbatasan. Saya menemukan bekerja dengan representasi string dari tanggal sebagai tempat yang baik untuk segera menemukan beberapa masalah utama. Mulailah dengan string input seperti ini: '12-2-2019 5:1:48.670'

dan mengatur fungsi Paolo seperti ini:

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

Saya ingin ekspresi reguler diteruskan, sehingga fungsinya lebih dapat digunakan kembali, kedua, saya ingin parameter menjadi string, sehingga klien tidak harus membuat regex, tetapi cukup mencocokkan string, seperti metode kelas utilitas string standar.

Sekarang, di sini Anda dapat melihat bahwa saya berurusan dengan masalah dengan input. Dengan berikut ini:

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

Saya memastikan bahwa input tidak apa-apa seperti literal 0, false, undefined, atau null, tidak ada yang string. Karena literal ini tidak ada dalam string masukan, tidak boleh ada kecocokan, tetapi harus cocok '0', yaitu string.

Dengan berikut ini:

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

Saya berurusan dengan fakta bahwa konstruktor RegExp akan (menurut saya, salah) menafsirkan string '.'sebagai semua pencocokan karakter\.\

Akhirnya, karena saya menggunakan konstruktor RegExp, saya perlu memberinya 'g'bendera global sehingga menghitung semua kecocokan, bukan hanya yang pertama, mirip dengan saran di posting lain.

Saya menyadari bahwa ini adalah jawaban yang sangat terlambat, tetapi mungkin bermanfaat bagi seseorang yang tersandung di sini. BTW inilah versi 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;
}
Michael Coxon
sumber
-2

bagaimana kalau seperti ini

function isint(str){
    if(str.match(/\d/g).length==str.length){
        return true;
    }
    else {
         return false
    }
}
anders
sumber