在Javascript中统计字符串中字符出现的次数

785
我需要统计一个字符串中某个字符出现的次数。
例如,假设我的���符串包含:
var mainStr = "str1,str2,str3,str4";

我想要找到逗号 , 字符的数量,它是3。以及在逗号分割后的单个字符串的数量,这是4。

我还需要验证每个字符串即str1或str2或str3或str4都不应超过15个字符。


根据下面排名最高的答案,你也可以使用这个在线工具来交叉检查结果:https://magictools.dev/#!/tools/character-occurences - WJA
39个回答

6

我发现在一个非常长的字符串中(例如1,000,000个字符),搜索一个字符的最佳方法是使用replace()方法。

window.count_replace = function (str, schar) {
    return str.length - str.replace(RegExp(schar), '').length;
};

您可以查看另一个JSPerf套件,测试此方法以及在字符串中查找字符的其他方法。


很明显,如果你的代码以某种方式每秒迭代100万个字符500000次,那么我的CPU运行速度至少为100GHz(假设没有SIMD;即使有也至少为40 GHz)。因此,我不认为这个基准测试是正确的。 - the default.

6

更新 2022年6月10日

经过多次性能测试,如果您的用例允许,使用split似乎是整体表现最佳的选择。


function countChar(char: string, string: string): number  {

  return string.split(char).length - 1

}

countChar('x', 'foo x bar x baz x')


我知道我来晚了,但是我很惊讶社区中没有人回答这个最基本的方法。对于这个问题,社区提供的大部分答案都是基于迭代的,但是所有答案都按字符为基础移动字符串,这并不是真正高效的方法。

当处理包含数千个字符的大字符串时,逐个遍历每个字符以获取出现次数可能变得相当冗余,更不用说是一种代码异味。下面的解决方案利用了 sliceindexOf 和可信的传统 while 循环。这些方法避免了我们必须逐个遍历每个字符,并将大大加快计算出现次数所需的时间。这些方法遵循与需要进行字符串遍历的解析器和词法分析器类似的逻辑。

使用 Slice

在此方法中,我们利用了 slice,并在每次 indexOf 匹配时通过消除之前搜索过的部分来遍历整个字符串。每次调用 indexOf 时,它搜索的字符串大小都会变小。

function countChar (char: string, search: string): number {
  
  let num: number = 0;
  let str: string = search;
  let pos: number = str.indexOf(char);
  
  while(pos > -1) {
    str = str.slice(pos + 1);
    pos = str.indexOf(char);
    num++;
  }

  return num;

}

// Call the function
countChar('x', 'foo x bar x baz x') // 3

使用指定起始位置的indexOf方法

与使用slice方法的第一种方法类似,但不是扩展字符串进行搜索,而是利用indexOf方法中的from参数。

function countChar (char: string, str: string): number {
  
  let num: number = 0;
  let pos: number = str.indexOf(char);
  
  while(pos > -1) {
    pos = str.indexOf(char, pos + 1);
    num++;
  }

  return num;

}

// Call the function
countChar('x', 'foo x bar x baz x') // 3

就我个人而言,我更倾向于第二种方法而不是第一种方法,但在处理大字符串和较小的字符串时,两种方法都是可以的并且性能良好。


5

Split与RegExp的性能比较

var i = 0;

var split_start = new Date().getTime();
while (i < 30000) {
  "1234,453,123,324".split(",").length -1;
  i++;
}
var split_end = new Date().getTime();
var split_time = split_end - split_start;


i= 0;
var reg_start = new Date().getTime();
while (i < 30000) {
  ("1234,453,123,324".match(/,/g) || []).length;
  i++;
}
var reg_end = new Date().getTime();
var reg_time = reg_end - reg_start;

alert ('Split Execution time: ' + split_time + "\n" + 'RegExp Execution time: ' + reg_time + "\n");


4

我对已接受的答案进行了轻微改进,它允许使用区分大小写/不区分大小写匹配,并且是附加到字符串对象的方法:

String.prototype.count = function(lit, cis) {
    var m = this.toString().match(new RegExp(lit, ((cis) ? "gi" : "g")));
    return (m != null) ? m.length : 0;
}

lit是要搜索的字符串(比如' ex '),cis是大小写不敏感,默认为false,它将允许选择大小写不敏感的匹配。


要在字符串'I love StackOverflow.com'中搜索小写字母'o',您需要使用:

var amount_of_os = 'I love StackOverflow.com'.count('o');

amount_of_os 将等于 2


如果我们再次使用不区分大小写的匹配搜索相同的字符串,您将使用:

var amount_of_os = 'I love StackOverflow.com'.count('o', true);

这一次,amount_of_os将等于3,因为字符串中的大写字母O也被包括在搜索中。

4
我刚刚在Node v7.4上使用repl.it进行了一个非常简单粗暴的测试。对于单个字符,标准for循环是最快的:
一些代码:
// winner!
function charCount1(s, c) {
    let count = 0;
    c = c.charAt(0); // we save some time here
    for(let i = 0; i < s.length; ++i) {
        if(c === s.charAt(i)) {
            ++count;
        }
    }
    return count;
}

function charCount2(s, c) {
    return (s.match(new RegExp(c[0], 'g')) || []).length;
}

function charCount3(s, c) {
    let count = 0;
    for(ch of s) {
        if(c === ch) {
            ++count;
        }
    }
    return count;
}

function perfIt() {
    const s = 'Hello, World!';
    const c = 'o';

    console.time('charCount1');
    for(let i = 0; i < 10000; i++) {
        charCount1(s, c);
    }
    console.timeEnd('charCount1');
    
    console.time('charCount2');
    for(let i = 0; i < 10000; i++) {
        charCount2(s, c);
    }
    console.timeEnd('charCount2');
    
    console.time('charCount3');
    for(let i = 0; i < 10000; i++) {
        charCount2(s, c);
    }
    console.timeEnd('charCount3');
}

一些运行结果:

perfIt()
charCount1: 3.301ms
charCount2: 11.652ms
charCount3: 174.043ms
undefined

perfIt()
charCount1: 2.110ms
charCount2: 11.931ms
charCount3: 177.743ms
undefined

perfIt()
charCount1: 2.074ms
charCount2: 11.738ms
charCount3: 152.611ms
undefined

perfIt()
charCount1: 2.076ms
charCount2: 11.685ms
charCount3: 154.757ms
undefined

更新于2021年2月10日:在repl.it演示中修正了拼写错误。
更新于2020年10月24日:Node.js 12仍然存在此问题(在此处尝试自己的操作)

不错的工作@NuSkooler!有几个注意事项:尝试使用更长的字符串(例如查找'\n'-s流行歌曲),并尝试在不同的s上运行所有计数。说s+i,c。另外,您有一个拼写错误:charCount3被误写为charCount2,因此结果相似。 如果您尝试使用我的修改,您会发现split是获胜者,而正则表达式匹配排名第二。 - Barney Szabolcs
1
感谢@BarneySzabolcs!更新了repl演示并重新运行了结果。我确实保留了相同的字符串--正如你所说,YMMV。我相信一些在低级语言中起作用的技巧在这里也可以适用,特别是对于更长的字符串。 - NuSkooler

4
s = 'dir/dir/dir/dir/'
for(i=l=0;i<s.length;i++)
if(s[i] == '/')
l++

4

这是我的解决方案。之前已经有很多解决方案发布了,但我愿意在这里分享我的观点。

const mainStr = 'str1,str2,str3,str4';

const commaAndStringCounter = (str) => {
  const commas = [...str].filter(letter => letter === ',').length;
  const numOfStr = str.split(',').length;

  return `Commas: ${commas}, String: ${numOfStr}`;
}

// Run the code
console.log(commaAndStringCounter(mainStr)); // Output: Commas: 3, String: 4

在这里查看我的 REPL


4

Easiest way i found out...

Example-

str = 'mississippi';

function find_occurences(str, char_to_count){
    return str.split(char_to_count).length - 1;
}

find_occurences(str, 'i') //outputs 4

concise! Thanks! - LeOn - Han Li

3

我正在开发一个需要子串计数器的小项目。搜索错误的短语没有给我提供任何结果,但在编写自己的实现后,我遇到了这个问题。无论如何,以下是我的方法,它可能比大多数方法慢,但对某些人可能有帮助:

function count_letters() {
var counter = 0;

for (var i = 0; i < input.length; i++) {
    var index_of_sub = input.indexOf(input_letter, i);

    if (index_of_sub > -1) {
        counter++;
        i = index_of_sub;
    }
}

http://jsfiddle.net/5ZzHt/1/

如果您发现此实现失败或不符合某些标准,请告知我!:)

更新 您可能需要替换:

    for (var i = 0; i < input.length; i++) {

随着:

for (var i = 0, input_length = input.length; i < input_length; i++) {

有趣的阅读,讨论了上述问题:http://www.erichynds.com/blog/javascript-length-property-is-a-stored-value


1
是的,它可以用于子字符串,而不仅仅是子字符。但是,您需要向函数添加参数 :) - Nico

2
function len(text,char){

return text.innerText.split(string).length
}

console.log(len("str1,str2,str3,str4",","))

这是一个非常简短的函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接