Подсчитать количество совпадений регулярного выражения в Javascript

98

Я хотел написать регулярное выражение для подсчета количества пробелов / табуляции / новой строки в фрагменте текста. Так что я наивно написал следующее:

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

По неизвестным причинам он всегда возвращается 1. В чем проблема с приведенным выше утверждением? С тех пор я решил проблему со следующим: -

numSpaces : function(text) { 
    return (text.split(/\s/).length -1); 
}
вай
источник

Ответы:

192

tl; dr: Общий счетчик шаблонов

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

Для тех, кто пришел сюда в поисках универсального способа подсчета количества вхождений шаблона регулярного выражения в строку и не хочет, чтобы он терпел неудачу, если вхождений нет, этот код - то, что вам нужно. Вот демонстрация:

/*
 *  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части регулярного выражения он будет соответствовать только первому вхождению и остановится на нем.

Также обратите внимание, что ваше регулярное выражение будет считать последовательные пробелы дважды:

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

Если это нежелательно, вы можете сделать это:

>>> 'hi  there'.match(/\s+/g).length;
1
Паоло Бергантино
источник
5
Это работает, если у вас есть хотя бы один пробел в вашем вводе. В противном случае match () досадно возвращает null.
sfink
3
sfink прав, вы определенно хотите проверить, вернул ли match () null:var result = text.match(/\s/g); return result ? result.length : 0;
Gras Double
37
Вы также можете защитить себя от нуля, используя эту конструкцию:( str.match(...) || [] ).length
a'r
11

Как упоминалось в моем предыдущем ответе , вы можете использовать RegExp.exec()для перебора всех совпадений и подсчета каждого вхождения; Преимущество ограничивается только памятью, потому что в целом она примерно на 20% медленнее, чем при использовании String.match().

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

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

return count;
Разъем
источник
4
(('a a a').match(/b/g) || []).length; // 0
(('a a a').match(/a/g) || []).length; // 3

На основе https://stackoverflow.com/a/48195124/16777, но исправлено, чтобы фактически работать в случае нулевых результатов.

Кев
источник
2

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

Уэстон Гэнджер
источник
1
Я думаю, вы || []не туда поместили , так и должно быть('my string'.match(/\s/g) || []).length
woojoo666 09
0

В этом, безусловно, есть множество ловушек. Я работал с ответом Паоло Бергантино и понимал, что даже у него есть некоторые ограничения. Я нашел, что работа со строковыми представлениями дат - хорошее место, чтобы быстро найти некоторые из основных проблем. Начните с такой строки ввода: '12-2-2019 5:1:48.670'

и настройте функцию Паоло следующим образом:

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

Я хотел, чтобы регулярное выражение было передано, чтобы функция была более многоразовой, во-вторых, я хотел, чтобы параметр был строкой, чтобы клиенту не приходилось создавать регулярное выражение, а просто совпадать со строкой, например стандартный метод служебного класса строки.

Теперь вы можете видеть, что я имею дело с проблемами с вводом. Со следующим:

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

Я обеспечение , что входной нет ничего подобного в буквальном смысле 0, false, undefinedили null, ни один из которых являются строками. Поскольку этих литералов нет во входной строке, совпадений быть не должно, но они должны совпадать '0', то есть строка.

Со следующим:

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

Я имею дело с тем, что конструктор RegExp (я думаю, ошибочно) интерпретирует строку '.'как сопоставление всех символов\.\

Наконец, поскольку я использую конструктор RegExp, мне нужно присвоить ему глобальный 'g'флаг, чтобы он учитывал все совпадения, а не только первое, как и в других сообщениях.

Я понимаю, что это очень поздний ответ, но он может быть полезен тем, кто здесь спотыкается. Кстати, вот версия 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

как насчет этого

function isint(str){
    if(str.match(/\d/g).length==str.length){
        return true;
    }
    else {
         return false
    }
}
Андерс
источник