Как подсчитать уникальные символы в строке

Допустим, у нас есть переменная 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. Готовы ли они к работе? какие-либо предложения?

Вы можете использовать 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)

Проблема нашего первого наивного решения заключается в том, что он не обрабатывает Юникод должным образом, и он также не учитывает, что пользователь воспринимает как характер . Попробуем "𠀑".Distinct().Count() и ваш код ошибочно вернутся … 2, потому что его представление UTF-16 равно 0xD840 0xDC11 (BTW, каждый из них один, не является допустимым символом Юникода, потому что они высокий и низкий суррогат, соответственно).

Здесь я не буду очень строго относиться к терминам и определениям, поэтому, пожалуйста, обратитесь к http://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).

Если вам нужно правильно это обработать, вам нужно сделать вещи намного сложнее (и медленно). К счастью, у classа 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; } 

Обратите внимание, что этот пример не обрабатывает дубликаты (когда один и тот же символ может иметь разные коды или может быть одной кодовой точкой или комбинированным символом).

Комбинированные символы

Если вам приходится обрабатывать комбинированные символы (и, конечно, кодирование), лучший способ сделать это - использовать class StringInfo . Вы будете перечислять (и затем подсчитывать) как комбинированные, так и закодированные символы:

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

Walk() является тривиальным для реализации метода расширения, который просто просматривает все элементы IEnumerator (это нам нужно, потому что GetTextElementEnumerator() возвращает IEnumerator вместо IEnumerable ).

Обратите внимание, что после правильного разбиения текста его можно пересчитать с помощью нашего первого решения (дело в том, что кирпич не char а последовательность char (для простоты здесь возвращается сама string ). Снова этот код не обрабатывает дубликаты ,

культура

Существует не так много, чтобы справляться с проблемами, перечисленными в пункте 3. Каждый язык имеет свои собственные правила и поддерживать их все может быть болью. Дополнительные примеры о проблемах культуры на этом более длинном конкретном посту .

Важно знать о них (так что вам нужно немного узнать о языках, на которые вы нацеливаетесь), и не забывайте, что Unicode и несколько переведенных файлов resx не будут делать ваше приложение глобальным .

Если обработка текста важна в вашем приложении, вы можете решить многие проблемы, используя специализированные библиотеки DLL для каждой поддерживаемой вами локали (для подсчета символов, подсчета слов и т. Д.), Как это делают текстовые процессоры. Например, перечисленные мной проблемы могут быть просто решены с использованием словарей. То, что я обычно делаю, это не использовать стандартные функции .NET для строк (также из-за некоторых ошибок), я создаю class Unicode со статическими методами для всего, что мне нужно (подсчет символов, преобразование, сравнение) и множество специализированных производных classов для каждого поддерживаемый язык. Во время выполнения статические методы будут использовать текущее имя культуры streamа, чтобы выбрать правильную реализацию из словаря и делегировать работу на это. Скелет может быть примерно таким:

 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() 

сделаю это.

  • Как преобразовать строку dd / mm / yyyy в datetime в SQL Server?
  • Печать реверса любой строки без использования какой-либо предопределенной функции?
  • string = string + int: Что за кулисами?
  • Как заменить символы новой строки с помощью JSP и JSTL?
  • Почему оператор switch не может применяться к строкам?
  • Как получить последний сегмент пути uri
  • Заменить часть строки другой строкой
  • Как читать / писать строки из файла на Android
  • Как вернуть текст из Native (C ++) кода
  • Почему нет Char.Empty, как String.Empty?
  • StringBuilder vs Конкатенация строк в toString () в Java
  • Давайте будем гением компьютера.