Matching a regular expression with a string (file name) - objective-c

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

Related

Apostrophes (') is not recognised in regular expression

I want a regular expression for first name that can contain
1)Alphabets
2)Spaces
3)Apostrophes
Exp: Raja, Raja reddy, Raja's,
I used this ^([a-z]+[,.]?[ ]?|[a-z]+[']?)+$ but it is failing to recognise Apostrophes (').
- (BOOL)validateFirstNameOrLastNameOrCity:(NSString *) inputCanditate {
NSString *firstNameRegex = #"^([a-z]+[,.]?[ ]?|[a-z]+[']?)+$";
NSPredicate *firstNamePredicate = [NSPredicate predicateWithFormat:#"SELF MATCHES[c] %#",firstNameRegex];
return [firstNamePredicate evaluateWithObject:inputCanditate];
}
May I recommand ^[A-Z][a-zA-Z ']* ?
// The NSRegularExpression class is currently only available in the Foundation framework of iOS 4
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"^[A-Z][a-zA-Z ']*" options:NSRegularExpressionAnchorsMatchLines error:&error];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:searchText options:0 range:NSMakeRange(0, [string length])];
return numberOfMatches > 1;
^[A-Z] : Force start with a capital letter from A to Z
[a-zA-Z ']* : followed by any number of charactere that an be 'a' to 'z', 'A' to 'Z', space or simple quote
I think you are looking for a pattern like this: ^[a-zA-Z ']+$
However, this is pretty bad. What about umlauts, accents, and a whole lot other letters that are not part of the ASCII alphabet?
A better solution would be to allow any kind of letter from any language.
To do so you can use the Unicode "letter" category \p{L}, e.g. ^[\p{L}]+$.
.. or you could just drop that rule all together - as reasonably suggested.

Objective-C Find first matching regular expression in string

I have a task with regular expressions. I have a list of NSRegularExpression objects with different patterns. Also I have a NSString object to define a source. I need to find which regular expression (from the given list) matches for the BEGINNING of source.
Is there a way to do it with Objective-C?
For example:
Expressions patterns
[a-z]
[A-Z]
[1-9]
source
Hello32
Result
Expression no 2 fits for the beginning of source, because of letter H.
Why don't you just try them out?
NSString *testString = #"Hello";
NSArray *patterns = #[
#"[a-z]",
#"[A-Z]",
#"[1-9]",
];
for (NSString *pattern in patterns) {
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
options:0
error:NULL];
BOOL matchAtStart = [regex rangeOfFirstMatchInString:testString
options:0
range:(NSRange){0, testString.length}].location == 0;
NSLog(#"'%#': %#", pattern, #(matchAtStart));
}
You can prepend \A(?: and append ) to each pattern to force them to match at the beggining of the string. The patterns provided as example would become:
\A(?:[a-z])
\A(?:[A-Z])
\A(?:[1-9])
\A is an anchor to the beggining of the string (behaves exactly like ^ when the Multiline flag is not set).

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

How to check if NSString contains a string with a specific format?

How can I detect if a certain NSString contains a string format like this for example:
I would like to check if a string is in a certain format. For example if I have the string format, #"%d %d/%d %#", I would like my code to return YES if I compare it against #"1 1/2 oz", and like wise would return NO if I compared it against #"20 ml".
What you want to use are regular expressions. The regex for this specific case is
[0-9]+\ [0-9]+\/[0-9]+\ [a-zA-Z]+
Try using NSRegularExpression.
Here's also a regex tutorial.
you can use NSRegularExpression
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:yourPattern options:NSRegularExpressionCaseInsensitive error:&error];

Why NSRegularExpression says that there are two matches of ".*" in the "a" string?

I'm very happy that Lion introduced NSRegularExpression, but I can't understand why the pattern .* matches two occurrences in a string like "a" (text can be longer).
I was using following code:
NSError *anError = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#".*"
options:0
error:&anError];
NSString *text = #"a";
NSUInteger counter = [regex numberOfMatchesInString:text
options:0
range:NSMakeRange(0, [text length])];
NSLog([NSString stringWithFormat:#"counter = %u", counter]);
Output from the console is:
2011-07-27 22:03:27.689 Regex[1930:707] counter = 2
Can anyone explain why that is?
The regular expression .* matches zero or more characters. Thus, it will match the empty string as well as a and as such there are two matches.
Mildly surprised that it didn't match 3 times. One for the "" before the "a", one for the "a" and one for the "" after the "a".
As has been noted, use a more precise pattern; including anchors (^ and/or $) might also change the behaviour.
No-one has asked, but why would you want to do this anyway?
The documents on NSRegularExpression say the following:
Some regular expressions [...] can
successfully match a zero-length range, so the comparison of the
resulting range with {NSNotFound, 0} is the most reliable way to
determine whether there was a match or not.
I more reliable way to get just one match would be to change the expression to .+