Подсчет частот букв

Я читаю в текстовом файле с помощью StreamReader для программы. Мне нужно записать частоту каждой буквы в строке в массив (где индекс 0 будет A, и так далее). Какой самый простой подход для этого?

Редактировать: у меня было это первоначально, пока я не понял, что это было совершенно неправильно.

int counter = 0;
int[] freq = new int[26]; // create frequency array

// counts frequency
while (counter < inValue.Length)
{
      int A = 65; // ASCII value for "A"
      char x = char.Parse(inValue.Substring(counter, 1)); // get individual characters from string
       int s = (int)x; // cast character to integer value

       if (s == A + counter)
             freq[counter]++;

             counter++;
 }

Где inValue - текстовый файл, который StreamReader считывает в программу.

4 ответа

var freqs = File.ReadAllText("myfile.txt")
                    .Where(c => Char.IsLetter(c))
                    .GroupBy(c => c)
                    .ToDictionary(g => g.Key, g => g.Count());

Это должно дать вам словарь символов и их количество.

Обновить:

Если вы хотите, чтобы регистр не учитывался, просто измените GroupBy:

.GroupBy(c => Char.ToUpper(c)) // instead of .GroupBy(c => c)

И на мой взгляд, словарь лучше, чем массив в этом случае, потому что символ, которому принадлежит "count", не просто подразумевается индексом; вместо этого это явный ключ. Это облегчает поиск, потому что вам не нужно преобразовывать символ в индекс. Кроме того, это делает его более гибким при добавлении поддержки интернационализации. Однако, если вам абсолютно необходим массив, это простое изменение:

var freqs = File.ReadAllText("myfile.txt")
                    .Where(c => Char.IsLetter(c))
                    .GroupBy(c => c)
                    .OrderBy(g => g.Key) 
                    .Select(g => g.Count())
                    .ToArray()

Вы можете попробовать что-то вроде этого. Это сработало для меня, но я не использовал StreamReader:-

   int[] c = new int[(int)char.MaxValue];

string s = File.ReadAllText("text.txt");

foreach (char t in s)
{
    c[(int)t]++;
}

for (int i = 0; i < (int)char.MaxValue; i++)
{
    if (c[i] > 0 &&
    char.IsLetterOrDigit((char)i))
    {
    Console.WriteLine("Letter: {0}  Frequency: {1}",(char)i, c[i]);
    }
}

Несколько модификаций в вашем коде приведут его в действие, при условии, что вы хотите считать только буквы от "A" до "Z":

int counter = 0;
int[] freq = new int[26]; // create frequency array

// counts frequency
while (counter < inValue.Length)
{
    char c = invalue[counter];
    if (c >= 'A' && c <= 'Z')
    {
        ++freq[(int)c - 65]
    }
    ++counter;
}

Если вы также хотите считать строчные буквы, измените первую строку в цикле на:

char c = char.ToUpper(invalue[counter]);

Я потратил довольно много времени, чтобы выяснить этот Linq, который приведет к тому же массиву, который вы хотите:

int[] occurance = File.ReadAllText("myfile.txt")
                  .Where(c => char.IsLetter(c))
                  .Select(c => (int)char.ToUpperInvariant(c) - 65)
                  .GroupBy(a => a)
                  .ToDictionary(a => a.Key, a => a.Count())
                  .OrderBy(a => a.Key)
                  .Select(a => a.Value)
                  .ToArray();
Другие вопросы по тегам