นับจำนวนการจับคู่ของ regex ใน Javascript


101

ฉันต้องการเขียนนิพจน์ทั่วไปเพื่อนับจำนวนช่องว่าง / แท็บ / ขึ้นบรรทัดใหม่เป็นกลุ่มข้อความ ดังนั้นฉันจึงเขียนสิ่งต่อไปนี้อย่างไร้เดียงสา: -

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

ด้วยเหตุผลที่ไม่ทราบสาเหตุก็มักจะกลับ1มา ปัญหาเกี่ยวกับข้อความข้างต้นคืออะไร? ฉันได้แก้ไขปัญหาดังต่อไปนี้แล้ว: -

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

คำตอบ:


199

tl; dr: ตัวนับรูปแบบทั่วไป

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

สำหรับผู้ที่มาถึงที่นี่กำลังมองหาวิธีทั่วไปในการนับจำนวนครั้งที่เกิดขึ้นของรูปแบบ regex ในสตริงและไม่ต้องการให้มันล้มเหลวหากไม่มีการเกิดขึ้นเป็นศูนย์รหัสนี้คือสิ่งที่คุณต้องการ นี่คือการสาธิต:

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

คำตอบเดิม

ปัญหาเกี่ยวกับรหัสเริ่มต้นของคุณคือคุณไม่มีตัวระบุส่วนกลาง :

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

หากไม่มีgส่วนของ regex มันจะจับคู่เหตุการณ์แรกและหยุดที่นั่น

โปรดทราบว่า regex ของคุณจะนับช่องว่างต่อเนื่องสองครั้ง:

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

หากไม่เป็นที่ต้องการคุณสามารถทำได้:

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

5
สิ่งนี้ใช้ได้ตราบเท่าที่คุณมีช่องว่างอย่างน้อยหนึ่งช่องในข้อมูลที่คุณป้อน มิฉะนั้นการจับคู่ () จะส่งคืนค่าว่างอย่างน่ารำคาญ
sfink

3
sfink ถูกต้องคุณต้องการตรวจสอบว่า match () ส่งคืน null หรือไม่:var result = text.match(/\s/g); return result ? result.length : 0;
Gras Double

37
คุณยังสามารถป้องกันโมฆะได้โดยใช้โครงสร้างนี้:( str.match(...) || [] ).length
อา

11

ดังที่ได้กล่าวไว้ในคำตอบก่อนหน้าของฉันคุณสามารถใช้RegExp.exec()เพื่อย้ำการแข่งขันทั้งหมดและนับแต่ละครั้งที่เกิดขึ้น ประโยชน์จะถูก จำกัด ให้หน่วยความจำเพียงเพราะในทั้งมันเป็นประมาณ 20% String.match()ช้ากว่าการใช้

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

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

return count;



0

นี่เป็นสิ่งที่มีกับดักมากมายอย่างแน่นอน ฉันกำลังทำงานกับคำตอบของ Paolo Bergantino และตระหนักดีว่าแม้จะมีข้อ จำกัด อยู่บ้าง ฉันพบว่าการใช้การแสดงวันที่แบบสตริงเป็นสถานที่ที่ดีในการค้นหาปัญหาหลัก ๆ เริ่มต้นด้วยสตริงอินพุตดังนี้: '12-2-2019 5:1:48.670'

และตั้งค่าฟังก์ชันของ Paolo ดังนี้:

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

ฉันต้องการส่งผ่านนิพจน์ทั่วไปเพื่อให้ฟังก์ชันสามารถใช้ซ้ำได้มากขึ้นประการที่สองฉันต้องการให้พารามิเตอร์เป็นสตริงเพื่อที่ไคลเอนต์จะได้ไม่ต้องสร้าง regex แต่เพียงแค่จับคู่กับสตริงเช่น วิธีคลาสยูทิลิตี้สตริงมาตรฐาน

ตอนนี้คุณจะเห็นว่าฉันกำลังจัดการกับปัญหาเกี่ยวกับอินพุต มีดังต่อไปนี้:

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

ฉันกำลังสร้างความมั่นใจว่าการป้อนข้อมูลอะไรที่ไม่เหมือนตัวอักษร0, false, undefinedหรือnullไม่มีใครที่มีสตริง เนื่องจากลิเทอรัลเหล่านี้ไม่อยู่ในสตริงอินพุตจึงไม่ควรมีการจับคู่ แต่ควรจับคู่'0'ซึ่งเป็นสตริง

มีดังต่อไปนี้:

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

ฉันกำลังจัดการกับความจริงที่ว่าตัวสร้าง RegExp จะ (ฉันคิดว่าผิด) ตีความสตริง'.'เป็นตัวจับคู่อักขระทั้งหมด\.\

ในที่สุดเนื่องจากฉันใช้ตัวสร้าง RegExp ฉันจึงต้องให้'g'ค่าสถานะทั่วโลกเพื่อให้นับการจับคู่ทั้งหมดไม่ใช่แค่ตัวแรกเท่านั้นที่คล้ายกับคำแนะนำในโพสต์อื่น ๆ

ฉันตระหนักดีว่านี่เป็นคำตอบที่ช้ามาก แต่อาจเป็นประโยชน์สำหรับใครบางคนที่สะดุดตรงนี้ BTW นี่คือเวอร์ชัน 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;
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.