Question

I am writing a text editor and need to provide a live word count. Right now I am using this extension method:

 public static int WordCount(this string s)
    {
        s = s.TrimEnd();
        if (String.IsNullOrEmpty(s)) return 0;
        int count = 0;
        bool lastWasWordChar = false;
        foreach (char c in s)
        {
            if (Char.IsLetterOrDigit(c) || c == '_' || c == '\'' || c == '-')
            {
                lastWasWordChar = true;
                continue;
            }
            if (lastWasWordChar)
            {
                lastWasWordChar = false;
                count++;
            }
        }
        if (!lastWasWordChar) count--;
        return count + 1;
    }

I have it set so that the word count runs on the richtextbox's text every tenth of a second (if the selection start is different from what it was last time the method ran). The problem is that the word count gets slow when working on very long files. To solve this I am thinking about having the word count only run on the current paragraph, recording the word count each time and comparing it against what the word count was last time the word count ran. It would then add the difference between the two to the total word count. Doing this would cause many complications (if the user pastes, if the user deletes a paragraph, ect.) Is this a logical way to go about improving my word count? Or is there something that I don't know about which would make it better?

EDIT: Would it work to run the word count on a different thread? I don't know much about threading, will research.

SAMPLE TEXT THAT I USED:

Was it helpful?

Solution

You could do a simpler word count based on white-space:

public static int WordCount(this string s)
{
  return s.Split(new char[] {' '}, 
    StringSplitOptions.RemoveEmptyEntries).Length;
}

MSDN provides this example, should give you an accurate word count much faster on large files.

OTHER TIPS

You could also use a very simple Regex that looks for at least one word character and/or apostrophe to capture the contractions:

public static int WordCount(this string s) 
{
    return Regex.Matches(s, @"[\w']+").Count;
}

This will return 2141 matches (which is actually more correct than Word in this case because Word counts the single asterisk as a word in the sentence "by stabbing a * with her finger").

Your method is actually faster than the proposed String.Split method, nearly three times faster on x86 and more than two times faster on x64 in fact. I suspect JIT is messing with your timings, always run your microbenchmarks twice as JIT will occupy the vast majority of the time during your first run. And because String.Split has been NGEN'd, it doesn't need to be compiled to native code and thus will appear to be faster.

Not to mention it's also more accurate, String.Split will count 7 words here:

test :     : this is a test

It also makes sense, String.Split doesn't perform any magic and I would be very surprised if the creation of an array of many strings would be faster than simply iterating over the individual characters in the string. Foreaching over a string has apparently been highly optimized as I tried unsafe pointer arithmetic and it was actually a tiny bit slower than a simple foreach. I really doubt there's any way to do this faster, other than being smart about which sections in your text need word counts.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top