NSSpellChecker certain language not working - objective-c

My mac version is 10.13.4 with xcode version 9.4.1
I'm trying to use NSSpellChecker with Korean language. But regardless of whether the word I check is misspelled or not, the Range returned is {2^63 - 1, 0}
Based on quick experiments with english, that Range is returned when there are no misspelled words OR if the input word language doesn't match current spell checker's language.
This is what I'm doing:
NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
NSString *sampleWord = #"마직막";
NSRange range = [checker checkSpellingOfString:sampleWord, startingAt: 0];
NSLog(#"range: %#", NSStringFromRange(range));
that sampleWord is misspelled, but I'm getting
range: {9223372036854775807, 0}
as the output.
Could someone provide any insight on what's happening?
Edit1: When I throw correct english words at it, NSSpellChecker indicates spelling error, which is correct. I gave an english word to korean environment. So I guess NSSpellChecker itself is working correctly, but there is something I missed regarding setup kind of..?

Here is code I used, based on your code and comments.
// insert code here...
NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
// NSLog ( #"Available %#", checker.availableLanguages );
NSLog ( #"Korean %#", [checker.availableLanguages containsObject:#"ko"] ? #"YES" : #"NO" ); // Prints YES
[checker setAutomaticallyIdentifiesLanguages:NO]; // Seems to have NO impact
NSLog ( #"KO %#", [checker setLanguage:#"ko"] ? #"YES" : #"NO" ); // Prints YES
// NSString * sampleWord = #"마직막 마직막 마직막 마직막 마직막"; // makes no difference
NSString * sampleWord = #"마직막"; // makes no difference
// NSString * sampleWord = #"마직막 ddd sss aaa"; // consistently prints { 4, 3 }
// NSString * sampleWord = #"안녕하세요 햇살 가득한 남아프리카에서 왔습니다. 마직막 안녕하세요 햇살 가득한 남아프리카에서 왔습니다."; // all fine for this one also
// NSString * sampleWord = #"안녕하세요 햇살 가녕하세득한 남녕하세아프리카에서 왔습니다. 마직막 안녕하세요 햇녕하세살 가득한 남아프리카에서 왔습니다."; // here I finally start to see some result { 45, 5 }
NSRange range = [checker checkSpellingOfString:sampleWord
startingAt:0];
NSLog(#"range: %#", NSStringFromRange(range)); // Consistently prints { NSNotFound, 0 }
NSInteger wc;
// Since you force the language I think this
// is better way of checking
range = [checker checkSpellingOfString:sampleWord
startingAt:0
language:#"ko"
wrap:NO
inSpellDocumentWithTag:0
wordCount:& wc];
NSLog(#"spelling range: %# wc %lu", NSStringFromRange(range), wc); // Consistently prints { NSNotFound, 0 } wc 1
range = [checker checkGrammarOfString:sampleWord
startingAt:0
language:#"ko"
wrap:NO
inSpellDocumentWithTag:0
details:nil];
NSLog(#"grammer range: %# wc %lu", NSStringFromRange(range), wc); // Consistently prints { NSNotFound, 0 } wc 1
The word you are testing with 마직막 appears to be valid and passes consistently - even in the grammar test.
If I create some kind of a sentence and mess it up by randomly pasting in letters then I start getting some sensible (and consistent) feedback from the spell checker. See the code for more info.
Mostly I think you should use, as shown in the code, the message that allows you to specify the language rather than the one that does not since you are forcing the language. But I do not see inconsistency here. If I paste the word into Notes it also does not highlight it, although my intentionally erroneous string does light up there as well.
EDIT
I've played with your code a bit. Note that readline will append the newline so I changed the code by trimming all white space and newlines from theWord as below.
NSString *sampleWord = [[NSString stringWithCString:theWord encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
Initially I got the discrepancy you mention but just as I got excited it disappeared. So yes, it seems inconsistent. I am not sure if the spell checker got smarter or if the console got better at dealing with the input.
The basic problem, I believe, is that getline reads chars. So I also tried e.g.
NSString *sampleWord = [[[NSString alloc] initWithBytesNoCopy:theWord
length:len_read
encoding:NSUTF8StringEncoding
freeWhenDone:YES] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
but got the same result. Here I also tried
NSASCIIStringEncoding
and
NSUTF16StringEncoding
but only
NSUTF8StringEncoding
worked correctly, so it seems the console returns UTF8 strings. At least for me, I suspect this will be different based on the locale and other settings.
Discrepancy
So what about that. Yesterday when I ran my code, for the very first time, I think it produced a spelling error. Never again. I ran it a lot but never again and I am not even sure it really did produce that error anymore. Same today. I ran your code and the first time it marked the word as an error, but never again. By now, after running it again many times, I am starting to wonder if I was not mistaken as I simply can not generate it again.
Maybe the spell checker takes a while to initialise and this gives rise to the discrepancy. I could not find anything in the manual that points to this. Maybe the console gets smart as I enter Hangul and this is why it generates two separate outputs. Maybe the clipboard sees I am copy and pasting Hangul and changes something somewhere.
The strange thing though is that I repeated the whole thing on my MacBook as well, hoping to get the failure / misspelt word the first time again and this time copying the log out. But there it just worked from the first time.
Maybe run your code a few times and see if it stabilises after a while. Here it seems that (maybe) the first time it marks the word as misspelt but never again, so I am as perplexed as you are at present.
Follow up
Note that I am unable to get the spellchecker to mark the word as misspelt. I also tried the other words you gave and later even some gibberish but the spell checker never flagged anything as incorrect.
Then I tried on my Mac again and this time everything I tried was marked (correctly) as incorrect. So the result is not consistent between my Mac and Macbook, although, where it works, it seems that "마직막" is spelt correctly according to Apple.
I can not explain this but suspect it has to do with language and locale setting differences between the two machines, even though the code is the same and there are no differences I am aware of. On both machines the output is the same, notably it outputs YES when I initialise the Korean dictionary.

This is the code I'm using right now, where "마직막" is failing.
while(true) {
char *theWord = NULL;
size_t len = 0;
printf("ENTER A WORD\n");
size_t len_read = getline(&theWord, &len, stdin);
printf("%s\n", theWord);
NSString *sampleWord = [NSString stringWithCString:theWord encoding:NSUTF8StringEncoding];
NSInteger wc;
NSRange range = [checker checkSpellingOfString:sampleWord startingAt:0 language:#"ko" wrap:NO inSpellDocumentWithTag:0 wordCount:&wc];
if (range.location == NSNotFound) {
printf("Correct Spelling\n");
} else {
printf("Bad Spelling. Incorrect Spelling at\n");
NSLog(#"The range: %#", NSStringFromRange(range));
}
fflush(stdin);
free(theWord);
}
And if I input "마직막", this is what I'm getting.
ENTER A WORD
마직막
ㅁㅏㅈㅣㄱㅁㅏㄱ
Bad Spelling. Incorrect Spelling at
2020-11-27 10:39:23.916315+0900 spellCheckTest[6410:124279] The range: {0, 8}
So we're getting different results using the same input. Could this be because of how I'm accepting input from console??

Related

Addition of two numbers in Objective-C and displaying result

I have made a small app for iPhone, that can add two complex numbers(I have just started to learn).
In the program I can enter some number in the text field, program will process string and save numbers.
The problem for me is how to display addition of the two numbers.
What do I mean:
I have a function that can display real and imaginary part of true complex number instance, and it work fine:
- (void) functionDisplayInfo {
// number 1
if ([number1 functionProcessString:self.textEditComplexNumber1.text]) {
self.labelComplex1.text = [NSString stringWithFormat: #"true, %f, %f", number1.getRealValue, number1.getImgValue];
} else {
self.labelComplex1.text = #"false";
}
// number 2
if ([number2 functionProcessString:self.textEditComplexNumber2.text]) {
self.labelComplex2.text = [NSString stringWithFormat: #"true, %f, %f", number2.getRealValue, number2.getImgValue];
} else {
self.labelComplex2.text = #"false";
}
}
but when I try to do true addition of two real or imaginary parts of complex number it does not work. The code is similar to the code sample above. This is the code sample:
- (IBAction)functionAddition:(id)sender {
self.labelDisplayResult.text = [NSString stringWithFormat:#"result is: %f %f", number1.getRealValue + number2.getRealValue, number1.getImgValue + number2.getImgValue];
[self functionDisplayInfo];
}
This code displays only the 0.000.. and 0.000... when it should display result of addition.
Can somebody tell me what am I doing wrong. Thanks
Just a guess, as there is not enough information.
Reading your code it looks like the method functionProcessString: takes a string and parses a complex number from it. In functionDisplayInfo call this on number1 and number2 - so the method is misnamed, it parses and displays.
In your method functionAddition: you do the addition and then call functionDisplayInfo, this suggests you are doing the addition before the numbers have been parsed.
I suspect that whatever type number1 and number2 are it is initialised to zero and only changes after functionProcessString: is called, so doing the addition first gives you zero.
You might solve your problem by simply swapping the order of the two statements in functionAddition: so the parsing occurs first.
You might want to consider redesigning your code so that the parsing and display of your inputs is handled by separate methods. You can also do the same for output, separating the operation on the inputs from the display of its results. This might give you a cleaner design and avoid problems like you hit. E.g. under such a design your functionAddition: in pseudo-code would be:
- (IBAction)functionAddition:(id)sender
{
if ([self functionParseInput])
{
[self displayInput];
[self addInputs];
[self displayOutput];
}
}
HTH

RAM build up? adding zeros to a NSString

Hi i'm trying to get to NSStrings to equal each other in length for a project of mine but i'm seem to be having trouble. I got two string
NSString *word1=#"123456";
NSString *word2=#"123";
I hard coded them to be different lengths for other testing.I'm trying to append 0s at the from of word to so it'll look like #"000123" and i tried this method to append the string but instead it builds up ram and if i leave the program running long enough my system crashes..any help on how to fix this? is there a different method to doing this?
while ([word1 length]>[word2 length]){
[word2 stringByAppendingString:#"0"];
}
while ([word1 length]<[word2 length]){
[word1 stringByAppendingString:#"0"];
}
You are not changing the strings. NSString objects are immutable, you either have to assign the result:
word2 = [word2 stringByAppendingString:#"0"];
or use NSMutableString and appendString:
[word2 appendString:#"0"];
You also have to reverse your logic, as "append" means "add to the end".
append |əˈpɛnd|
verb [ with obj. ]
add (something) to the end of a written document: the results of the survey are appended to this chapter.
You need to change your code to this
while ([word1 length]>[word2 length]){
word2 = [word2 stringByAppendingString:#"0"];
}
while ([word1 length]<[word2 length]){
word1 = [word1 stringByAppendingString:#"0"];
}
I think the reason for the memory increase and app crash is because you are having an infinite loop in your old code. Try putting NSLog into the while loop in your old code and see what is the output. The code I suggested should not have such problem.

error with NSSring

I'm very new to objective-c and I'm getting a basic error and unsure on how to fix it.
for(ZBarSymbol *sym in syms) {
resultText.text = sym.data; //sym.data = 0012044012482
[self phpPost:(int)sym.data];
break;
}
}
- (void)phpPost: (int)barcode {
NSString *theValue = [NSString stringWithFormat:#"%#", barcode]; //problem line
labelScan.text = theValue;
//labelScan.text = #"Barcode scanned";
}
when i use #"%#" the text of the label is correct (0012044012482), but when i use #"%d" it isn't (random number every time i restart the app). I would like to use #"%#" (or something that works) but for some reason xCode is giving me an error. and I'm unsure on how to fix the error.
The error is:
Format specifies type 'id' but the argument has type 'int'
In the end I plan on having that code (sym.data) written to a MySQL database using the POST method.
You can't just convert it to an int by casting if it's an object (which it must be if the %# format specifier isn't causing a crash).
Assuming from the fact that you're assinging it directly to a label's text that it's an NSString, you should either change the parameter type of phpPost:
- (void)phpPost: (NSString *)barcode {
labelScan.text = barcode;
}
or extract the intValue before passing sym.data:
[self phpPost:[sym.data intValue]];
and then use the proper %d format specifier in phpPost:.
Your barcode isn't an int, it is an NSString. Instead of doing (int)sym.data, pass in [sym.data intValue]. That should correctly convert it to an integer.
The reason you get a random number is because you can't just cast a string object to a primitive data type :)
I don't know what type sym.data is, but it is likely a pointer to an object, and not the value itself. You cast that pointer to int, so when you are using %d you are effectively printing the memory location of the object. That is why it changes each time you run the program (Objective-C let's you do this without any warnings - something to watch out for).
To fix this, either extract the integer value you need from the sym.data object using it's properties; or pass the object as a pointer. For instance, you could try calling your method like this:
[self phpPost:sym.data];
And then change your method to be:
- (void)phpPost: (id)barcode {
NSString *theValue = [NSString stringWithFormat:#"%#", barcode];
labelScan.text = theValue;
}
Ok, I did some thinking while I was at work today, and I figured out that an INT isn't going to work for me. if I make that object to an int, I would loss some data that is vital to what I'm doing. eg. object=001234 int=1234. I need the zeros. So, in the end, I'm keeping it an object (string) and just passing it into the function.
Here is my code after I got it working correctly.
for(ZBarSymbol *sym in syms) {
resultText.text = sym.data;
[self phpPost:sym.data];
break;
}
}
- (void)phpPost: (NSString *)barcode {
labelScan.text = barcode;
//labelScan.text = #"Barcode scanned"; //My custon label
}
Thanks, everyone for your responses. Your answer will not go unused. I'm sure I'll be needing this information here soon.
O, if you see that I did this wrong, or not the correct way, please make a comment and tell me .

Multi-character character sequence in 3rd party library

I'm using a 3rd party library for an iOS project I work on, and I'm down to one warning left in the project, namely on this line of code
[NSNumber numberWithUnsignedLongLong:'oaut']
And the warning is
Multi-character character constant
I suck at C, so I don't know how to fix this, but I'm sure the fix is relatively easy. Help?
EDIT: More context.
#implementation MPOAuthCredentialConcreteStore (KeychainAdditions)
- (void)addToKeychainUsingName:(NSString *)inName andValue:(NSString *)inValue {
NSString *serverName = [self.baseURL host];
NSString *securityDomain = [self.authenticationURL host];
// NSString *itemID = [NSString stringWithFormat:#"%#.oauth.%#", [[NSBundle mainBundle] bundleIdentifier], inName];
NSDictionary *searchDictionary = nil;
NSDictionary *keychainItemAttributeDictionary = [NSDictionary dictionaryWithObjectsAndKeys: (id)kSecClassInternetPassword, kSecClass,
securityDomain, kSecAttrSecurityDomain,
serverName, kSecAttrServer,
inName, kSecAttrAccount,
kSecAttrAuthenticationTypeDefault, kSecAttrAuthenticationType,
[NSNumber numberWithUnsignedLongLong:"oaut"], kSecAttrType,
[inValue dataUsingEncoding:NSUTF8StringEncoding], kSecValueData,
nil];
if ([self findValueFromKeychainUsingName:inName returningItem:&searchDictionary]) {
NSMutableDictionary *updateDictionary = [keychainItemAttributeDictionary mutableCopy];
[updateDictionary removeObjectForKey:(id)kSecClass];
SecItemUpdate((CFDictionaryRef)keychainItemAttributeDictionary, (CFDictionaryRef)updateDictionary);
[updateDictionary release];
} else {
OSStatus success = SecItemAdd( (CFDictionaryRef)keychainItemAttributeDictionary, NULL);
if (success == errSecNotAvailable) {
[NSException raise:#"Keychain Not Available" format:#"Keychain Access Not Currently Available"];
} else if (success == errSecDuplicateItem) {
[NSException raise:#"Keychain duplicate item exception" format:#"Item already exists for %#", keychainItemAttributeDictionary];
}
}
}
EDIT 2: They were attempting to meet the requirements of this by creating that NSNumber:
#constant kSecAttrType Specifies a dictionary key whose value is the item's
type attribute. You use this key to set or get a value of type
CFNumberRef that represents the item's type. This number is the
unsigned integer representation of a four-character code (e.g.,
'aTyp').
In C and Obj-C the single-quote ' is used only for single-character constants. You need to use the double-quote: "
Like so:
[NSNumber numberWithUnsignedLongLong:"oaut"]
That covers the warning, but there's also a semantic issue here. Although a single character constant, such as 'o', can be treated as an integer (and can be promoted to an unsigned long long), a "string" (char * or char []) cannot, which means you can't use "oaut" as an argument to numberWithUnsignedLongLong:
Update:
I guess the four-character code is supposed to be treated as an integer, i.e., the 8 bits of each char put in place as if they together were a 32-bit int:
char code[] = "oaut";
uint32_t code_as_int = code[0] | (code[1] << 8) | (code[2] << 16) | (code[3] << 24);
[NSNumber numberWithUnsignedLongLong:code_as_int]
although I'm not sure which endianness would be expected here, nor why this is calling for an unsigned long long, unless just to be certain there are enough bits.
Rudy's comment, now that I think of it, is correct -- multi-character constants are allowed by some compilers for exactly this purpose (it is "implementation-defined" behavior).
'oaut' (single quotes) is a character, so the compiler tries to interpret it as a multi-byte character but can't make any sense of it. That explains the error message.
I guess that if you gave a proper string, like #"oaut", you'd get another error message, since numberWithUnsignedLongLong: expects an unsigned long long, not a string or a character. Are you trying to pass a variable with the name "oaut"? If so, use
[NSNumber numberWithUnsignedLongLong: oaut];
If not, then please explain what "oaut" is.
Edit
'oaut' may actually be the original value. There are/were multi-character character constants in C. Using a (4 byte) char, used as int and promoted to unsigned long long would then be possible. This must be old code. It seems such code was accepted by CodeWarrior.
Assuming that really a multi-char char const was meant, 'oaut' looks like a "magic number" and this value was chosen because it is the beginning of "oauth". I guess it should either be value 0x6F617574 or 0x7475616F.
#Josh Caswell 's answer is partially right, the simplest and "official" solution is:
[NSNumber numberWithUnsignedInt:'oaut']
unsigned int's length is 32-bit in both 32-bit and 64-bit cpu, there's a practical example from Apple: https://developer.apple.com/library/ios/samplecode/CryptoExercise/Listings/Classes_SecKeyWrapper_m.html

Questions about validating user input in Objective-C, number vs string

Why 'exactly' does this code loop endlessly if you enter a non number character?
The first question comes about because I want to learn good defensive coding. Does anyone know a good way to check user input? My google-fu failed me. Some people seemed to be of the opinion that if I specify %f in scanf that I am 'demanding' a float; I verified this, in a way, by printing the value of userInput. In fact, if I comment out the do while loop, there is 'no problem' with the execution of the code. It assigns a 0 to userInput and goes about its business.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
float userInput;
float result;
NSLog(#"3X^3 -5x^2 + 6");
do {
NSLog(#"What is x?");
scanf("%f", &userInput);
NSLog(#"userInput = %f", userInput);
} while(userInput == 0);
result = 3 * (userInput * userInput * userInput) - 5 * (userInput * userInput) + 6;
NSLog(#"the result is: %f", result);
[pool drain];
return 0;
}
This is really nothing to do with Objective-C or Cocoa. The issue is simply to do with the use of the standard C library function scanf, and handling the error condition. From the scanf manpage, describing the return code:
Zero indicates that, although there was input available, no conversions were assigned; typically this is due to an invalid input character, such as an alphabetic character for a `%d' conversion.
A valid numeric input can be parsed by scanf with the %f specifier, so that obviously works as expected. But if you enter in a non-numeric character, scanf cannot convert this to a float, and leaves the text in the buffer of stdin. Since the code is not checking the return code from scanf, and only testing if userInput is non-zero, the loop will never exit, as userInput happens to start at 0.0, and will never be updated as scanf will not pull the non-numeric characters out of the stdin buffer. So that is why your code runs in an infinite loop.
If you had initialized userInput to a non-zero value, that would fix the problem one way, as non-numeric input would cause scanf to fail and the while condition would be triggered. But a better fix would be to check the return code of scanf. If it is zero, print an error message, and do a fpurge(stdin) to clear out the invalid input before you loop around again, like this:
int rc = scanf("%f", &userInput);
if (rc == 0)
{
NSLog(#"Invalid input, try again.");
fpurge(stdin);
}
So this is the plain C approach to input and parsing. The bottom line for defensive coding is that you should always check the return code!
As Chris mentions, for an actual Cocoa application, you would want to look at NSNumberFormatter and the like, but then you would presumably be taking input from widgets rather than file streams, so the code would be quite different to the above.
The proper way to validate user input in Cocoa is to use an instance of an appropriate subclass of NSFormatter, in this case something like NSNumberFormatter.