Word-counting script in C# My goal is to read from standard input, break up the input into words (Unicode letters and apostrophes), case-insensitively, and produce a report to standard output, where each line is a word followed by a space followed by its count. The output should be sorted by words.

This is my code:

using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;

class TraditionalWordCountApp
{
    public static void Main(string[] args)
    {
        SortedDictionary&lt;string, int&gt; counts = new SortedDictionary&lt;string, int&gt;();
        Regex wordRegex = new Regex(@"[\p{L}']+");
        string line;
        while ((line = Console.ReadLine()) != null)
        {
            line = line.ToLower();
            foreach (Match m in wordRegex.Matches(line))
            {
                String word = m.Value;
                int count;
                counts.TryGetValue(word, out count);
                counts[word] = ++count;
            }
        }
        foreach (KeyValuePair&lt;string, int&gt; pair in counts)
        {
            Console.WriteLine("{0} {1}", pair.Key, pair.Value);
        }
    }
}


My concerns:


Should things be wrapped in a namespace?
Should either the class or the main function or both be public?
Is there a nicer way to declare the sorted dictionary? Java has &lt;&gt;, does C#?
I used the C-style idiom of assigning to a variable and then checking for null, all inside the while-condition! Surely there is a nicer way to do this in C#. Is there? I like neither the embedded assignment nor the billion-dollar-mistake null. :)
I searched StackOverflow a bit for a nicer way to update a word frequency dictionary. Most languages seem to have a built-in get-with-default method on dictionaries, but the answers I saw said no, and they recommended TryGetValue. These answers are old, though. Is there something nicer in the most modern C#?
Any nicer way to do the last loop?
Would a LINQ-based solution be nicer? More idiomatic? What would it look like? I think (though I am brand new to C#) I could whip something up, but should I? FWIW I do know how to do the LINQ-y stuff in Java but wonder if this is the right way to go in C#.
It's not mandatory but it's a widely accepted guideline. Break it only if you have a good reason to do so.
No, they don't. Class should be internal (default) and static (because it has not instance methods). Main() method should be private to make clear that it won't be called outside this class but it's not mandatory (someone prefers public because it's logically called from outside).


For the other points things are more complex that a paragraph...

You can create a generator to read from standard input (see very last paragraph for a discussion about this):

private static IEnumerable&lt;string&gt; ReadAllInputs()
{
    while (true)
    {
        string line = Console.ReadLine();
        if (line == null)
            break;

        yield return line;
    }
}


You do not need ToLower() on input, I do not even start to mention all the problems of case insensitive comparison if not done properly: a lower case string may be different from an upper case string (even in number of characters) but they may be compared equal. Don't put yourself in trouble and let the framework do the work for you:

var counts = new SortedDictionary&lt;string, int&gt;(StringComparer.CurrentCultureIgnoreCase);


Note that you do not need a SortedDictionary here, ordering may be done (in this case) when printing output. If performance matters then you should profile by yourself.

When you increment word count you also increment local variable count, not a big performance hit (and compiler may be smart enough to elide local variable) but it's about code clarity. You can do:

counts[word] = count + 1;


Or simply:

++counts[word];


Note that this innocent code ++counts[word] (as well as original counts[word] = ++count) when word doesn't exist in dictionary (TryGetValue returns false) will perform another lookup vanishing part of the benefit of TryGetValue for the first insertion! You have to explicitly call Add().

If you want you can refactor foreach loop to print results with LINQ, not a big gain (IMO), let's see how:

Console.WriteLine(String.Join(Environment.NewLine,
    counts.Select(x =&gt; $"{x.Key} {x.Value}")));


If we put everything together with then have something like this:

namespace Test {
    static class TraditionalWordCountApp {
        static void Main(string[] args) {
            var counts = new Dictionary&lt;string, int&gt;(StringComparer.CurrentCultureIgnoreCase);
            var wordRegex = new Regex(@"(?i)[\p{L}']+");

            foreach (var match in ReadAllInputs().SelectMany(x =&gt; wordRegex.Match(x))) {
                if (counts.TryGetValue(match.Value, out count))
                    counts[word] = count + 1;
                else
                    counts.Add(match.Value, 1);    
            }

            Console.WriteLine(String.Join(Environment.NewLine,
                counts.OrderBy(x =&gt; x.Key).Select(x =&gt; $"{x.Key} {x.Value}")));
        }

        static IEnumerable&lt;string&gt; ReadAllInputs() {
            while (true) { // Yes, in C# this is "safe"
                string line = Console.ReadLine();
                if (line == null)
                    break;

                // No need to match any regex against an empty line
                if (line.Length == 0)
                    continue; 

                yield return line;
            }
        }
    }
}




All these done you may think that...there should be another way to do it. You can use GroupBy(). Profile both versions to see which one has better performance (and the one you feel more easy to understand):

var counts = ReadAllInputs()
    .SelectMany(x =&gt; regex.Match(x))
    .GroupBy(x =&gt; x.Value, StringComparer.CurrentCultureIgnoreCase)
    .Select(x =&gt; new { Word = x.Key, Count = x.Count() })
    .OrderBy(x =&gt; x.Word);


Then you print it with:

Console.WriteLine(String.Join(Environment.NewLine,
    counts.Select(x =&gt; $"{x.Word} {x.Count}")));




One final note about word counting. It may apply or not to your case but don't forget that word is a concept with different meanings in different languages. Topic is vast but try to paste a - for example - Japanese text in Microsoft Word and check what the word count is: number of characters (another vague concept). If you have to deal with international text you may need to write specific code for different cultures.



Comments. Few notes collected from comments.

Sometimes you see this kind of code:

while (Console.In.Peek() != -1)
   yield return Console.ReadLine();


It works only if console input is redirected, when input is line buffered then it'll end immediately after first line. Nice and short but simply it doesn't work. If you really really love LINQ and you can't live without one-line functions then you may do this:

static IEnumerable&lt;string&gt; ReadAllInputs() {
    return Enumerable.Range(0, Int32.MaxValue)
        .Select(_ =&gt; Console.ReadLine())
        .TakeWhile(x =&gt; x != null);
}


Can we make our reader more "generic"? The obvious idea is to use it with files. Maybe yes, maybe not. First of all a console application can have piped input (from file or from another application) then we may not need this feature. If we do then we should introduce a separate class InputReader, implementation will be different for ConsoleInputReader, FileInputReader and HttpInputReader (for example); one function with a TextReader parameter will probably be the worst compromise for all our use-cases. What I'm suggesting is just to don't introduce complexity until you need it...