NSPredicate Detect First & Last Name - objective-c

I am trying to use NSPredicate to evaluate whether or not a NSString has both a first and last name (Essentially a space between two non-digit words). This code hasn't been working for me (Code taken & modified slightly from: What are best practices for validating email addresses in Objective-C for iOS 2.0?:
-(BOOL) validName:(NSString*) nameString {
NSString *regExPattern = #"[A-Z]+_[A-Z]";
NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:regExPattern options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger regExMatches = [regEx numberOfMatchesInString:nameString options:0 range:NSMakeRange(0, [nameString length])];
if (regExMatches == 0) {
return NO;
} else
return YES;
}
}
I think there is something wrong with my regEx pattern, but I'm not sure how to fix it. This is how I check the string:
if([self validName:nameTextField.text]) {
// Valid Name
} else {
// Name no valid
}

First, if you want to match a space, then just put a space in the regex pattern. The underscore you have now will require an underscore in your name field in order to match.
Second, NSPredicate matches the whole string against the regex, so the pattern would not catch normal last names (which have more than one character), even with the space. You'll need to add some expression that covers the last part of the name.
Third, since you pass the text field directly into the check, you are putting some pressure on your users to type everything like you expected. You might want to clean the string a bit first, before testing. Personally, I would at least trim the string for spaces and replace multiple spaces with a single one.
Here is some code that does this:
NSString *regExPattern = #"[A-Z]+ [A-Z]+"; //Added a "+" to match the whole string up to the end.
Check:
NSString *name = nameTextField.text;
name = [name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
name = [name stringByReplacingOccurrencesOfString:#" +"
withString:#" "
options:NSRegularExpressionSearch
range:NSMakeRange(0, name.length)];
if([self validName: name]) {
// Valid Name
} else {
// Name no valid
}
As you can imagine there are many ways to do this, but this is a start. You should consider your test for "correct" names, though, as there are many names that won't pass you simple regex, for instance names with apostrophes and accents, for instance:
Jim O'Malley
Zoë Jones
etc.

If you just want to check for the space-separated fore- and surname, I would try this:
- (BOOL)validName:(NSString*)name
{
NSArray *components = [name componentsSeparatedByString:#" "];
return ([components count] >= 1);
}
This will check if you've at least two components separated by a space. This will also work for names with 3 or more components (middle names).

Related

Replacing bad words in a string in Objective-C

I have a game with a public highscore list where I allow layers to enter their name (or anything unto 12 characters). I am trying to create a couple of functions to filter out bad words from a list of bad words
I have in a text file. I have two methods:
One to read in the text file:
-(void) getTheBadWordsAndSaveForLater {
badWordsFilePath = [[NSBundle mainBundle] pathForResource:#"badwords" ofType:#"txt"];
badWordFile = [[NSString alloc] initWithContentsOfFile:badWordsFilePath encoding:NSUTF8StringEncoding error:nil];
badwords =[[NSArray alloc] initWithContentsOfFile:badWordFile];
badwords = [badWordFile componentsSeparatedByString:#"\n"];
NSLog(#"Number Of Words Found in file: %i",[badwords count]);
for (NSString* words in badwords) {
NSLog(#"Word in Array----- %#",words);
}
}
And one to check a word (NSString*) agains the list that I read in:
-(NSString *) removeBadWords :(NSString *) string {
// If I hard code this line below, it works....
// *****************************************************************************
//badwords =[[NSMutableArray alloc] initWithObjects:#"shet",#"shat",#"shut",nil];
// *****************************************************************************
NSLog(#"checking: %#",string);
for (NSString* words in badwords) {
string = [string stringByReplacingOccurrencesOfString:words withString:#"-" options:NSCaseInsensitiveSearch range:NSMakeRange(0, string.length)];
NSLog(#"Word in Array: %#",words);
}
NSLog(#"Cleaned Word Returned: %#",string);
return string;
}
The issue I'm having is that when I hardcode the words into an array (see commented out above) then it works like a charm. But when I use the array I read in with the first method, it does't work - the stringByReplacingOccurrencesOfString:words does not seem to have an effect. I have traced out to the log so I can see if the words are coming thru and they are... That one line just doesn't seem to see the words unless I hardcore into the array.
Any suggestions?
A couple of thoughts:
You have two lines:
badwords =[[NSArray alloc] initWithContentsOfFile:badWordFile];
badwords = [badWordFile componentsSeparatedByString:#"\n"];
There's no point in doing that initWithContentsOfFile if you're just going to replace it with the componentsSeparatedByString on the next line. Plus, initWithContentsOfFile assumes the file is a property list (plist), but the rest of your code clearly assumes it's a newline separated text file. Personally, I would have used the plist format (it obviates the need to trim the whitespace from the individual words), but you can use whichever you prefer. But use one or the other, but not both.
If you're staying with the newline separated list of bad words, then just get rid of that line that says initWithContentsOfFile, you disregard the results of that, anyway. Thus:
- (void)getTheBadWordsAndSaveForLater {
// these should be local variables, so get rid of your instance variables of the same name
NSString *badWordsFilePath = [[NSBundle mainBundle] pathForResource:#"badwords" ofType:#"txt"];
NSString *badWordFile = [[NSString alloc] initWithContentsOfFile:badWordsFilePath encoding:NSUTF8StringEncoding error:nil];
// calculate `badwords` solely from `componentsSeparatedByString`, not `initWithContentsOfFile`
badwords = [badWordFile componentsSeparatedByString:#"\n"];
// confirm what we got
NSLog(#"Found %i words: %#", [badwords count], badwords);
}
You might want to look for whole word occurrences only, rather than just the presence of the bad word anywhere:
- (NSString *) removeBadWords:(NSString *) string {
NSLog(#"checking: %# for occurrences of these bad words: %#", string, badwords);
for (NSString* badword in badwords) {
NSString *searchString = [NSString stringWithFormat:#"\\b%#\\b", badword];
string = [string stringByReplacingOccurrencesOfString:searchString
withString:#"-"
options:NSCaseInsensitiveSearch | NSRegularExpressionSearch
range:NSMakeRange(0, string.length)];
}
NSLog(#"resulted in: %#", string);
return string;
}
This uses a "regular expression" search, where \b stands for "a boundary between words". Thus, \bhell\b (or, because backslashes have to be quoted in a NSString literal, that's #"\\bhell\\b") will search for the word "hell" that is a separate word, but won't match "hello", for example.
Note, above, I am also logging badwords to see if that variable was reset somehow. That's the only thing that would make sense given the symptoms you describe, namely that the loading of the bad words from the text file works but replace process fails. So examine badwords before you replace and make sure it's still set properly.

Matching a regular expression with a string (file name)

I'm trying to differentiate between 2 files (in NSString format). As far as I know, this can be done by comparing and matching a regular expression. The format of the 2 jpg files which I have are:
butter.jpg
butter-1.jpg
My question is what regular expression can I write to match the 2 strings above? I've search and found an example expression, but I'm not sure how is it read and think it's wrong.
Here is my code:
NSString *exampleFileName = [NSString stringWithFormat:#"butter-1.jpg"];
NSString *regEx = #".*l{2,}.*";
NSPredicate *regExTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regEx];
if ([regExTest evaluateWithObject:exampleFileName] == YES) {
NSLog(#"Match!");
} else {
NSLog(#"No match!");
}
EDIT:
I tried using the following:
NSString *regEx = #"[a-z]+-[0-9]+.+jpg";
to try to match:
NSString *exampleFileName = [NSString stringWithFormat:#"abcdefg-112323.jpg"];
Tested with:
abc-11.jpg (Match)
abcsdas-.jpg (No Match)
abcdefg11. (No Match)
abcdefg-3123.jpg (Match)
As of now it works, but I want to eliminate any chances that it might not, any inputs?
NSString *regEx = #"[a-z]+-[0-9]+.+jpg";
will fail for butter.jpg, as it needs to have one - and at least on number.
NSString *regEx = #"[a-z]+(-[0-9]+){0,1}.jpg";
and if you do
NSString *regEx = #"([a-z])+(?:-([0-9])+){0,1}.jpg";
You can access the informations you probably would like to have later as capture groups.
(...) |Capturing parentheses. Range of input that matched the parenthesized subexpression is available after the match.
and if you dont need capture groups
NSString *regEx = #"(?:[a-z])+(?:-[0-9]+){0,1}.jpg";
(?:...)| Non-capturing parentheses. Groups the included pattern, but does not provide capturing of matching text. Somewhat more efficient than capturing parentheses.
You can match an alphabetic character (in any language) using \p{L}. You can match a digit using \d. You need to escape the . because in a regular expression, . means “any character”.
Parsing a regular expression is expensive, so you should only do it once.
BOOL stringMatchesMyPattern(NSString *string) {
static dispatch_once_t once;
static NSRegularExpression *re;
dispatch_once(&once, ^{
re = [NSRegularExpression regularExpressionWithPattern:
#"^\\p{L}+-\\d+\\.jpg$" options:0 error:NULL];
}
return nil != [re firstMatchInString:string options:0
range:NSMakeRange(0, string.length)];
}

capitalizedString doesn't capitalize correctly words starting with numbers?

I'm using the NSString method [myString capitalizedString], to capitalize all words of my string.
However capitalization doesn't work very well for words starting with numbers.
i.e. 2nd chance
becomes
2Nd Chance
Even if n is not the first letter of the word.
thanks
You have to roll your own solution to this problem. The Apple docs state that you may not get the specified behavior using that function for multi-word strings and for strings with special characters. Here's a pretty crude solution
NSString *text = #"2nd place is nothing";
// break the string into words by separating on spaces.
NSArray *words = [text componentsSeparatedByString:#" "];
// create a new array to hold the capitalized versions.
NSMutableArray *newWords = [[NSMutableArray alloc]init];
// we want to ignore words starting with numbers.
// This class helps us to determine if a string is a number.
NSNumberFormatter *num = [[NSNumberFormatter alloc]init];
for (NSString *item in words) {
NSString *word = item;
// if the first letter of the word is not a number (numberFromString returns nil)
if ([num numberFromString:[item substringWithRange:NSMakeRange(0, 1)]] == nil) {
word = [item capitalizedString]; // capitalize that word.
}
// if it is a number, don't change the word (this is implied).
[newWords addObject:word]; // add the word to the new list.
}
NSLog(#"%#", [newWords description]);
Unfortunately this seems to be the general behaviour of capitalizedString.
Perhaps a not so nice workaround / hack would be to replace each number with a string before the transformation, and then change it back afterwards.
So, "2nd chance" -> "xyznd chance" -> "Xyznd Chance" -> "2nd Chance"

Finding 2 Capitalized Words in a Row NSString

I'm writing a Mac app that goes through an NSString, and adds all its word to an NSArray (by separating them based on whitespace). Now, I've got the whole system down, but I'm still having one little problem: names (first + last), are added as two different words, and that's bothersome to me.
I thought of a couple solutions to fix this. My best idea was to, before actually adding the words to the array, join two words in a row that are capitalized. Then, through an if statement, determine if a word has two capitals in it, and then split the word and add it as one word. However, I can't find a way to find 2 words in a row with capitals.
Should I be using RegexKitLite (which I'm not familiar with), for example, to find two capitalized words in a row? I've seen this question: Regexp to pull capitalized words not at the beginning of sentence and two adjacent words
which seems somehow related, but due to my lack of understand of regular expressions, I don't really know if this is exactly what I need.
I've also seen this: Separating NSString into NSArray, but allowing quotes to group words
which is also similar, yet not exactly adapted to my needs.
So, to conclude, does anyone know how to either join capitalized words in an NSString, or even better, how to find two capitalized words in a row in an NSString ?
If you're targeting iOS 4.0 or greater OR OS 10.7 you can use NSRegularExpression
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:#"[A-Z]\\w*\\s[A-Z]\\w*"
options:nil
error:&error];
NSString *inputString = #"One two Three Four five six Seven Eight";
NSArray *stringsWithTwoCapitalizedWordsInARow = [regex
matchesInString:inputString
options:0
range:NSMakeRange(0, [string length])];
You'll get something like this
["Three Four", "Seven Eigth"]
You could just do a second pass on the resulting array after it has been loaded to append entries together that need to be joined.
Names are notoriously difficult to match with regular expressions alone, as it is not unheard of for names (first or last) to contain spaces themselves.
NSMutableArray* words = ...;
NSMutableArray* joinedWords = [NSMutableArray array];
for (int i=0; i < [words length]; i++)
{
NSString* currentLine = [words objectAtIndex:i];
bool capitalized = false;
bool capitalizedNext = false;
capitalized = isCap(currentLine); // Up to your discretion here
NSString* nextLine = nil;
// for the last entry
if (i+1 < [words length])
{
nextLine = [words objectAtIndex:i+1];
capitalizedNext = isCap(nextLine);
}
// Check if first letter is uppercase
if (capitalized == true && capitalizedNext == true)
{
[words replaceObjectAtIndex:i withObject:[NSString stringWithFormat:#"%# %#", currentLine, nextLine];
[words removeObjectAtIndex:i+1];
// Run test again on new version of the line
i--;
}
else
{
[joinedWords addObject:currentLine];
}
}
[A-Z][A-Za-z]* [A-Z][A-Za-z]*|[\S]*
http://rubular.com/r/DrOabOAfBr
I've written a regular expression for you. This regex will try to match a name first, then fall back to a word, so your job is as simple as feeding this into NSRegularExpression, and take all the matches as your words, or names joined.

How to convert text to camel case in Objective-C?

I'm making little utility to help me generate code for an app I'm making. I like to have constants for my NSUserDefaults settings, so that my code is more readable and easier to maintain. The problem is, that making constants for everything takes some time, so I'm trying to write a utility to generate code for me. I'd like to be able to enter a string and have it converted to camel case, like so:
- (NSString *)camelCaseFromString:(NSString *)input{
return inputAsCamelCase;
}
Now, the input string might be composed of multiple words. I'm assuming that I need some sort of regular expression here, or perhaps there is another way to do it. I'd like to input something like this:
#"scrolling direction"
or this:
#"speed of scrolling"
and get back something like this:
kScrollingDirection
or this:
kSpeedOfScrolling
How would you go about removing spaces and replacing the character following the space with the uppercase version?
- (NSString *)camelCaseFromString:(NSString *)input {
return [#"k" stringByAppendingString:[[input capitalizedString] stringByReplacingOccurrencesOfString:#" " withString:#""]];
}
Capitalize each word.
Remove whitespace.
Insert "k" at the beginning. (Not literally, but a simplification using stringByAppendingString.)
The currently accepted answer has a bug. (also pointed out by #jaydee3)
Words already having proper camelCasing, PascalCasing, or capitalized TLA acronyms will have their correct casing "destroyed" by having non-first characters lowercased via the call to capitalizedString.
So "I prefer camelCasing to PascalCasing for variables" would look like "iPreferCamelcasingToPascalcasingForVariables" but according to the question, should be "iPreferCamelCasingToPascalCasingForVariables"
Use the below category on NSString to create non-destructive camel and pascal casing. It also properly leaves ALL_CAPS words/acronyms in place, although that wasn't really part of the orig question.
An acronym as a first word for a camelCased string would be weird. "WTH" becomes "wTH" which looks weird. Anyway, that's an edge case, and not addressed. However, since question asker is prefixing with "k" then it "k IBM" becomes "kIBM" which looks ok to me, but would look be "kIbm" with currently accepted answer.
Use:
NSString *str = #"K computer manufacturer IBM";
NSLog(#"constant: %#", str.pascalCased);
// "constant: kComputerManufacturerIBM"
Category (class extension) code.
#implementation NSString (MixedCasing)
- (NSString *)camelCased {
NSMutableString *result = [NSMutableString new];
NSArray *words = [self componentsSeparatedByString: #" "];
for (uint i = 0; i < words.count; i++) {
if (i==0) {
[result appendString:((NSString *) words[i]).withLowercasedFirstChar];
}
else {
[result appendString:((NSString *)words[i]).withUppercasedFirstChar];
}
}
return result;
}
- (NSString *)pascalCased {
NSMutableString *result = [NSMutableString new];
NSArray *words = [self componentsSeparatedByString: #" "];
for (NSString *word in words) {
[result appendString:word.withUppercasedFirstChar];
}
return result;
}
- (NSString *)withUppercasedFirstChar {
if (self.length <= 1) {
return self.uppercaseString;
} else {
return [NSString stringWithFormat:#"%#%#",[[self substringToIndex:1] uppercaseString],[self substringFromIndex:1]];
}
}
- (NSString *)withLowercasedFirstChar {
if (self.length <= 1) {
return self.lowercaseString;
} else {
return [NSString stringWithFormat:#"%#%#",[[self substringToIndex:1] lowercaseString],[self substringFromIndex:1]];
}
}
#end
Just use: #"This is a sentence".capitalizedString;
Becomes: >> "This Is A Sentence"
Replace spaces and manipulate...
Uppercases first char of word, and lowers the other letters for each word.