unichar comparision in objective c - objective-c

I need to implement a method, which compares two strings for equality, considering some turkish letters as latin(e.g. ı = i). That's bottleneck in program, so it needs to be implemented as efficient as possible.
I can't use NSString compare: withOption:nsdiactricinsensitivesearch, because it doesn't work with turkish letters correctly.
Here's the implementation of my algorithm:
- (NSComparisonResult) compareTurkishSymbol:(unichar)ch with:(unichar)another
{
//needs to be implemented
//code like: if (ch == 'ı') doesn't work correctly
}
- (NSComparisonResult)compareTurkish:(NSString*)word with:(NSString*)another
{
NSUInteger i;
for (i =0; i < word.length; ++i) {
NSComparisonResult result =[self compareTurkishSymbol:[word characterAtIndex:i] with:[another characterAtIndex:i]];
if (result != NSOrderedSame) {
return result;
}
}
return another.length > word.length ? NSOrderedDescending : NSOrderedSame;
}
The problem is I can't compare unichars correctly. It doesn't compare correctly non-ascii symbols. How to deal with that?

Finally I found an answer.
unichar is unsigned short, that means every symbol has its code. So we can compare them not as chars but as numbers.
- (NSComparisonResult) compareTurkishSymbol:(unichar)ch with:(unichar)another
{
if (ch == 305) {//code of 'ı'
ch = 'i';
}
return ch - another;
}

Related

Count number of zero(0) using objective-c

I want to count how many number of zero(0) before numeric number. Because I need to save those number which is present before numeric.
Exam:- suppose I have a number 0000102. So I want to calculate how many zero(0) before numeric start. In this exam we are see here is 4 zero's(0) are present. It is possible to calculate this?
for (i=0;i<string.length;i++)
{
if ([[string characterAtIndex:i] intValue] <= 9 || [[string characterAtIndex:i] intValue] > 0 )
{
i++;
}
else
{
numerOfZeros++;
}
}
int count = 0;
NSString *strr = #"0000102";
unichar findC;
for (int i = 0; i<strr.length; i++)
{
findC = [strr characterAtIndex:i];
if (findC == '0')
{
count++;
}
else
break;
}
NSLog(#"%d",count);
Recursive approach for a one liner:
#implementation NSString (category)
- (NSUInteger)zeroPrefixCount
{
return [string hasPrefix:#"0"] ? 1 + [[string substringFromIndex:1] zeroPrefixCount] : 0;
}
#end
This is not an optimal solution, performance-wise, but it's typical of your first days at programming classes.
usage
// x will be 4
NSUInteger x = [#"0000102" zeroPrefixCount];
I recommend you to save this kind of numbers as String itself and no need to further evaluate how many zeros are there rather do a string comparison if needed.
If you really want to count zeros in your number then you can consider converting it to a string and use NSRange and NSString helper methods to get what you want. Similar situation is answered here.
search if NSString contains value

How to check if an NSString contains fancy characters?

I have a game that renders the player's nickname.
Normally, I use a nice, styled, bitmap font to render the nickname. However, I only have bitmaps for "normal" characters - A,B,C,...,1,2,3,...!##$%^,.... There are no bitmaps for Chinese, Japanese or whatever other "fancy" characters in any other language.
Trying to render such text with a bitmap will crash because I don't supply such bitmaps. Therefore I decided to detect whether the given string was a "fancy" string, and if that was the case, render the nickname using some generated system font.
How can I detect if a string has fancy characters? My current solution is something like
-(BOOL)isNormalText:(NSString *)text {
char accepted[] = {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!##$%^&*()_+{}/\\\"\'?.,"};
for (int i = 0; i < [text length]; ++i) {
char character = [text characterAtIndex:i];
BOOL found = NO;
for (int j = 0; j < 84 && !found; ++j) {
char acceptedChar = accepted[j];
if (character == acceptedChar) {
found = YES;
}
}
if (!found) {
return NO;
}
}
return YES;
}
Which does NOT work, I think. Because a fancy character is not one character - it is a sequence like "\u123".
I have seen a question, in Java, about something similar here: How to check if the word is Japanese or English?
They check if the character value is within the 255 range. But when I do this check in Objective-C, it tells me it is redundant because a char will always be within such range - which makes sense as I imagine the fancy characters to be actually a sequence like "\u123"...
Use an NSCharacterSet, fill it with the characters that you have bitmaps for, then invert the set so that it represents all characters that you don't have. Then use -[NSString rangeOfCharacterFromSet:]. If it returns NSNotFound then the string contains only valid characters.
Just as an example to illustrate what I mean:
- (BOOL) isNormalText:(NSString *) str
{
if (str == nil)
return NO;
NSCharacterSet *allowedChars = [NSCharacterSet characterSetWithCharactersInString:#"ABCDEFG"];
NSCharacterSet *notAllowedChars = [allowedChars invertedSet];
return [str rangeOfCharacterFromSet:notAllowedChars].location == NSNotFound;
}
Use regular expression checking
-(BOOL)isNormalText:(NSString *)text {
NSString * regex = #"(^[A-Za-z0-9]*$)";
NSPredicate * pred = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
BOOL isMatch = [pred evaluateWithObject:text];
return isMatch;
}

Objective C Switch on Character

I've been trying to find a better way to switch on each character of a string.
My existing code is:
NSUInteger len = [oldName length], i;
SEL xSelector = #selector(characterAtIndex:);
unichar (*charAtIdx)(id, SEL, NSUInteger) = (typeof (charAtIdx)) [oldName methodForSelector:xSelector];
NSMutableString *NewName = [NSMutableString new];
for (i=0 ; i<len ; i++){
unichar c = charAtIdx(oldName,xSelector,i);
if (c == "Ú" || c == "°"){
[NewName appendString:#"s"];
}
else if (c == "Û" || c == "”"){
[NewName appendString:#"s"];
}
else if (c == "◊" || c == "˜"){
[NewName appendString:#"x"];
}
else blablabla
}
return NewName;
Now, the above seems to be working, however i have about 50 if statements that "switch" mainly extended ASCII codes (character codes 128-255) to more meaningful ones.
I thought about using a switch statement with a typedef enum and switch on that, however, the below doesn't work:
typedef enum {·,¡,Ê,∆} ExtendedASCII;
The idea would be to replace "unichar c = charAtIdx(oldName,xSelector,i);" with the below:
ExtendedASCII c = charAtIdx(oldName,xSelector,i);
Switch c
case 0: //being ·
case 1: // being ¡
blablabla
Any ideas????
thanks,
alex
usualy you could do
switch(c)
{
case:'a':; // fall throught
case:'b':
{
[NewName appendString:#"x"];
break;
}
}
but if you use 'Ú' you will get a compiler error, because this "char" is no unichar.
you can try to set the int value for the char
switch(c)
{
case: 218:; // 'Ú' , fall throught
case: 186: // '°'
{
[NewName appendString:#"x"];
break;
}
...
}
I cannot test, because I cannot get a valid 'Ú'.
Hoping for comments, maybe I can extend and answer upcomming questions ;)

Matching strings, consider some characters are the same

please help me with this problem.
I want to check if the targetString match the keyword or not. Consider some character may different, but should still return true.
Example:
targetString = #"#ß<"
keyword = #"abc", #"∂B(", #"#Aß<"
result: all must return true.
(Matched.targetString and all keyword are the same.)
Consider me have an array, contains list of character set that can be the same:
NSArray *variants = [NSArray arrayWithObjects:#"aA#∂", #"bBß", #"c©C<(", nil]
So that when matching, with this rule, it can match as the example above.
Here is what i've done so far (using recursion):
- (BOOL) test:(NSString*)aString include:(NSString*) keyWord doTrim:(BOOL)doTrim {
// break recursion.
if([aString length] < [keyWord length]) return false;
// First, loop through each keyword's character
for (NSUInteger i = 0; i < [keyWord length]; i++) {
// Get #"aA#∂", #"bBß", #"c©C<(" or only the character itself.
// like, if the keyword's character is A, return the string #"aA#∂".
// If the character is not in the variants set, eg. P, return #"P"
char c = [keyWord characterAtIndex:i];
NSString *rs = [self variantsWithChar:c];
// Check if rs (#"aA#∂" or #"P") contains aString[i] character
if([rs rangeOfString:[NSString stringWithCharacters:[aString characterAtIndex:i] length:1]].location == NSNotFound) {
// If not the same char, remove first char in targetString (aString), recursion to match again.
return [self test:[aString substringFromIndex:1] include:keyWord doTrim:NO];
}
}
// If all match with keyword, return true.
return true;
}
- (NSString *) variantsWithChar:(char) c {
for (NSString *s in self.variants) {
if ([s rangeOfString:[NSString stringWithFormat:#"%c",c]].location != NSNotFound) {
return s;
}
}
return [NSString stringWithFormat:#"%c", c];
}
The main problem is, variantsWithChar: doesn't return the correct string. I don't know which datatype and which function should I use here. Please help.
For thou who know ruby, here's the example in ruby. It work super fine!
require 'test/unit/assertions'
include Test::Unit::Assertions
class String
def matching?(keyword)
length >= keyword.length && (keyword.chars.zip(chars).all? { |cs| variants(cs[0]).include?(cs[1]) } || slice(1, length - 1).matching?(keyword))
end
private
VARIANTS = ["aA#∂", "bBß", "c©C<("]
def variants(c)
VARIANTS.find { |cs| cs.include?(c) } || c
end
end
assert "abc".matching?("#ß<")
PS: The fact is, it's containt a japanese character set that sounds the same (like あア, いイ... for thou who know japanese)
PS 2: Please feel free to edit this Question, since my engrish is sooo bad. I may not tell all my thought.
PS 3: And, maybe some may comment about the performance. Like, search about 10,000 target words, with nearly 100 variants, each variant have at most 4 more same characters.
So first off, ignore comments about ASCII and stop using char. NSString and CFString use unichar
If what you really want to do is transpose hiragana and katakana you can do that with CFStringTransform()
It wraps the ICU libraries included in OS X and iOS.
It makes it very simple.
Search for that function and you will find examples of how to use it.
After a while (a day) working on the code above, I finally get it through. But don't know about the performance. Someone comment and help me improve about performance, please. Thanks.
- (BOOL) test:(NSString*)aString include:(NSString*) keyWord doTrim:(BOOL)doTrim {
// break recursion.
if([aString length] < [keyWord length]) return false;
// First, loop through each keyword's character
for (NSUInteger i = 0; i < [keyWord length]; i++) {
// Get #"aA#∂", #"bBß", #"c©C<(" or only the character itself.
// like, if the keyword's character is A, return the string #"aA#∂".
// If the character is not in the variants set, eg. P, return #"P"
NSString* c = [NSString stringWithFormat:#"%C", [keyWord characterAtIndex:i]];
NSString *rs = [self variantsWithChar:c];
NSString *theTargetChar = [NSString stringWithFormat:#"%C", [aString characterAtIndex:i]];
// Check if rs (#"aA#∂" or #"P") contains aString[i] character
if([rs rangeOfString:theTargetChar].location == NSNotFound) {
// If not the same char, remove first char in targetString (aString), recursion to match again.
return [self test:[aString substringFromIndex:1] include:keyWord doTrim:NO];
}
}
// If all match with keyword, return true.
return true;
}
If you remove all comment, it'll be pretty short...
////////////////////////////////////////
- (NSString *) variantsWithChar:(NSString *) c{
for (NSString *s in self.variants) {
if ([s rangeOfString:c].location != NSNotFound) {
return s;
}
}
return c;
}
You could try comparing ascii values of the japanese characters in the variants's each character's ascii value. These japanese characters aren't treated like usual characters or string. Hence, string functions like rangeOfString won't work on them.
to be more precise: have a look at the following code.
it will search for "∂" in the string "aA#∂"
NSString *string = #"aA#∂";
NSMutableSet *listOfAsciiValuesOfString = [self getListOfAsciiValuesForString:string]; //method definition given below
NSString *charToSearch = #"∂";
NSNumber *ascii = [NSNumber numberWithInt:[charToSearch characterAtIndex:0]];
int countBeforeAdding = [listOfAsciiValuesOfString count],countAfterAdding = 0;
[listOfAsciiValuesOfString addObject:ascii];
countAfterAdding = [listOfAsciiValuesOfString count];
if(countAfterAdding == countBeforeAdding){ //element found
NSLog(#"element exists"); //return string
}else{
NSLog(#"Doesnt exists"); //return char
}
===================================
-(NSMutableSet*)getListOfAsciiValuesForString:(NSString*)string{
NSMutableSet *set = [[NSMutableSet alloc] init];
for(int i=0;i<[string length];i++){
NSNumber *ascii = [NSNumber numberWithInt:[string characterAtIndex:i]];
[set addObject:ascii];
}
return set;
}

Trying to work with NSString but I'm having problems comparing two strings

I have a simple program that I'm testing a printer class in.
-(void) setInkType {
NSMutableString *theInkType;
InkType typeOfInk;
char inkFromInput[50];
NSLog(#"What type of ink are you using?");
NSLog(#"Options are photoInk, lazerJet, regularInk");
fgets(inkFromInput,50,stdin);
theInkType = [[NSMutableString alloc] initWithUTF8String:inkFromInput];
NSLog(#"%#",theInkType);
if([theInkType compare: #"photoInk"]==true) {
typeOfInk.photoInk = 564;
NSLog(#"Your using a photo ink of type %d",typeOfInk.photoInk);
inkType.photoInk = typeOfInk.photoInk;
}
else { if ([theInkType compare: #"lazerJet"] == true) {
typeOfInk.lazerJet = 94;
NSLog(#"Your using a lazer toner of type %d",typeOfInk.lazerJet);
inkType.lazerJet = typeOfInk.lazerJet;
}
else { if ([theInkType compare: #"regularInk"] == true) {
typeOfInk.regularInk = 910;
NSLog(#"Your using a regular ink of type %d",typeOfInk.regularInk);
inkType.regularInk = typeOfInk.regularInk;
}
}
}
}
When I run this I can enter in "photoInk" and "lazerInk" and I get a proper output. Why is it when I type "regularInk" I get a bad output?
I'm thinking it could be my {}'s but I'm not quite sure. I've been scratching my head for a few hours at this.
If there is anymore Cocoa flavoring I can do to make this look smoother let me know too please.
-compare: doesn't return a boolean true/false value, it returns an NSComparisonResult, which is either NSOrderedAscending, NSOrderedSame, or NSOrderedDescending.
So you could do this:
if ([theInkType compare: #"photoInk"] == NSOrderedSame)
But really, the -isEqual: method is closer to your true intention.
if ([theInkType isEqual: #"photoInk"])
Also: you're doing your else clauses wrong. Not this:
if (x) {
...
}
else { if (y) {
...
} }
But this:
if (x) {
...
} else if (y) {
...
}
I think this should work for you. This is my answer which I have taken from the link:
Comparing text in UITextView?
SOLUTION-1: I have modified it here a bit to make it more easier for your case:
Let us assume String1 is one NSString.
//Though this is a case sensitive comparison of string
BOOL boolVal = [String1 isEqualToString:#"My Default Text"];
//Here is how you can do case insensitive comparison of string:
NSComparisonResult boolVal = [String1 compare:#"My Default Text" options:NSCaseInsensitiveSearch];
if(boolVal == NSOrderedSame)
{
NSLog(#"Strings are same");
}
else
{
NSLog(#"Strings are Different");
}
Here if boolVal is NSOrderedSame then you can say that strings are same else they are different.
SOLUTION-2: Also you don't find this easy, you can refer to Macmade's answer under the same link.
Hope this helps you.
Hope this helps you.