计算Javascript中字符串中字符的出现次数

Posted

技术标签:

【中文标题】计算Javascript中字符串中字符的出现次数【英文标题】:Count the number of occurrences of a character in a string in Javascript 【发布时间】:2010-10-27 05:39:01 【问题描述】:

我需要统计一个字符在字符串中出现的次数。

例如,假设我的字符串包含:

var mainStr = "str1,str2,str3,str4";

我想找到逗号,字符的数量,即3。以及沿逗号拆分后的单个字符串的数量,即4。

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

【问题讨论】:

根据以下评分最高的答案,您也可以使用此在线工具交叉检查结果:magictools.dev/#!/tools/character-occurences 【参考方案1】:

我已经更新了这个答案。我更喜欢使用匹配的想法,但速度较慢:

console.log(("str1,str2,str3,str4".match(/,/g) || []).length); //logs 3

console.log(("str1,str2,str3,str4".match(new RegExp("str", "g")) || []).length); //logs 4

如果您事先知道要搜索的内容,请使用正则表达式文字,否则您可以使用 RegExp 构造函数,并将 g 标志作为参数传入。

match 返回null 没有结果,因此|| []

我在 2009 年做出的原始答案如下。它不必要地创建了一个数组,但使用拆分更快(截至 2014 年 9 月)。我很矛盾,如果我真的需要速度,毫无疑问我会使用拆分,但我更喜欢使用匹配。

旧答案(从 2009 年开始):

如果您正在寻找逗号:

(mainStr.split(",").length - 1) //3

如果您正在寻找 str

(mainStr.split("str").length - 1) //4

@Lo 的回答和我自己的愚蠢 performance test split 都在速度上领先,至少在 Chrome 中是这样,但再次创建额外的数组似乎并不明智。

【讨论】:

测试显示 Firefox 在拆分时比任何其他浏览器都快很多。 jsperf.com/count-the-number-of-occurances-in-string 呃,我刚刚测试了 vsync 的 jsperf,并且正则表达式在 Chrome、Firefox 和 IE 中较慢。分别为 68%、100% 和 14%。我有一个 i7 2600。 我真的不喜欢使用正则表达式的想法,因为“你更喜欢它”。正则表达式有其用途,但通常当有一个简单的非正则表达式解决方案时,它是一个更好的选择。另请注意,这两种方法都会创建一个数组,因此这也不是使用正则表达式的理由。 我更喜欢它在这种情况下是有原因的。将字符串拆分为数组以获得多次出现是一种获取该信息的方法。拆分数组只是因为实现细节而更快,一些可以改变的东西,而获得匹配的数量可以提高可读性,意图很明显,不会创建和填充未使用的数据结构。 split() 是 javascript 中的一个基本工具,概念上很简单,计算拆分的目的很明确,并且完全可读。【参考方案2】:

至少有五种方法。最好的选项,也应该是最快的(由于本机 RegEx 引擎)放在顶部。

方法一

("this is foo bar".match(/o/g)||[]).length;
// returns 2

方法二

"this is foo bar".split("o").length - 1;
// returns 2

不推荐拆分,因为它需要大量资源。它为每个匹配分配新的 'Array' 实例。不要尝试通过 FileReader 获取 >100MB 的文件。您可以使用 Chrome 的分析器 选项观察确切的资源使用情况。

方法3

    var stringsearch = "o"
       ,str = "this is foo bar";
    for(var count=-1,index=-2; index != -1; count++,index=str.indexOf(stringsearch,index+1) );
// returns 2

方法四

搜索单个字符

    var stringsearch = "o"
       ,str = "this is foo bar";
    for(var i=count=0; i<str.length; count+=+(stringsearch===str[i++]));
     // returns 2

方法五

元素映射和过滤。不建议这样做,因为它的整体资源预分配而不是使用 Pythonian 'generators':

    var str = "this is foo bar"
    str.split('').map( function(e,i) if(e === 'o') return i; )
                 .filter(Boolean)
    //>[9, 10]
    [9, 10].length
    // returns 2

分享: 我做了这个gist,目前有 8 种字符计数方法,所以我们可以直接汇集和分享我们的想法 - 只是为了好玩,也许还有一些有趣的基准:)

【讨论】:

我花了一点时间才意识到||[] 在做什么,但这个答案很棒!对于其他摸不着头脑的人,如果没有找到匹配项,match() 将返回 null,如果 match() 返回 null||[] 将返回长度为 0 的数组,这意味着 length() 将返回 0 而不是生成输入错误。 Nathan,为我辩护,在编写上面的代码之前,我确实详细说明了这一点:gist.github.com/2757164。我想避免发布小代码片段的博客文章,但是这将允许您通过 google-search 即时访问。作为 sn-p 存储库的 Gist 索引非常稀疏,不太理想。 PS:我也讨厌不明确的句法特质。 Lo Sauer,无需为自己辩护,代码是可靠的,我通过弄清楚它是如何工作的,我自己学到了一些东西 :) 我更喜欢这种方法,而不是实际标记为答案的方法。如果我们不打算使用结果,则不需要拆分字符串。 您的第三种方法(不幸的是,也是最快的)将错过大海捞针中索引 0 处的任何匹配。您可以改用 do...while 循环来修复它: var strsearch = "o", str = "othis is foo bar", index = -1, count = -1;做 index = str.indexOf(strsearch, index+1);计数++; 而(索引!= -1);计数 设置开始index = -2就足够了,但非常感谢@Augustus【参考方案3】:

简单地说,使用 split 找出一个字符在字符串中出现的次数。

mainStr.split(',').length // 给出 4 是使用分隔符逗号分割后的字符串数mainStr.split(',').length - 1 // 给出 3 是逗号的个数

【讨论】:

【参考方案4】:

将此函数添加到 sting 原型中:

String.prototype.count=function(c)  
  var result = 0, i = 0;
  for(i;i<this.length;i++)if(this[i]==c)result++;
  return result;
;

用法:

console.log("strings".count("s")); //2

【讨论】:

"stringsstringstrings".count("str") 呢? @Toskan 请看 OPs 的问题,它是关于“一个字符”的。我想这就是为什么参数被称为“c”而不是“s”的原因 - 用于“[c]haracter”,而不是“[s]tring”。大多数其他答案都是可怕的,人们为这个极其简单的任务抛出数组和额外的对象,就好像内存分配是免费的一样。【参考方案5】:

快速搜索Google 得到了这个(来自http://www.codecodex.com/wiki/index.php?title=Count_the_number_of_occurrences_of_a_specific_character_in_a_string#JavaScript)

String.prototype.count=function(s1)  
    return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;

像这样使用它:

test = 'one,two,three,four'
commas = test.count(',') // returns 3

【讨论】:

* char (SyntaxError: nothing to repeat) 上的错误 参数必须是正则表达式。所以如果要统计的,需要传入'[*]'【参考方案6】:

您也可以rest 您的字符串并像使用元素数组一样使用它

Array.prototype.filter()

const mainStr = 'str1,str2,str3,str4';
const commas = [...mainStr].filter(l => l === ',').length;

console.log(commas);

或者

Array.prototype.reduce()

const mainStr = 'str1,str2,str3,str4';
const commas = [...mainStr].reduce((a, c) => c === ',' ? ++a : a, 0);

console.log(commas);

【讨论】:

这是尝试用尽可能多的(实际)努力来解决一个简单的问题吗?【参考方案7】:

这是一个类似的解决方案,但它使用Array.prototype.reduce

function countCharacters(char, string) 
  return string.split('').reduce((acc, ch) => ch === char ? acc + 1: acc, 0)

如前所述,String.prototype.split 的运行速度比 String.prototype.replace 快​​得多。

【讨论】:

【参考方案8】:

好的,另一个带有正则表达式的 - 可能不快,但比其他的短且可读性更好,在我的情况下,只需 '_' 计数

key.replace(/[^_]/g,'').length

删除所有看起来不像你的字符的东西 但是使用字符串作为输入看起来不太好

【讨论】:

【参考方案9】:

如果您使用的是 lodash,_.countBy 方法将执行此操作:

_.countBy("abcda")['a'] //2

此方法也适用于数组:

_.countBy(['ab', 'cd', 'ab'])['ab'] //2

【讨论】:

【参考方案10】:

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

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

您可以查看yet another JSPerf 套件来测试此方法以及其他在字符串中查找字符的方法。

【讨论】:

很明显,如果您的代码以某种方式每秒迭代超过一百万个字符 500000 次,那么我的 CPU 运行速度至少为 100GHz(假设没有 SIMD;即使那样,它也至少为 40GHz)。因此我不相信这个基准是正确的。【参考方案11】:

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");

【讨论】:

【参考方案12】:

我对接受的答案做了一点改进,它允许检查区分大小写/不区分大小写的匹配,并且是附加到字符串对象的方法:

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 ***.com' 中搜索小写字母 'o',您可以使用:
var amount_of_os = 'I love ***.com'.count('o');

amount_of_os 将等于 2


如果我们要使用不区分大小写的匹配再次搜索相同的字符串,您将使用:
var amount_of_os = 'I love ***.com'.count('o', true);

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

【讨论】:

【参考方案13】:

我发现的最简单的方法...

例子-

str = 'mississippi';

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


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

【讨论】:

简洁!谢谢!【参考方案14】:

这是我的解决方案。很多解决方案已经在我面前发布。但我喜欢在这里分享我的观点。

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

Here you find my REPL

【讨论】:

【参考方案15】:
s = 'dir/dir/dir/dir/'
for(i=l=0;i<s.length;i++)
if(s[i] == '/')
l++

【讨论】:

【参考方案16】:

我正在做一个需要子字符串计数器的小项目。搜索错误的短语没有给我任何结果,但是在编写了自己的实现之后,我偶然发现了这个问题。无论如何,这是我的方式,它可能比这里的大多数都慢,但可能对某人有帮助:

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

【讨论】:

是的,它适用于子字符串,而不仅仅是子字符。但是,您需要将参数添加到函数中:)【参考方案17】:

我刚刚使用 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 日更新:Still the case with Node.js 12 (play with it yourself here)

【讨论】:

干得好@NuSkooler!一些caviats:尝试使用更长的字符串,(例如寻找'\n'-s 一首流行歌曲),并尝试在不同的s 上运行所有计数。说charCountN(s+i, c)。此外,您有一个错字:charCount3 被错误输入为 charCount2,因此结果相似。如果您尝试使用我的修改,您会发现 split 是赢家,而正则表达式匹配排在第二位。 感谢@BarneySzabolcs!更新了 repl 演示并重新运行了结果。我确实让字符串保持不变——正如你所说,YMMV。我确信在低级语言中工作的其他一些技巧也可以在这里工作,特别是对于较长的字符串。【参考方案18】:

string.split(desiredCharecter).length-1 怎么样

例子:

var str = "你好,生活怎么样"; var len = str.split("h").length-1;将为上述字符串中的字符“h”提供计数 2;

【讨论】:

【参考方案19】:

最快的方法似乎是通过索引运算符:

function charOccurances (str, char)

  for (var c = 0, i = 0, len = str.length; i < len; ++i)
  
    if (str[i] == char)
    
      ++c;
    
  
  return c;


console.log( charOccurances('example/path/script.js', '/') ); // 2

或者作为原型函数:

String.prototype.charOccurances = function (char)

  for (var c = 0, i = 0, len = this.length; i < len; ++i)
  
    if (this[i] == char)
    
      ++c;
    
  
  return c;


console.log( 'example/path/script.js'.charOccurances('/') ); // 2

【讨论】:

【参考方案20】:

下面使用正则表达式来测试长度。 testex 确保您没有 16 个或更多连续的非逗号字符。如果它通过了测试,那么它将继续拆分字符串。计算逗号就像计算标记减一一样简单。

var mainStr = "str1,str2,str3,str4";
var testregex = /([^,]16,)/g;
if (testregex.test(mainStr)) 
  alert("values must be separated by commas and each may not exceed 15 characters");
 else 
  var strs = mainStr.split(',');
  alert("mainStr contains " + strs.length + " substrings separated by commas.");
  alert("mainStr contains " + (strs.length-1) + " commas.");

【讨论】:

【参考方案21】:

我使用的是 Node.js v.6.0.0,最快的是带索引的(Lo Sauer 回答中的第三种方法)。

第二个是:

function count(s, c) 
  var n = 0;
  for (let x of s) 
    if (x == c)
      n++;
  
  return n;

【讨论】:

【参考方案22】:

还有:

function character_count(string, char, ptr = 0, count = 0) 
    while (ptr = string.indexOf(char, ptr) + 1) count ++
    return count

也适用于整数!

【讨论】:

【参考方案23】:

这是一个与 split() 和 replace 方法一样快的方法,它们比正则表达式方法(在 Chrome 和 Firefox 中)快一点。

let num = 0;
let str = "str1,str2,str3,str4";
//Note: Pre-calculating `.length` is an optimization;
//otherwise, it recalculates it every loop iteration.
let len = str.length;
//Note: Don't use a `for (... of ...)` loop, it's slow!
for (let charIndex = 0; charIndex < len; ++charIndex) 
  if (str[charIndex] === ',') 
    ++num;
  

【讨论】:

哇! 这比我在 FF 中的 split().length - 1 方法慢了 10 倍!这让我很生气! 哈哈。我讨厌需要性能优化——但 10 倍意味着你需要它! FF JIT 一定很糟糕。太糟糕了,它是我首选的浏览器......坚持住 - 你永远不知道什么得到了优化,什么没有。 嗯,可能只是split() 是在较低级别实现的,而存储和访问字符串的字符是在高级JS 中。我想知道什么可能会阻止您的代码 JIT... 嗯... 耶!我想到了!它不喜欢你的 for 循环。我会更新你的答案;使用常规的for 循环,它与split() 一样快。我相信原因和这里列出的一样:***.com/a/43821929/1599699 安德鲁 - 你不应该改变答案,只是评论它。我的原始帖子提供了一个替代方案,该替代方案(当时)在 chrome 中优化,但不是 Firefox。这是一种不同的形式,也许有人可能会用到。您的版本与此处的其他答案相匹配,使我的答案隐藏且无用。我很高兴你得到了你的算法集。但是,我对 Javascript 的投入不足以改变我的答案。此注释是对其他人的历史评论。检查原始版本的编辑。【参考方案24】:

var mainStr = "str1,str2,str3,str4";
var splitStr = mainStr.split(",").length - 1; // subtracting 1 is important!
alert(splitStr);

拆分成一个数组会给我们一些元素,这些元素总是比字符实例的数量多 1。这可能不是最节省内存的方法,但如果您的输入总是很小,这是一种直接且易于理解的方法。

如果您需要解析非常大的字符串(超过几百个字符),或者如果这是在处理大量数据的核心循环中,我会推荐不同的策略。

【讨论】:

虽然此代码可能会为问题提供解决方案,但最好添加有关其工作原理/方式的上下文。这可以帮助未来的用户学习并最终将这些知识应用到他们自己的代码中。在解释代码时,您也可能会得到用户的积极反馈/赞成。【参考方案25】:

我知道我在这里聚会迟到了,但我很困惑没有人用最基本的方法回答这个问题。社区为这个问题提供的大部分答案都是基于迭代的,但所有答案都是基于每个字符移动字符串,这并不是很有效。

在处理包含数千个字符的大字符串时,遍历每个字符以获取出现次数可能变得相当无关紧要,更不用说代码异味了。以下解决方案利用了sliceindexOf 和受信任的传统while 循环。这些方法使我们不必遍历每个字符,并将大大加快计算出现次数的时间。这些遵循与您在需要字符串遍历的解析器和词法分析器中发现的逻辑相似的逻辑。

与切片一起使用

在这种方法中,我们利用了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

就我个人而言,我倾向于第二种方法而不是第一种方法,但在处理大字符串时两者都很好且性能良好,但也适用于较小的字符串。

【讨论】:

【参考方案26】:

我的解决方案:

function countOcurrences(str, value)
   var regExp = new RegExp(value, "gi");
   return str.match(regExp) ? str.match(regExp).length : 0;  

【讨论】:

这将不起作用,因为 String.prototype.match 返回 null 没有匹配。这意味着不引用具有length 属性的对象。换句话说:String.prototype.match.call('willnotwork', /yesitwill/) === null【参考方案27】:

我知道这可能是一个老问题,但我为 JavaScript 的低级初学者提供了一个简单的解决方案。

作为一个初学者,我只能理解这个问题的一些解决方案,所以我使用了两个嵌套的 FOR 循环来检查字符串中的每个字符与每个其他字符,增加一个 count 为每个找到的与该字符相等的字符提供变量。

我创建了一个新的空白对象,其中每个属性键都是一个字符,值是每个字符在字符串中出现的次数(计数)。

示例函数:-

function countAllCharacters(str) 
  var obj = ;
  if(str.length!==0)
    for(i=0;i<str.length;i++)
      var count = 0;
      for(j=0;j<str.length;j++)
        if(str[i] === str[j])
          count++;
        
      
      if(!obj.hasOwnProperty(str[i]))
        obj[str[i]] = count;
      
    
  
  return obj;

【讨论】:

【参考方案28】:

我相信您会发现下面的解决方案非常短,非常快,能够处理非常长的字符串,能够支持多字符搜索,防错,并且能够处理空字符串搜索。

function substring_count(source_str, search_str, index) 
    source_str += "", search_str += "";
    var count = -1, index_inc = Math.max(search_str.length, 1);
    index = (+index || 0) - index_inc;
    do 
        ++count;
        index = source_str.indexOf(search_str, index + index_inc);
     while (~index);
    return count;

示例用法:

console.log(substring_count("Lorem ipsum dolar un sit amet.", "m "))

function substring_count(source_str, search_str, index) 
    source_str += "", search_str += "";
    var count = -1, index_inc = Math.max(search_str.length, 1);
    index = (+index || 0) - index_inc;
    do 
        ++count;
        index = source_str.indexOf(search_str, index + index_inc);
     while (~index);
    return count;

上面的代码修复了 Jakub Wawszczyk 中的主要性能错误,即使在 indexOf 说没有匹配项并且他的版本本身由于他忘记给函数输入参数而无法正常工作之后,该代码仍会继续寻找匹配项。

【讨论】:

【参考方案29】:
var a = "acvbasbb";
var b= ;
for (let i=0;i<a.length;i++)
    if((a.match(new RegExp(a[i], "g"))).length > 1)
        b[a[i]]=(a.match(new RegExp(a[i], "g"))).length;
    

console.log(b);

在 javascript 中,您可以使用上面的代码来获取字符串中某个字符的出现。

【讨论】:

【参考方案30】:

我的 ramda js 解决方案:

const testString = 'somestringtotest'

const countLetters = R.compose(
  R.map(R.length),
  R.groupBy(R.identity),
  R.split('')
)

countLetters(testString)

Link to REPL.

【讨论】:

以上是关于计算Javascript中字符串中字符的出现次数的主要内容,如果未能解决你的问题,请参考以下文章

用函数编程实现计算字符串中子串出现的次数,这个程序不知道错哪了

JavaScript计算每个字母出现的次数

python2 怎么统计列表字符串出现次数

计算字符串中指定字符最大连续出现的次数

JavaScript学习(七十七)—统计字符串中出现次数最多的字符和每个字符出现的次数

python输入小写字符串,输出字符串中出现字母最多的字母及其出现次数,如果有多