如何计算字符串中的唯一字符

假设我们有变量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,doe“998769”它将是4.是否准备好了function? 有什么建议?

您可以使用LINQ:

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

它使用一个事实,该string实现IEnumerable

没有LINQ,你可以在内部做与Distinct相同的东西并使用HashSet

 var count = (new HashSet(myString)).Count; 

如果您只处理英文的ANSI文本(或BMP中的字符),那么如果您编写以下内容则为80%:

 myString.Distinct().Count() 

你会过得很开心,也不会有任何麻烦。 让我发布这个答案只是为了真正需要以正确的方式处理它的人。 我会说每个人都应该,但我知道这不是真的(引自维基百科):

因为最常用的字符都在基本多语言平面中,所以代理对的处理通常没有经过彻底的测试。 这会导致持续存在的漏洞和潜在的安全漏洞,即使在流行且经过良好评估的应用软件中也是如此(例如CVE-2008-2938,CVE-2012-2135)

我们的第一个天真的解决方案的问题是它没有正确处理Unicode ,也没有考虑用户认为是什么字符 。 让我们尝试"𠀑".Distinct().Count()和你的代码将错误地返回… 2因为它的UTF-16表示是0xD840 0xDC11 (BTW中的每一个,单独,不是有效的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 characters = new HashSet(); 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点列出的问题。每种语言都有自己的规则并且支持它们都可能很痛苦。 关于这个较长的特定职位的文化问题的更多例子。

重要的是要了解它们(所以你必须对你所针对的语言有所了解)并且不要忘记Unicode和很少翻译的resx文件不会使你的应用程序全局化

如果文本处理在您的应用程序中很重要,您可以使用专门的DLL为您支持的每个语言环境(计算字符,计算单词等)解决许多问题,就像Word处理器一样。 例如,我列出的问题可以使用词典简单地解决。 我通常做的是不对字符串使用标准.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() .Distinct().Count(); } private Dictionary _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很好地拯救了 – 再次:

 "blabla".Distinct().Count() 

会做的。