关于c#:如何计算字符串中的唯一字符

How to count unique characters in string

本问题已经有最佳答案,请猛点这里访问。

假设我们有变量myString="blabla"或myString=998769

1
2
3
myString.Length; //will get you your result

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

如何获得,唯一的字符数?我的意思是"blabla"的结果必须是3,doe"998769"将是4。准备好了吗?有什么建议吗?


您可以使用LINQ:

1
var count = myString.Distinct().Count();

它使用了这样一个事实:string实现IEnumerable

如果没有linq,您可以在内部执行与Distinct相同的操作,并使用HashSet

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


如果您只处理英文(或BMP中的字符)的ANSI文本,那么80%的时间如果您写:好的。

1
myString.Distinct().Count()

你会幸福地生活,不会有任何麻烦。我只为那些真正需要以适当方式处理的人发布这个答案。我想说每个人都应该这样做,但我知道这不是真的(引自维基百科):好的。

Because the most commonly used characters are all in the Basic Multilingual Plane, handling of surrogate pairs is often not thoroughly tested. This leads to persistent bugs and potential security holes, even in popular and well-reviewed application software (e.g. CVE-2008-2938, CVE-2012-2135)

Ok.

我们的第一个NA的问题?ve解决方案是它不能正确处理Unicode,也不考虑用户认为什么是字符。让我们试试"??".Distinct().Count(),您的代码将错误地返回…2,因为它的utf-16表示是0xD840 0xDC11(顺便说一句,每一个都不是有效的Unicode字符,因为它们分别是高代理和低代理)。好的。

这里我对术语和定义不太严格,请参考www.unicode.org。对于更广泛的讨论,请阅读"我如何逐字符进行Unicode感知比较?"编码不仅仅是你必须考虑的问题。好的。

1)它没有考虑到.NET System.Char不代表字符(或者更具体地说是图形),而是一个UTF-16编码文本的代码单元(例如,可能有表意字符)。它们经常重合,但现在总是。好的。

2)如果您正在计算用户认为(或感知到)的字符数,那么这将再次失败,因为它不检查这样的组合字符??(阿拉伯语中的许多例子)。由于历史原因,存在重复的代码:例如_,它既是一个Unicode代码点,又是一个组合(这样代码就会失败)。好的。

3)我们谈论的是西方/美国对性格的定义。如果要为最终用户计数字符,您可能需要将定义更改为他们期望的值(例如,在韩语中,字符的定义可能不太明显,另一个示例是捷克文CHABD,它始终被计为单个字符)。最后,当您将字符转换为大写/小写(例如,在德语中)时,不要忘记一些奇怪的事情。是大写的s,另请参阅本文)。好的。编码

c字符串编码为utf-16(char是两个字节),但utf-16不是固定大小的编码,char应该正确地称为代码单元。这是什么意思?你可能有一个string,其中Length是2,但实际上用户会看到(实际上是)只有一个字符(那么计数应该是1)。好的。

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

以下代码未经测试(出于说明目的,因此绝对没有优化,我相信它可以做得比这更好),因此请将其作为进一步调查的起点:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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类。您将枚举(然后计数)组合字符和编码字符:好的。

1
2
StringInfo.GetTextElementEnumerator(text).Walk()
    .Distinct().Count();

Walk()是一种简单地遍历所有IEnumerator元素的扩展方法(我们需要它,因为GetTextElementEnumerator()返回IEnumerator而不是IEnumerable)。好的。

请注意,在正确分割文本后,可以使用我们的第一个解决方案计算文本(要点是砖块不是Char,而是Char的序列(为了简单起见,此处返回string)。同样,此代码不处理重复项。好的。文化

对于第3点列出的问题,您可以做的工作不多。每种语言都有自己的规则,支持它们都是痛苦的。关于文化问题的更多例子。好的。

重要的是要注意它们(所以你必须对目标语言有一点了解),不要忘记Unicode和少量翻译的resx文件不会使你的应用程序成为全局的。好的。

如果文本处理在您的应用程序中很重要,您可以像字处理程序一样,为您支持的每个区域设置(计算字符、计算单词等)使用专门的DLL来解决许多问题。例如,我列出的问题可以使用字典简单地解决。我通常做的是不要对字符串使用标准的.NET函数(也因为某些错误),我用静态方法创建了一个Unicode类来满足我所需要的一切(字符计数、转换、比较),并为每种支持的语言创建了许多专门的派生类。在运行时,静态方法将使用当前线程区域性名称从字典中选择正确的实现,并将工作委托给该实现。骨架可以是这样的:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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];
    }
}

好啊。


如果你用的是C,那么Linq会很好地帮助你-再次:

1
"blabla".Distinct().Count()

会做的。