如何统计字符串中不同字符的数量

4
假设我们有一个变量 myString="blabla" 或 mystring=998769。
myString.Length; //will get you your result

myString.Count(char.IsLetter);    //if you only want the count of letters:

如何获取唯一字符计数?我的意思是对于“blabla”,结果必须为3,对于“998769”,结果将为4。是否有现成的函数可用?有什么建议吗?

3个回答

19
您可以使用LINQ:
var count = myString.Distinct().Count();

它利用了一个事实,即 string 实现了 IEnumerable<char>

没有 LINQ,你可以使用 HashSet<char> 做出与 Distinct 内部相同的操作:

var count = (new HashSet<char>(myString)).Count;

完整的代码必须像这样吗?http://www.sourcedrop.net/c1Hfcf81c516a - heron

5
如果您只处理英文 ANSI 文本(或 BMP 字符),那么在编写以下内容时,80%的情况下:
myString.Distinct().Count()

你将会过上幸福生活,不会再有任何麻烦。让我发表这个答案只为那些真正需要以适当方式处理它的人。我想说每个人都应该这样做,但我知道这是不正确的(摘自维基百科):
“因为最常用的字符都在基本多语言平面中,所以对代理对的处理通常没有经过彻底测试。这导致持久性错误和潜在的安全漏洞,即使在流行的和经过良好审查的应用软件中也是如此(例如CVE-2008-2938,CVE-2012-2135)”
我们第一个天真的解决方案的问题在于它不能正确地处理Unicode,也没有考虑用户感知的字符。让我们尝试 "".Distinct().Count(),你的代码将错误地返回2,因为它的UTF-16表示形式是0xD840 0xDC11(顺便说一下,它们各自单独存在时不是有效的Unicode字符,因为它们分别是高代理和低代理)。

我在这里不会严格定义术语,因此请参考www.unicode.org。如果您想进行更广泛的讨论,请阅读如何执行基于Unicode的逐字符比较?,编码不是您必须考虑的唯一问题。

1)它没有考虑到.NET System.Char 不表示一个字符(或更具体地说是字形),而是UTF-16编码文本代码单元(例如,使用表意文字)。通常它们是相同的,但不总是。

2) 如果您正在计算用户认为(或感知)的字符,则此方法将失败,因为它不检查组合字符,例如ا́(阿拉伯语中有许多这样的例子)。由于历史原因,存在重复项:例如é既是单个Unicode代码点,也是一个组合(那么该代码将失败)。

3) 我们谈论的是西方/美国对字符的定义。如果您正在为最终用户计算字符,则可能需要更改其期望的定义(例如,在韩语字符定义中可能不太明显,另一个例子是捷克文本ch始终被视为单个字符)。最后,请不要忘记在将字符转换为大写/小写时出现一些奇怪的事情(例如,在德语中,ß在大写时为SS,另请参见此帖子)。

编码

C#字符串采用UTF-16编码(char为两个字节),但UTF-16不是固定大小的编码方式,char应该被正确地称为代码单元。这意味着什么?你可能有一个string,它的Length是2,但实际上用户将看到(并且实际上也是)只有一个字符(然后计数应该是1)。

如果你需要正确处理这个问题,那么你必须使事情变得更加复杂(和缓慢)。幸运的是,Char类有一些有用的方法来处理代理项。

下面的代码未经测试(仅用于说明目的,因此绝对不是最优化的,我相信可以比这做得更好),所以只是作为进一步研究的起点:

int CountCharacters(string text)
{
    HashSet<string> characters = new HashSet<string>();

    string currentCharacter = "";

    for (int i = 0; i < text.Length; ++i)
    {
        if (Char.IsHighSurrogate(text, i))
        {
            // Do not count this, next one will give the full pair
            currentCharacter = text[i].ToString();
            continue;
        }
        else if (Char.IsLowSurrogate(text, i))
        {
            // Our "character" is encoded as previous one plus this one
            currentCharacter += text[i];
        }
        else
            currentCharacter = text[i].ToString();

        if (!characters.Contains(currentCharacter))
            characters.Add(currentCharacter);
    }

    return characters.Count;
}

请注意,此示例不处理重复项(当相同字符可能具有不同的代码或可以是单个代码点或组合字符时)。

组合字符

如果您必须处理组合字符(当然还包括编码),那么最好的方法是使用StringInfo类。您将枚举(然后计数)组合和编码字符:

StringInfo.GetTextElementEnumerator(text).Walk()
    .Distinct().Count();
Walk()是一个易于实现的扩展方法,它简单地遍历所有IEnumerator元素(我们需要它,因为GetTextElementEnumerator()返回的是IEnumerator而不是IEnumerable)。
请注意,在文本正确分割后,它可以使用我们的第一种解决方案进行计数(关键在于brick不是char,而是一系列char(为了简单起见,在这里作为string本身返回)。再次强调,此代码不处理重复项。

文化

在处理第3点列出的问题方面,您无法做太多事情。每种语言都有自己的规则,支持它们可能很麻烦。有关文化问题的更多示例,请参见this longer specific post
重要的是要意识到这些问题(因此您必须了解一些目标语言),并且不要忘记Unicode和少量翻译的resx文件并不能使您的应用程序成为全球性的。
如果文本处理在您的应用程序中很重要,您可以使用针对每个受支持语言的专门DLL(例如文字处理器)来解决许多问题(例如计算字符数、单词数等)。例如,我列出的问题可以简单地使用字典解决。我通常不使用标准的.NET字符串函数(也因为存在一些错误),而是创建一个Unicode类,并为所需的每个内容(字符计数、转换、比较)创建静态方法和许多专门的派生类来支持每种语言。在运行时,这些静态方法将使用当前线程区域性名称从字典中选择正确的实现并将任务委派给它。骨架可能是这样的:
abstract class Unicode
{
    public static string CountCharacters(string text)
    {
        return GetConcreteClass().CountCharactersCore(text);
    }

    protected virtual string CountCharactersCore(string text)
    {
        // Default implementation, overridden in derived classes if needed
        return StringInfo.GetTextElementEnumerator(text).Cast<string>()
            .Distinct().Count();
    }

    private Dictionary<string, Unicode> _implementations;

    private Unicode GetConcreteClass()
    {
        string cultureName = Thread.Current.CurrentCulture.Name;

        // Check if concrete class has been loaded and put in dictionary
        ...

        return _implementations[cultureName];
    }
}

1
如果您正在使用C#,那么Linq会很好地帮助您 - 再次。
"blabla".Distinct().Count()

将会做到。


3
不需要将其转换为字符数组,应该使用Distinct()而不是Disctinct() - DGibbs
太对了,已按建议删除。 - Kevin Roche

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