Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Count the number of words in NSString

I'm trying to implement a word count function for my app that uses UITextView.

There's a space between two words in English, so it's really easy to count the number of words in an English sentence. The problem occurs with Chinese and Japanese word counting because usually, there's no any space in the entire sentence.

I checked with three different text editors in iPad that have a word count feature and compare them with MS Words.

For example, here's a series of Japanese characters meaning the world's idea: 世界(the world)の('s)アイデア(idea)

世界のアイデア

1) Pages for iPad and MS Words count each character as one word, so it contains 7 words.

2) iPad text editor P*** counts the entire as one word --> They just used space to separate words.

3) iPad text editor i*** counts them as three words --> I believe they used CFStringTokenizer with kCFStringTokenizerUnitWord because I could get the same result)

I've researched on the Internet, and Pages and MS Words' word counting seems to be correct because each Chinese character has a meaning.

I couldn't find any class that counts the words like Pages or MS Words, and it would be very hard to implement it from scratch because besides Japanese and Chinese, iPad supports a lot of different foreign languages.

I think CFStringTokenizer with kCFStringTokenizerUnitWord is the best option though.

Is there a way to count words in NSString like Pages and MSWords?

Thank you

like image 627
pnmn Avatar asked Mar 03 '11 06:03

pnmn


3 Answers

I recommend keep using CFStringTokenizer. Because it's platform feature, so will be upgraded by platform upgrade. And many people in Apple are working hardly to reflect real cultural difference. Which are hard to know for regular developers.

This is hard because this is not a programming problem essentially. This is a human cultural linguistic problem. You need a human language specialist for each culture. For Japanese, you need Japanese culture specialist. However, I don't think Japanese people needs word count feature seriously, because as I heard, the concept of word itself is not so important in the Japanese culture. You should define concept of word first.

And I can't understand why you want to force concept of word count into the character count. The Kanji word that you instanced. This is equal with counting universe as 2 words by splitting into uni + verse by meaning. Not even a logic. Splitting word by it's meaning is sometimes completely wrong and useless by the definition of word. Because definition of word itself are different by the cultures. In my language Korean, word is just a formal unit, not a meaning unit. The idea that each word is matching to each meaning is right only in roman character cultures.

Just give another feature like character counting for the users in east-asia if you think need it. And counting character in unicode string is so easy with -[NSString length] method.

I'm a Korean speaker, (so maybe out of your case :) and in many cases we count characters instead of words. In fact, I never saw people counting words in my whole life. I laughed at word counting feature on MS word because I guessed nobody would use it. (However now I know it's important in roman character cultures.) I have used word counting feature only once to know it works really :) I believe this is similar in Chinese or Japanese. Maybe Japanese users use the word counting because their basic alphabet is similar with roman characters which have no concept of composition. However they're using Kanji heavily which are completely compositing, character-centric system.

If you make word counting feature works greatly on those languages (which are using by people even does not feel any needs to split sentences into smaller formal units!), it's hard to imagine someone who using it. And without linguistic specialist, the feature should not correct.

like image 194
eonil Avatar answered Nov 19 '22 21:11

eonil


This is a really hard problem if your string doesn't contain tokens identifying word breaks (like spaces). One way I know derived from attempting to solve anagrams is this:

At the start of the string you start with one character. Is it a word? It could be a word like "A" but it could also be a part of a word like "AN" or "ANALOG". So the decision about what is a word has to be made considering all of the string. You would consider the next characters to see if you can make another word starting with the first character following the first word you think you might have found. If you decide the word is "A" and you are left with "NALOG" then you will soon find that there are no more words to be found. When you start finding words in the dictionary (see below) then you know you are making the right choices about where to break the words. When you stop finding words you know you have made a wrong choice and you need to backtrack.

A big part of this is having dictionaries sufficient to contain any word you might encounter. The English resource would be TWL06 or SOWPODS or other scrabble dictionaries, containing many obscure words. You need a lot of memory to do this because if you check the words against a simple array containing all of the possible words your program will run incredibly slow. If you parse your dictionary, persist it as a plist and recreate the dictionary your checking will be quick enough but it will require a lot more space on disk and more space in memory. One of these big scrabble dictionaries can expand to about 10MB with the actual words as keys and a simple NSNumber as a placeholder for value - you don't care what the value is, just that the key exists in the dictionary, which tells you that the word is recognised as valid.

If you maintain an array as you count you get to do [array count] in a triumphal manner as you add the last word containing the last characters to it, but you also have an easy way of backtracking. If at some point you stop finding valid words you can pop the lastObject off the array and replace it at the start of the string, then start looking for alternative words. If that fails to get you back on the right track pop another word.

I would proceed by experimentation, looking for a potential three words ahead as you parse the string - when you have identified three potential words, take the first away, store it in the array and look for another word. If you find it is too slow to do it this way and you are getting OK results considering only two words ahead, drop it to two. If you find you are running up too many dead ends with your word division strategy then increase the number of words ahead you consider.

Another way would be to employ natural language rules - for example "A" and "NALOG" might look OK because a consonant follows "A", but "A" and "ARDVARK" would be ruled out because it would be correct for a word beginning in a vowel to follow "AN", not "A". This can get as complicated as you like to make it - I don't know if this gets simpler in Japanese or not but there are certainly common verb endings like "ma su".

(edit: started a bounty, I'd like to know the very best way to do this if my way isn't it.)

like image 38
Adam Eberbach Avatar answered Nov 19 '22 19:11

Adam Eberbach


If you are using iOS 4, you can do something like

__block int count = 0;
[string enumerateSubstringsInRange:range
                           options:NSStringEnumerationByWords
                        usingBlock:^(NSString *word,
                                     NSRange wordRange,
                                     NSRange enclosingRange,
                                     BOOL *stop)
    {
        count++;
    }
];

More information in the NSString class reference.

There is also WWDC 2010 session, number 110, about advanced text handling, that explains this, around minute 10 or so.

like image 1
Marcos Crispino Avatar answered Nov 19 '22 21:11

Marcos Crispino