Input mask Textfield with Regex - ios

I try to create an input mask for an UITextField for enter aircraft callsign. The mask should be "F-" and 4 letters. I try it with regular expression but it doesn't work fine. I show you my code. It is impossible to enter any character with it.
if (textField==_tImmat) {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSString *expression = #"^(F-)([A-Z]{4})$";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString options:0 range:NSMakeRange(0, [newString length])];
if (numberOfMatches == 0){
return NO;
}
Any ideas to create regex which works fine for a string value like this :"F-XXXX" where X is an uppercase letter?
Thanks for your help...

I'm guessing that code is in your shouldChangeCharactersInRange method?
If so, because you are imposing of this specific format, no other string can ever be entered, i.e. unless the value of newString is F-<4 LETTERS>, the method will return NO.
So you can either move your validation to textFieldShouldReturn method:
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSString *expression = #"^(F-)([A-Z]{4})$";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:textField.text options:0 range:NSMakeRange(0, [textField.text length])];
if (numberOfMatches == 0)
{
NSLog(#"wrong format!");
}
else
{
NSLog(#"correct format!");
}
return NO;
}
(by the way, for options you are using NSRegularExpressionCaseInsensitive which means it will allow user to put in both uppercase and lowercase characters and validate them as correct)
If you'd like to validate as the user types and restrict input of illegal characters, I'd change your code to something like this:
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
switch (newString.length)
{
case 0:
//allow user empty string
return YES;
case 1:
//check that first character is F
return [newString isEqualToString:#"F"];
case 2:
//check that the second character is -
return [newString isEqualToString:#"F-"];
case 3 ... 6:
//this will check the last four characters based on your regex
{
NSString *expression = #"^(F-)([A-Z]{0,4})$"; //notice the added range
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString options:0 range:NSMakeRange(0, [newString length])];
if (numberOfMatches == 0)
{
return NO;
}
else
{
return YES;
}
}
default:
//in all other cases validation will fail
return NO;
}

Your validation only accepts the value F-XXXX. Starting with a black text field if the user enters the F, your validation fails because it's not F-XXXX.
You need to let the user enter each character and only fail the validation if the entered text can't possibly ever match the pattern.
There's no reason to use a regular expression for this. Just scan newString. Make sure it starts with an F. If the length is 2 or more, check that the 2nd character is a ```. Then simply make sure the length is less than 6 and the last few characters are the letters A-Z.

Related

SubString from existing string iOS

I have two strings as followed:
NSString *newStr = #"143.2a";
NSString *expression = #"^([0-9]*)(\\.([0-9]{0,10})?)?$";
I want to substring "newStr" such as all my characters in "expression" should be present after subString. Like
NSString * extractedString = #"143.2";
(except all alphabets and symbols other than single'.')
How shall I do this?
First of all, your regex pattern won't extract that string.
If you want to check for one or more digits followed be a dot followed be one or more digits the pattern is supposed to be
NSString *expression = #"\\d+\\.\\d+";
To extract the string use the NSRegularExpression class as suggested by Larme.
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:0 error:nil];
NSTextCheckingResult *firstMatch = [regex firstMatchInString:newStr options:0 range:NSMakeRange(0, newStr.length)];
if (firstMatch) {
NSString *extractedString = [newStr substringWithRange:firstMatch.range];
NSLog(#"%#", extractedString);
} else {
NSLog(#"Not Found");
}

Objective C - NSRegularExpression with specific substring

I have an NSString which I am checking if there is an NSLog and then I comment it out.
I am using NSRegularExpression and then looping through result.
The code:
-(NSString*)commentNSLogFromLine:(NSString*)lineStr {
NSString *regexStr =#"NSLog\\(.*\\)[\\s]*\\;";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:nil];
NSArray *arrayOfAllMatches = [regex matchesInString:lineStr options:0 range:NSMakeRange(0, [lineStr length])];
NSMutableString *mutStr = [[NSMutableString alloc]initWithString:lineStr];
for (NSTextCheckingResult *textCheck in arrayOfAllMatches) {
if (textCheck) {
NSRange matchRange = [textCheck range];
NSString *strToReplace = [lineStr substringWithRange:matchRange];
NSString *commentedStr = [NSString stringWithFormat:#"/*%#*/",[lineStr substringWithRange:matchRange]];
[mutStr replaceOccurrencesOfString:strToReplace withString:commentedStr options:NSCaseInsensitiveSearch range:matchRange];
NSRange rOriginal = [mutStr rangeOfString:#"NSLog("];
if (NSNotFound != rOriginal.location) {
[mutStr replaceOccurrencesOfString:#"NSLog(" withString:#"DSLog(" options:NSCaseInsensitiveSearch range:rOriginal];
}
}
}
return [NSString stringWithString:mutStr];
}
The problem is with the test case:
NSString *str = #"NSLog(#"A string"); NSLog(#"A string2")"
Instead of returning "/*DSLog(#"A string");*/ /*DSLog(#"A string2")*/" it returns: "/*DSLog(#"A string"); NSLog(#"A string2")*/".
The issue is how the Objective-C handles the regular expression. I would expected 2 results in arrayOfAllMatches but instead that I am getting only one. Is there any way to ask Objective-C to stop on the first occurrence of ); ?
The problem is with the regular expression. You are searching for .* inside the parentheses, which causes it to include the first close parenthesis, continue through the second NSLog statement, and go all the way to the final close parentheses.
So what you want to do is something like this:
NSString *regexStr =#"NSLog\\([^\\)]*\\)[\\s]*\\;";
That tells it to include everything inside the parenthesis except for the ) character. Using that regex, I get two matches. (note that you omitted the final ; in your string sample).

Issue with NSRegularExpression / RegEx to validate Postal Code

I have the following two methods, where I am calling an NSRegularExpression (first time using this) with two separate patterns, one to check against Canadian and the other American Postal/Zip codes.
-(BOOL)isValidPostalCode:(NSString*)code
{
BOOL isValid = NO;
NSString *cdnCheck = #"^[ABCEGHJKLMNPRSTVXY]\d[A-Z][- ]*\d[A-Z]\d$";
NSString *usCheck = #"^\d{5}(-\d{4})?$";
if ([self validateString:code withPattern:cdnCheck] || [self validateString:code withPattern:usCheck]) {
isValid = YES;
}
return isValid;
}
- (BOOL)validateString:(NSString *)string withPattern:(NSString *)pattern
{
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
if (error)
{
NSLog(#"Couldn't create regex with given string and options");
}
NSRange textRange = NSMakeRange(0, string.length);
NSRange matchRange = [regex rangeOfFirstMatchInString:string options:NSMatchingReportProgress range:textRange];
NSTextCheckingResult *resultString = [regex firstMatchInString:string options:0 range:textRange];
BOOL didValidate = NO;
if (matchRange.location != NSNotFound) {
didValidate = YES;
}
return didValidate;
}
In the second method, matchRange's length is always 0 (which I think is an indicator of no match?), and resultString is always nil... And I can't figure out why.
What am I doing wrong?
-- DISCLAIMERS --
a) I am passing a string - with no whitespaces - such as 'V6E1H8' or '90210', so those are valid candidates
b) Yes, I know those are redundant checks in the second method, I am just debugging with various other options from the Class Reference and thought to leave it in there...
c) FYI, I got the expressions from this answer (and a few more) and they check out on http://regexpal.com/, so I am assuming that the patterns themselves are valid, and it's an issue with my logic (I am admittedly a bit fuzzy on how NSRange works and how the NSRegEx (excuse the shorthand) evaluates a valid result...

How to work with the results from NSRegularExpression when using the regex pattern as a string delimiter

I'm using a simple pattern with NSRegularExpression to delimit content within a string:
(\s)+(and|or)(\s)+
So, when I use matchesInString it's not the matches that I'm interested in, but the other stuff.
Below is the code that I'm using. Iterating over the matches and then using indexes and lengths to pull out the content.
Question: I'm just wondering if I'm missing something in the api to get the other bits? Or, is the approach below generally ok?
- (NSArray*)separateText:(NSString*)text
{
NSString* regExPattern = #"(\\s)+(and|or)(\\s)+";
NSError* error = NULL;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:regExPattern
options:NSRegularExpressionCaseInsensitive
error:&error];
NSArray* matches = [regex matchesInString:text options:0 range:NSMakeRange(0, text.length)];
if (matches.count == 0) {
return #[text];
}
NSInteger itemStartIndex = 0;
NSMutableArray* result = [NSMutableArray new];
for (NSTextCheckingResult* match in matches) {
NSRange matchRange = [match range];
if (!matchRange.location == 0) {
NSInteger matchStartIndex = matchRange.location;
NSInteger length = matchStartIndex - itemStartIndex;
NSString* item = [text substringWithRange:NSMakeRange(itemStartIndex, length)];
if (item.length != 0) {
[result addObject:item];
}
}
itemStartIndex = NSMaxRange(matchRange);
}
if (itemStartIndex != text.length) {
NSInteger length = text.length - itemStartIndex;
NSString* item = [text substringWithRange:NSMakeRange(itemStartIndex, length)];
[result addObject:item];
}
return result;
}
You can capture the string before the and|or with parentheses, and add it to your array with rangeAtIndex.
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(.+?)(\\s+(and|or)\\W+|\\s*$)" options:NSRegularExpressionCaseInsensitive error:&error];
NSMutableArray *phrases = [NSMutableArray array];
[regex enumerateMatchesInString:string options:0 range:NSMakeRange(0, [string length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange range = [result rangeAtIndex:1];
[phrases addObject:[string substringWithRange:range]];
}];
A couple of minor points about my regex:
I added the |\\s*$ construct to capture the last string after the final and|or. If you don't want that, you can eliminate that.
I replaced the second \\s+ (whitespace) with a \\W+ (non-word characters), in case you encountered something like and|or followed by a comma or something else. You could alternatively look explicitly for ,?\\s+ if the comma was the only non-word character you cared about. It just depends upon the specific business problem you're solving.
You might want to replace the first \\s+ with \\W+, too.
If your string contains newline characters, you might want to use the NSRegularExpressionDotMatchesLineSeparators option when you instantiate the NSRegularExpression.
You could replace all matches of the regex with a template string (e.g. ", " or "," etc) and then separate the string components based on that new delimiter.
NSString *stringToBeMatched = #"Your string to be matched";
NSString *regExPattern = #"(\\s)+(and|or)(\\s)+";
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regExPattern
options:NSRegularExpressionCaseInsensitive
error:&error];
if (error) {
// handle error
}
NSString *replacementString = [regex stringByReplacingMatchesInString:stringToBeMatched
options:0
range:NSMakeRange(0, stringToBeMatched.length)
withTemplate:#","];
NSArray *otherItemsInString = [replacementString componentsSeparatedByString:#","];

Editing textfield after "endEdit" with ShouldChangeCharactersInRange implemented

I have the following code implemented to restrict my user to enter in more than 2 decimal points and places after they have entered the first one. Users can still edit the textfield as long as they don't "leave" or "endEdit" the textfield. However, once they leave the textfield and go back, the textfield is not editable. How can I solve this problem?
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == self.SalesAmounttext)
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSString *expression = #"^([0-9]+)?(\\.([0-9]{1,2})?)?$";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression
options:NSRegularExpressionCaseInsensitive
error:nil];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString
options:0
range:NSMakeRange(0, [newString length])];
if (numberOfMatches == 0)
return NO;
}
return YES;
}
All I had to do was add in the clearsOnInsertion code and everything works! Thanks to #Wain
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == self.SalesAmounttext)
{
textField.clearsOnInsertion=YES ; // HERE
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSString *expression = #"^([0-9]+)?(\\.([0-9]{1,2})?)?$";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression
options:NSRegularExpressionCaseInsensitive
error:nil];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString
options:0
range:NSMakeRange(0, [newString length])];
if (numberOfMatches == 0)
return NO;
}
return YES;
}
Your logic places very tight restrictions on the user so any arbitrary edit will not be permitted. From a user standpoint that will probably be quite confusing so good options would be:
Set clearsOnInsertion when the text field is created so that each edit is a fresh start
Alert the user about invalid edits
Allow each edit and then format at the end of editing and alert the user to any issue then

Resources