NSRegularExpression Tutorial and Cheat Sheet
A NSRegularExpression tutorial that shows you how to search, replace, and validate data in your app. Includes a handy NSRegularExpression cheat sheet PDF! By Soheil Azarpour.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
NSRegularExpression Tutorial and Cheat Sheet
45 mins
Dealing With Multiple Format Patterns
In the previous section, you implemented a regular expression pattern to match a date. To do this, you made some assumptions and gave the user some flexibility to enter a date in dd-mm-yyyy
, dd/mm/yyyy
or dd.mm.yyyy
format.
But the user could have entered a date in the format dd\mm\yyyy
, and it would have failed validation. What if you’re in a situation where you want to enforce a specific pattern?
In RWThirdViewController.m, the user will enter a Social Security Number (SSN) into the app. In the United States, a Social Security Number has a specific format: xxx-xx-xxxx. There are only 11 possible characters in a SSN: 0 1 2 3 4 5 6 7 8 9 and – (hyphen).
How can you enforce this format in your app? What are your options?
You could:
- Display the default keyboard and intercept user input; if there is an invalid character typed, ignore it. Problem: not very user-friendly. User types something and nothing happens!
- Display a customized keyboard? Problem: too much work for such a simple task.
- Display the number pad keyboard, but also intercept user input and insert dashes where necessary. Problem: what if user copy + pastes text from somewhere else?
This tutorial implements the last option, as it requires the least effort, it is more user-friendly, and the problem of copy-pasting from another place can be easily fixed with the help of (surprise surprise!) regular expressions.
Open RWThirdViewController.m and take a look at textField:shouldChangeCharactersInRange:replacementString:
to see how the app intercepts user inputs and inserts or removes dashes.
All that’s left for you to do is to come up with a pattern to match a Social Security Number (xxx-xx-xxxx). You need a pattern that must contain 3 numbers, followed by a dash, then 2 numbers, followed by another dash, followed by the final 4 numbers.
In the real world, Social Security Numbers have more detailed criteria than just matching a certain format. However, for the purposes of this tutorial, you’ll keep it simple and just match the xxx-xx-xxxx format. As homework, try modifying the regex pattern for a Social Security Number so that it matches only acceptable ones, as detailed here: http://en.wikipedia.org/wiki/Social_Security_number#Structure
In the real world, Social Security Numbers have more detailed criteria than just matching a certain format. However, for the purposes of this tutorial, you’ll keep it simple and just match the xxx-xx-xxxx format. As homework, try modifying the regex pattern for a Social Security Number so that it matches only acceptable ones, as detailed here: http://en.wikipedia.org/wiki/Social_Security_number#Structure
Rather than [0-9]
, you can use the more concise digit class \d
in your regular expression. A pattern like @"^\\d{3}[-]\\d{2}[-]\\d{4}$"
should do the trick!
Replace the definition of kSocialSecuityNumberPattern>
at the top of RWThirdViewController.m with the following:
#define kSocialSecuityNumberPattern @"^\\d{3}[-]\\d{2}[-]\\d{4}$"
Build and run your app, and switch to the third tab. Whether you paste in numbers, characters, or use the number pad, the validation will kick in and auto-format, as seen in the screenshot below:
Handling Multiple Search Results
You haven’t yet implemented the Bookmark button found on the navigation bar. When the user taps on it, the app should highlight any date, time or location strings in the text.
Open up RWFirstViewController.m in Xcode, and find the following implementation for the Bookmark bar button item:
#pragma mark
#pragma mark - IBActions
- (IBAction)findInterestingData:(id)sender
{
[self underlineAllDates];
[self underlineAllTimes];
[self underlineAllLocations];
}
The method above calls three other helper methods to underline dates, times and locations in the text. If you look at the implementation of each of the helper methods above, you will see they are empty!
Guess it’s your job to flesh out these three methods!
Here’s the requirements for the helper methods:
Date Requirements:
- xx/xx/xx or xx.xx.xx or xx-xx-xx format. Day, month and year placement is not important since they will just be highlighted. Example: 10-05-12.
- Full or abbreviated month name (e.g. Jan or January, Feb or February, etc.), followed by one or two character number (e.g. x or xx). The day of the month can be ordinal (e.g. 1st, 2nd, 10th, 21st, etc.), followed by comma as separator, and then a four-digit number (e.g. xxxx). There can be zero or more white spaces between the name of the month, day and year. Example: March 13th, 2001
Time requirements:
- Find simple times like “9am” or “11 pm”: One or two digits followed by zero or more white spaces, followed by either lowercase “am” or “pm”.
Location requirements:
- Any word at least one character long, immediately followed by a comma, followed by zero or more white spaces followed by any capitalized English letter combination that is exactly 2 characters long. For example “Boston, MA”.
Give it a try and see if you can sketch out the needed regular expressions!
For your convenience, there is already a helper method in the project you can call with an array of patterns for the regex engine to find and highlight for you. It is - (void)highlightMatches:(NSArray *)matches
, and you can find its implementation at the very end of RWFirstViewController.m.
Here’s three samples for you to try. Replace the empty implementation of underlineAllDates with the following code:
- (void)underlineAllDates
{
NSError *error = NULL;
NSString *pattern = @"(\\d{1,2}[-/.]\\d{1,2}[-/.]\\d{1,2})|(Jan(uary)?|Feb(ruary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?)\\s*\\d{1,2}(st|nd|rd|th)?+[,]\\s*\\d{4}";
NSString *string = self.textView.text;
NSRange range = NSMakeRange(0, string.length);
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
NSArray *matches = [regex matchesInString:string options:NSMatchingProgress range:range];
[self highlightMatches:matches];
}
This pattern has two parts separated by the |
(OR) character. That means either the first part or the second part will match.
The first part reads: (\d{1,2}[-/.]\d{1,2}[-/.]\d{1,2})
. That means two digits followed by one of -
or /
or .
followed by two digits, followed by -
or /
or .
, followed by a final two digits.
The second part starts with (Jan(uary)?|Feb(ruary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?)
, which will match a full or abbreviated month name.
Next up is \\s*\\d{1,2}(st|nd|rd|th)?
which will match zero or many spaces, followed by 1 or 2 digits, followed by an optional ordinal suffix. As an example, this will match both “1” and “1st”.
Finally [,]\\s*\\d{4}
will match a comma followed by zero or multiple spaces followed by a four-digit number for the year.
That’s quite the intimidating regular expression! However, you can see how regular expressions are concise and pack a lot of information — and power! — into a seemingly cryptic string.
Next up are the the implementations of underlineAllTimes
and underlineAllLocations
. Fill in the blank implementations with the following:
- (void)underlineAllTimes
{
NSError *error = NULL;
NSString *pattern = @"\\d{1,2}\\s*(pm|am)";
NSString *string = self.textView.text;
NSRange range = NSMakeRange(0, string.length);
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
NSArray *matches = [regex matchesInString:string options:NSMatchingProgress range:range];
[self highlightMatches:matches];
}
- (void)underlineAllLocations
{
NSError *error = NULL;
NSString *pattern = @"[a-zA-Z]+[,]\\s*([A-Z]{2})";
NSString *string = self.textView.text;
NSRange range = NSMakeRange(0, string.length);
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
NSArray *matches = [regex matchesInString:string options:NSMatchingProgress range:range];
[self highlightMatches:matches];
}
As an exercise, see if you can explain the regular expression patterns based on the specifications above.
Build and run the app and tap on the Bookmark icon. You should see the link-style highlighting for dates, times, and locations, as shown below: