How to create an NSString with one random letter? - cocoa

I need to create an NSString that has a single random uppercase letter.
I can get random int's fine, and I could construct a C string from it and then make the NSString, but I imagine there has to be a better and more cocoa-ish way.
Thanks!

You can just make an NSString containing what you consider to be letters and pull a random character from it. Here's an example category:
#implementation NSString(RandomLetter)
- (NSString *)randomLetter {
return [self substringWithRange:[self rangeOfComposedCharacterSequenceAtIndex:random()%[self length]]];
}
#end
(You'll need to srandom(time()) at some point, obviously. Maybe include an initialize method in your category.)

I think the best way is to use a c string so that you can use an explicit encoding. Here's an example of that:
NSInteger MyRandomIntegerBetween(NSInteger min, NSInteger max) {
return (random() % (max - min + 1)) + min;
}
NSString *MyStringWithRandomUppercaseLetter(void) {
char string[2] = {0, 0};
string[0] = MyRandomIntegerBetween(65, 90); /// 'A' -> 'Z' in ASCII.
return [[[NSString alloc] initWithCString:string encoding:NSASCIIStringEncoding] autorelease];
}
Here's an alternative, that's really pretty much the same thing, but avoids the C string.
NSString *MyStringWithRandomUppercaseLetter(void) {
unichar letter = MyRandomIntegerBetween(65, 90);
return [[[NSString alloc] initWithCharacters:&letter length:1] autorelease];
}
I prefer the explicit character encodings in the first approach, but they're both correct.

Related

NSString to NSArray and iterate

I have a code that creates an NSString and separates value with a comma. What I need to do is take that string, convert it to a NSArray and separate each value from the NSString between the commas, then iterate through each one. The code below returns the array as a single string
NSString *emails = #"testemail#gmail.com, testemail2#gmail.com";
NSArray *listItems = [emails componentsSeparatedByString:#", "];
for (int i = 0; i < [listItems count]; i++) {
NSString *address = (NSString*) [listItems objectAtIndex:i];
NSLog (#"ADDRESS: %#", address);
The Log shows the response testemail#gmail.com, testemail2#gmail.com when I want it to separate each one individually. Should this be an NSMutableArray instead?
The code is correct. You'll get two (separate) lines starting with ADDRESS:
But nowadays (actually since 2009!) it's highly recommended to use Fast Enumeration if the loop index is not needed
NSString *emails = #"testemail#gmail.com, testemail2#gmail.com";
NSArray *listItems = [emails componentsSeparatedByString:#", "];
for (NSString *email in listItems) {
NSLog(#"ADDRESS: %#", email);
}

NSTextView + NSTextFinder + my separate NSSearchField

I am trying to implement searching in NSTextView with search query coming from my custom NSSearchField.
Sounds pretty simple, but I cannot get it working.
So far I've looked through all the Apple Documentation about NSTextFinder, its client and FindBarContainer. The TextFinder simply provides the FindBarView to the container, and container shows it when you activate searching.
All the communication between the client, container and TextFinder is hidden. It just looks like a black-box that is designed to work "as is" without any customisation or interference.
But what about - (void)performAction:(NSTextFinderAction)op method of NSTextFinder? Isn't it for sending custom commands to the TextFinder?
I was trying to assign a new search string to it with the following:
NSPasteboard* pBoard = [NSPasteboard pasteboardWithName:NSFindPboard];
[pBoard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, NSPasteboardTypeTextFinderOptions, nil] owner:nil];
[pBoard setString:_theView.searchField.stringValue forType:NSStringPboardType];
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSTextFinderCaseInsensitiveKey,
[NSNumber numberWithInteger:NSTextFinderMatchingTypeContains], NSTextFinderMatchingTypeKey,
nil];
[pBoard setPropertyList:options forType:NSPasteboardTypeTextFinderOptions];
[textFinder performAction:NSTextFinderActionSetSearchString];
but that doesn't work and simply breaks the normal findBar operation.
I have a strong feeling that I am doing something wrong.
All I want is to have a standard search functionality in my own NSSearchField. Is that possible?
I bet I am not the first one who is not happy with normal findBar.
Your help is very needed and appreciated!
For an NSTextView, NSTextFinder is mostly just a user interface for NSString's func range(of searchString: String, options mask: NSString.CompareOptions = [], range rangeOfReceiverToSearch: NSRange) -> NSRange
If you want to implement your own search on an NSTextView, use that. To search forward, you construct a range starting at the end of the current selections's range and going to the end of the NSTextView's text. To search backward, construct a range starting at 0 and going to the beginning of the current selection's range, and tell NSString to use backwards search.
If NSString returns a .notFound range, implement wrap-around yourself.
If you need startsWith, endsWith or wholeWord you'll need to take the result NSString's func gives you, check to see if it will do, and if not adjust the range and call it again.
You can use NSComboBox. Return search value using below delegate:
- (NSString *)comboBox:(NSComboBox *)aComboBox completedString:(NSString *)substring
{
if ([aComboBox tag] == 101 || [aComboBox tag] == 102) {
NSArray *currentList;
if ([aComboBox tag] == 101) {
NSArray *keyArray = keySuggestions;
currentList = keyArray;
} else {
currentList = [NSArray arrayWithArray:self.valueSuggestions];
}
NSEnumerator *theEnum = [currentList objectEnumerator];
id eachString;
NSInteger maxLength = 0;
NSString *bestMatch = #"";
while (nil != (eachString = [theEnum nextObject])) {
NSString *commonPrefix = [eachString
commonPrefixWithString:substring options:NSCaseInsensitiveSearch];
if ([commonPrefix length] >= [substring length] && [commonPrefix
length] > maxLength)
{
maxLength = [commonPrefix length];
bestMatch = eachString;
break;
}
}
return bestMatch;
}
return substring;
}

Remarkable behaviour by NSScanner

I have the following string in an NSTextView:
Horus avenged his father Osiris
There are two tags assigned to this, Horus and Osiris. I use NSScanner to scan the string for both tags and highlight them with a yellow background when found.
The code is:
for (Tag *aTag in tags) {
NSString *aTagName = [aTag name];
NSUInteger strLength = [aTagName length];
NSScanner *aScanner = [[NSScanner alloc] initWithString: aString];
[aScanner setCaseSensitive: YES];
[aScanner setScanLocation: 0];
BOOL result = [aScanner scanUpToString: aTagName intoString: nil];
while (![aScanner isAtEnd]) {
NSUInteger position = [aScanner scanLocation];
if (result) {
NSRange aRange = NSMakeRange(position, strLength);
[storage removeAttribute: NSBackgroundColorAttributeName range: aRange];
if (onOrOff) {
[storage addAttribute: NSBackgroundColorAttributeName value: aColor range: aRange];
}
position = position + [aTagName length];
[aScanner setScanLocation: position];
}
[aScanner scanUpToString: aTagName intoString: nil];
}
}
This fails when the frist tag (in the above example it's Horus) is located at the very beginning (location = 0) of the scanned string. The tag is not found and the code keeps looping.
However, when I replace the string with:
After 10 years Horus avenged his father Osiris
... it all works and both my tags are found (as expected) and nicely highlighted.
Am I missing something or is this a bug in NSScanner?
See the docs for
- (BOOL)scanUpToString:(NSString *)stopString intoString:(NSString **)stringValue
If stopString is the first string in the receiver, then the method returns NO and stringValue is not changed.
The scanner has found the string, but returned NO, because it has not scanned through any characters to do so. Your code then doesn't increase the position variable, because that part is within the if (result) block.
I think removing the if statement should fix the problem. If the scanner didn't find the stopString, the while condition will fail and the code won't get into the loop at all.

Check for String within a String

I'm trying to compare two strings
NSString strOne = #"Cat, Dog, Cow";
NSString strTwo = #"Cow";
How do I determine if strOne contains strTwo
Try using rangeOfString:
NSRange result = [strOne rangeOfString:strTwo];
From the documentation:
Returns an NSRange structure giving the location and length in the receiver of the first occurrence of aString. Returns {NSNotFound, 0} if aString is not found or is empty (#"").
For anyone needing the code to check is a string exists within a string, here's my code thanks to fbrereto. This example checks to see if any string contained in an array of strings (stringArray) can be found within a string (myString):
int count = [stringArray count];
for (NSUInteger x = 0; x < count; ++x) {
NSRange range = [self.myString rangeOfString:[stringArray objectAtIndex:x]];
if (range.length > 0) {
// A match has been found
NSLog(#"string match: %#",[stringArray objectAtIndex:x]);
}
}
I believe this is the correct syntax for checking if the range exists (correcting response from Kendall):
range.location != NSNotFound
Gradually straying off topic, but I always explode my strings, which would mean just exploding it using your search string as a key and you can use the array count to see how many instances you have.
Just incase anyone is coming from a code language that uses "explode" to blow a string up into an array like me, I found writing my own explode function tremendously helpful, those not using "explode" are missing out:
- (NSMutableArray *) explodeString : (NSString *)myString key:(NSString*) myKey
{
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSRange nextBreak = [myString rangeOfString:myKey];
while(nextBreak.location != NSNotFound)
{
[myArray addObject: [myString substringToIndex:nextBreak.location]];
myString = [myString substringFromIndex:nextBreak.location + nextBreak.length];
nextBreak = [myString rangeOfString:myKey];
}
if(myString.length > 0)
[myArray addObject:myString];
return myArray;
}
works like this:
[self explodeString: #"John Smith|Age: 37|Account Balance: $75.00" key:#"|"];
which will return this array:
[#"John Smith", #"Age: 37", #"Account Balance: $75.00"];
This lets you quickly pull out a specific value in a tight space, Like if you have a client and you want to know how much money he has:
[[self explodeString: clientData key: pipe] objectAtIndex: 1];
or if you wanted specifically the dollar amount as a float:
[[[self explodeString: [[self explodeString: clientData key: pipe] objectAtIndex: 1] key: #": "] objectAtIndex: 2] floatValue];
anyway I find arrays way easier to work with and more flexible, so this is very helpful to me. Additionally with a little effort you could make an "explodable string" data type for your private library that lets you treat it like a string or return an index value based on the key
ExplodableString *myExplodableString;
myExplodableString.string = #"This is an explodable|string";
NSString *secondValue = [myExplodableString useKey: #"|" toGetValue: index];

Check NSString for special characters

I want to check an NSString for special characters, i.e. anything except a-z, A-Z and 0-9.
I don't need to check how many special characters are present, or their positions, I just need to know whether a particular string contains any or not. If it does, then I want to be able to display "Error!", or something similar.
For example, jHfd9982 is OK but asdJh992# is not.
Also, letters with accents, diacritics, etc. should not be allowed.
How would I go about this?
Thanks!
Michael
NSCharacterSet * set = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
if ([aString rangeOfCharacterFromSet:set].location != NSNotFound) {
NSLog(#"This string contains illegal characters");
}
You could also use a regex (this syntax is from RegexKitLite: http://regexkit.sourceforge.net ):
if ([aString isMatchedByRegex:#"[^a-zA-Z0-9]"]) {
NSLog(#"This string contains illegal characters");
}
Here the code you can use it to check the string has any special character or not
NSString *string = <your string>;
NSString *specialCharacterString = #"!~`##$%^&*-+();:={}[],.<>?\\/\"\'";
NSCharacterSet *specialCharacterSet = [NSCharacterSet
characterSetWithCharactersInString:specialCharacterString];
if ([string.lowercaseString rangeOfCharacterFromSet:specialCharacterSet].length) {
NSLog(#"contains special characters");
}
You want to search NSString using a character set if it cant find any characters in the string then rangeOfCharacterFromSet: will return a range of {NSNotFound, 0}
The character set would be like [NSCharacterSet symbolCharacterSet] or your own set. Note you can also invert character sets so you could have a set of acceptable characters
If you want to remove special characters & numbers from any string (even textfield text), while you are editing, these lines below are quite handy:
#define ACCEPTABLE_CHARACTERS #"!~`##$%^&*-+();:=_{}[],.<>?\\/|\"\'0123456789"
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSCharacterSet *cs = [NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:#""];
return (![string isEqualToString:filtered]) ? NO : YES;
}
This code allows only numbers in UITextField input.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ([string rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]].location != NSNotFound)
return NO;
else
return YES;
}

Resources