How to use RegEx to support single line mode in textview? - objective-c

I set my custom textview to support regExPatternValidation = #"^[0-9]{0,10}$";
and use the following method to accomplish my validation:
+ (BOOL)validateString:(NSString *)string withRegExPattern:(NSString *)regexPattern
{
BOOL doesValidate = NO;
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexPattern
options:NSRegularExpressionCaseInsensitive
error:&error];
if (error)
{
DDLogError(#"%#:%# : regular expression error: [%#]", THIS_FILE, THIS_METHOD, error.description);
return doesValidate;
}
NSRange textRange = NSMakeRange(0, string.length);
NSUInteger regExMatches = [regex numberOfMatchesInString:string options:NSMatchingReportCompletion range:textRange];
if (regExMatches > 0 && regExMatches <= textRange.length+1)
{
doesValidate = YES;
}
else
{
doesValidate = NO;
}
return doesValidate;
}
One of its purposes is to control single or multi line modes. For some strange reason, when I hit the Return key (\n), the numberOfMatchesInString: still returns 1 match. Even though my regex pattern has no inclusion to support \n characters.
Is it possible to accomplish this feature using regex in Objective-C?

The issue you have has its roots in how anchors ^ and $ work.
^ matches at the beginning (right before the first character, or \n in our case), and $ matches at the end of string (at \n). When you press Return, your string looks like \n. Exactly a match!
So, in your case [0-9]* can match an empty string due to the * quantifier (0 or more occurrences of the preceding pattern).
So, you can avoid matching an empty string with a negative look-ahead:
#"^(?!\n$)[0-9]*$"
It will not match an empty string with just a newline symbol in it. See this demo.

Related

Regex to reject sequence of Digits

I need to validate phone number. Below is the code snippet
-(BOOL) validatePhone:(NSString*) phoneString
{
NSString *regExPattern = #"^[6-9]\\d{9}$"; ORIGINAL
// NSString *regExPattern = #"^[6-9](\\d)(?!\1+$)\\d*$";
NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:regExPattern options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger regExMatches = [regEx numberOfMatchesInString:phoneString options:0 range:NSMakeRange(0, [phoneString length])];
NSLog(#"%lu", (unsigned long)regExMatches);
if (regExMatches == 0) {
return NO;
}
else
return YES;
}
I want to reject phone number that is in sequnce example
9999999999, 6666677777
It seems you want to disallow 5 and more identical consecutive digits.
Use
#"^[6-9](?!\\d*(\\d)\\1{4})\\d{9}$"
See the regex demo
Details
^ - start of string
[6-9] - a digit from 6 to 9
(?!\d*(\d)\1{4}) - a negative lookahead that fails the match if, immediately to the right of the current location, there is
\d* - 0+ digits
(\d) - a digit captured into Group 1
\1{4} - the same digit as captured in Group 1 repeated four times
\d{9} - any 9 digits
$ - end of string (replace with \z to match the very end of string do disallow the match before the final LF symbol in the string).
Note that \d is Unicode aware in the ICU regex library, thus it might be safer to use [0-9] instead of \d.

NSDiacriticInsensitiveSearch and arabic search

As known, the NSDiacriticInsensitiveSearch does not do the same effect on arabic letters like it does on french. That's why i'm trying to create the same effect but with arabic letters.For example, if a user enters the letter "ا" , the search bar should show all the words containing the letter " ا " and the letter : " أ " at the same time.
The use of the following line :
text = [text stringByReplacingOccurrencesOfString:#"ا" withString:#"أ"];
will not show the results of the words starting with " ا ".
In the search bar, i tried to implement the same NSDiacriticInsensitiveSearch method like i did in the french case, and it didn't work out :
NSRange nameRange = [author.name rangeOfString:text options:NSAnchoredSearch | NSDiacriticInsensitiveSearch];
Any ideas how to get this done ?
You can use the regular expression to handle the Arabic (Alif) different shapes.
Assume that you have a context, that is "محمد بن إبراهيم الابراهيمي", and the pattern to search for is "إبراهيم", then you could convert the pattern to a regular expression that handles the differentiation between the "أ". The regular expression should be "(أ|إ|ا)بر(أ|إ|ا)هيم". This will search for the pattern by its all possible shapes.
Here is a simple code that I wrote:
#import <Foundation/Foundation.h>
NSString * arabify(NSString * string)
{
NSRegularExpression * alifRegex = [NSRegularExpression regularExpressionWithPattern:#"(أ|ا|إ)" options:0 error:nil];
return [alifRegex stringByReplacingMatchesInString:string options:0 range:NSMakeRange(0, [string length]) withTemplate:#"(أ|ا|إ)"];
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSString * context = #"محمد بن إبراهيم الابراهيمي";
NSString * pattern = #"إبراهيم";
// Get the regex for the Arabic word.
NSString * regex = arabify(pattern);
NSLog(#"context = %#", context);
NSLog(#"pattern = %#", pattern);
NSLog(#"regex = %#", regex);
NSRange range = [context rangeOfString:regex options:NSRegularExpressionSearch];
if (range.location == NSNotFound)
{
NSLog(#"Not found.");
}
else
{
NSLog(#"Found.");
NSLog(#"location = %lu, length = %lu", (unsigned long)range.location, (unsigned long)range.length);
}
}
return 0;
}
Good luck brother.
It seems that you are using the compound symbol (U+0623), which does not collate with other representations of Alif.
Did you consider other encoding methods for the Alif? You could use the decomposed variant, which then would collate with the "plain" Alif (U+0627) just how you intend:
ARABIC LETTER ALEF (U+0627) ARABIC HAMZA ABOVE (U+0654)
See here: http://www.fileformat.info/info/unicode/char/0623/index.htm

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)];
}

NSPredicate Detect First & Last Name

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).

why is code falling on substringwithrange

hi here is my function, but everytime when i try to init and alloc foo it falls, can you tell me why?
-(NSString*)modifyTheCode:(NSString*) theCode{
if ([[theCode substringToIndex:1]isEqualToString:#"0"]) {
if ([theCode length] == 1 ) {
return #"0000000000";
}
NSString* foo = [[NSString alloc]initWithString:[theCode substringWithRange:NSMakeRange(2, [theCode length]-1)]];
return [self modifyTheCode:foo];
} else {
return theCode;
}
}
the error message:
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.2 (8H7)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
replace this line
NSString* foo = [[NSString alloc]initWithString:[theCode substringWithRange:NSMakeRange(2, [theCode length]-1)]];
with this line
NSString* foo = [[NSString alloc]initWithString:[theCode substringWithRange:NSMakeRange(1, [theCode length]-1)]];
and try..
What is the error message?
If you are working with NSRange maybe you should check the length of theCode first.
Because the range is invalid. NSRange has two members, location and length. The range you give starts at the third character of the string and has the length of the string minus one. So your length is one character longer than the amount of characters left in the string.
Suppose theCode is #"0123". The range you create is { .location = 2, .length = 3 } This represents:
0123
^ start of range is here
^ start of range + 3 off the end of the string.
By the way, you'll be pleased to know that there are convenience methods so you don't have to mess with ranges. You could do:
if ([theCode hasPrefix: #"0"])
{
NSString* foo = [theCode substringFromIndex: 1]; // assumes you just want to strip off the leading #"0"
return [self modifyTheCode:foo];
} else {
return theCode;
}
By the way, your original code leaked foo because you never released it.