String search in objective-c - objective-c

Given a string like this:
http://files.domain.com/8aa55fc4-3015-400e-80f5-390997b43cf9/c07cb0d2-b7d7-4bfd-b0c3-6f43571e3c29-MyFile.jpg
I need to just locate the string "MyFile", and also tell what kind of image it is (.jpg or .png). How can I accomplish this?
The only thing I can think of is to search backward for the first four characters to get the file extension, then keep searching backward until I find the first hyphen, and assume the file name itself doesn't have any hyphens. But I don't know how to do that. Is there a better way?

Use NSRegularExpression to search for the file name. The search pattern really depends on what you know about the file name. If the "random" numbers and characters before MyFile has a known format, you could take that into account. My proposal below assumes that the file name doesn't contain any minus signs.
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:#"-([:alnum:]*)\\.(jpg|png)$"
options:NSRegularExpressionSearch
error:nil];
// Get the match between the first brackets.
NSTextCheckingResult *match = [regex firstMatchInString:string options:0
range:NSMakeRange(0, [string length])];
NSRange matchRange = [match rangeAtIndex:1];
NSString *fileName = [string substringWithRange:matchRange];
NSLog(#"Filename: %#", fileName);
// Get the extension with a simple NSString method.
NSString *extension = [string pathExtension];
NSLog(#"Extension: %#", extension);

[myString lastPathComponent] will get the filename.
[myString pathExtension] will get the extension.
To get the suffix of the filename, I think you'll have to roll your own parse. Is it always the string after the last dash and before the extension?
If so, here's an idea:
- (NSString *)lastLittleBitOfTheFilenameFrom:(NSString *)filename {
NSInteger fnStart = [filename rangeOfString:#"-" options:NSBackwardsSearch].location + 1;
NSInteger fnEnd = [filename rangeOfString:#"." options:NSBackwardsSearch].location;
// might need some error checks here depending on what you expect in the original url
NSInteger length = fnEnd - fnStart;
return [filename substringWithRange:NSMakeRange(fnStart, length)];
}
Or, thanks to #Chuck ...
// even more sensitive to unexpected input, but nice and tiny ...
- (NSString *)lastLittleBitOfTheFilenameFrom:(NSString *)filename {
NSString *nameExt = [[filename componentsSeparatedByString:#"-"] lastObject];
return [[nameExt componentsSeparatedByString:#"."] objectAtIndex:0];
}

If you have the string in an NSString object, or create it from that string, you may use the rangeOfString method to acomplish both.
See https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html for more details.

Related

Looping through the value in Textfield for Particular Text

I have a TextField which has values as shown below.
#"Testing<Car>Testing<Car2>Working<Car3 /Car 4> on the code"
Here I have to loop through the text field and check for the text present within Angle brackets(< >).
There can be space or any special characters within the Angle Brackets.
I tried using NSPredicate and also componentsSeparatedByString, but I was not able to get the exact text within.
Is there any way to get the exact text along with Angle Brackets. Like in the above mentioned example want only
#"<Car>,<Car2> , <Car3 /Car 4>"
Thanks for the help in Advance.
A possible solution is Regular Expression. The pattern checks for < followed by one or more non-> characters and one >.
enumerateMatchesInString extracts the substrings and append them to an array. Finally the array is flattened to a single string.
NSString *string = #"Testing<Car>Testing<Car2>Working<Car3 /Car 4> on the code";
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:#"<[^>]+>" options:0 error:nil];
__block NSMutableArray<NSString *> *matches = [NSMutableArray array];
[regex enumerateMatchesInString:string options:0 range:NSMakeRange(0, string.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
if (result) [matches addObject:[string substringWithRange:result.range]];
}];
NSLog(#"%#", [matches componentsJoinedByString:#", "]);
We can solve it in different ways. Now I am showing one of the way. You can place textFiled.text in place of str.
NSString *str = #"This is just Added < For testing %# ___ & >";
NSRange r1 = [str rangeOfString:#"<" options: NSBackwardsSearch];
NSRange r2 = [str rangeOfString:#">" options: NSBackwardsSearch];
NSRange rSub = NSMakeRange(r1.location + r1.length, r2.location - r1.location - r1.length);
NSString *sub = [str substringWithRange:rSub];

Objective C - Split string into array

How would I do this? I'm new to Objective-C but I can't find anything that would help me do this.
NSArray *splitLine = [currentLine componentsSeparatedByString:#":%#",notNumber];
Where notNumber is a string that represents anything that isn't a number. So I want to separate a string where there are colons separated by strings that aren't numbers. (I want to avoid splitting at times i.e. 3:00pm, but split at iCal parameters like DESCRIPTION: and LOCATION:.)
You can do this in several steps, like this. I have not compiled this code, but it should at least give you an idea of what to do.
1) Create a regex object to match your separators:
NSString *regexString = #"DESCRIPTION:\s|LOCATION:\s"; // or whatever makes sense for your scenario
NSRegularExpression *regex =
[NSRegularExpression regularExpressionWithPattern:regexString
options:NSRegularExpressionCaseInsensitive
error:nil];
2) Replace all the different separators matching your regex with just one separator:
NSRange range = NSMakeRange(0, string.length);
NSString *string2 = [regex stringByReplacingMatchesInString:string
options:0
range:range
withTemplate:#"SEPARATOR"];
3) Split the string!
NSArray *elements = [string2 componentsSeparatedByString:#"SEPARATOR"];
Shortest solution for splitting string.
NSString *str = #"Please split me to form array of words";
NSArray *wordsArray = [str componentsSeparatedByString:#" "];
You can use regular expressions!
Using the pattern (I believe this is the core of your question):
pattern = #"(?<=[^0-9]):(?=[^0-9])"
This pattern will only match ':' symbols not surrounded by numbers.
Then replace with a dummy value that won't show in your data
dummy = #"NEVERSEETHIS"
NSRegularExpressions *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];
NSRange range = NSMakeRange(0, [string length])
NSString *modified= [regex replaceMatchesInString:yourString options:0 range:range withTemplate:dummy];
and finally, split
return [modified componentsSeparatedByString:dummy];

NSRegularExpression get only the regex

i have a problem and i don't undestand how to do this ( after 6hours or googling)
i'have a string named "filename" containt this text :"Aachen-Merzbrück EDKA\r\r\nVerkehr"
i want to use regex to only get this part "Aachen-Merzbrück EDKA" but i cant....
here my code :
NSString *expression = #"\\w+\\s[A-Z]{4}";
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:NSRegularExpressionCaseInsensitive error:&error];
NSString *noAirportString = [regex stringByReplacingMatchesInString:filename options:0 range:NSMakeRange(0, [filename length]) withTemplate:#""];
EDIT :
this one work good :
\S+\s+[A-Z]{4}
but now, how to get only this "Aachen-Merzbrück" EDKA from "Aachen-Merzbrück EDKA\r\r\nVerkehr"
my regex with NSRegularExpression return me the same string ....
A couple of issues in your question:
No need to match city name characters - there are always weird ones around (hyphens, apostrophes, etc.) You can just match the first "line" in your text with a test for the ICAO code as an extra security.
Using stringByReplacingMatchesInString: you actually remove the airport name (and ICAO code) that you want keep.
stringByReplacingMatchesInString: is a hacky (because it deletes things, so you need to make your regexes "negative") shortcut that sometimes works (I use it myself) but which risks confusing things - and future readers.
Having said that, a few changes will fix it:
NSString *filename = #"Aachen-Merzbrück EDKA\r\r\nVerkehr";
// Match anything from the beginning of the line up to a space and 4 upper case letters.
NSString *expression = #"^.+\\s[A-Z]{4}$";
NSError *error = NULL;
//Make sure ^ and $ match line endings,
//and make it case sensitive (the default) to explicitly
//match the 4 upper case characters of the ICAO code
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:NSRegularExpressionAnchorsMatchLines error:&error];
NSArray *matches = [regex matchesInString:filename
options:0
range:NSMakeRange(0, [filename length])];
// Check that there _is_ a match before you continue
if (matches.count == 0) {
// Error
}
NSRange airportNameRange = [[matches objectAtIndex: 0] range];
NSString *airportString = [filename substringWithRange: airportNameRange];
Thanks it's good working, but i use this one, it's work better in my case :
NSString *expression = #"\\S+\\s+[A-Z]{4}";

Getting the last word of an NSString

As the title suggests, I would like to get the last word out of an NSString.
I thought using this code:
NSArray *listItems = [someNSStringHere componentsSeparatedByString:#" "];
NSString *lastWordString = [NSString stringWithFormat:#"%#", listItems.lastObject];
anotherNSStringHere = lastWordString;
But I think the NSArray will take a time to load if it's big (and it is big), and it wouldn't recognize a word separated by a comma.
Thanks for helping!
If you want to be super-robust:
__block NSString *lastWord = nil;
[someNSStringHere enumerateSubstringsInRange:NSMakeRange(0, [someNSStringHere length]) options:NSStringEnumerationByWords | NSStringEnumerationReverse usingBlock:^(NSString *substring, NSRange subrange, NSRange enclosingRange, BOOL *stop) {
lastWord = substring;
*stop = YES;
}];
(This should also work with non-Roman languages; iOS 4+/OS X 10.6+.)
Basic explanation:
-enumerateSubstringsInRage:options:usingBlock: does what it says on the tin: it enumerates substrings, which are defined by what you pass in as the options. NSStringEnumerationByWords says "I want words given to me", and NSStringEnumerationReverse says "start at the end of the string instead of the beginning".
Since we're starting from the end, the first word given to us in substring will be the last word in the string, so we set lastWord to that, and then set the BOOL pointed to by stop to YES, so the enumeration stops right away.
lastWord is of course defined as __block so we can set it inside the block and see it outside, and it's initialized to nil so if the string has no words (e.g., if it's empty or is all punctuation) we don't crash when we try to use lastWord.
Give this a try:
NSRange range = [someNSStringHere rangeOfString:#" " options:NSBackwardsSearch];
NSString *result = [someNSStringHere substringFromIndex:range.location+1];
If you wanted to use a regular expression (which can be useful if you want to start getting more complicated in terms of what you're looking for at the end of your string), you could do something like:
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\S+\\Z" options:0 error:nil];
NSTextCheckingResult *found = [regex firstMatchInString:inputString options:0 range:NSMakeRange(0, [inputString length])];
if (found.range.location != NSNotFound)
result = [inputString substringWithRange:found.range];
That works great as it also recognizes symbols like # and # which enumerateSubstringsInRange: doesn't do.
NSCharacterSet *charSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSArray *components = [someString componentsSeparatedByCharactersInSet:charSet];
NSString *lastWord = components.lastObject;
The most efficient way is likely to start at the end of the string, examine each character to see if it's part of what you define as a word, and then extract the word you want using substringFromIndex: or substringWithRange:.
You can read symbols from the end of the your string and copy them at the 0 index to result string. Whether you read space or comma, result string wil contain the last word
You could use NSString's function rangeOfSubstring:options: to determine it. For example:
Search the string for a space, using a backwards search option to start the search from the end of the string.
NSRange r = [string rangeOfString:#" " options:NSBackwardsSearch];
This will find the location of the last word of the string. Now just get the string using substringWithRange: For Example:
NSRange found = NSMakeRange(NSMaxRange(r), string.length - NSMaxRange(r));
NSString *foundString = [string substringWithRange:found];
Where r is the range from earlier.
Also be careful to make sure that you check r actually exists. If there is only 1 word in the string, then r will be {NSNotFound, 0}
Hope I could help!
Ben

Objective-C NSString character substitution

I have a NSString category I am working on to perform character substitution similar to PHP's strtr. This method takes a string and replaces every occurrence of each character in fromString and replaces it with the character in toString with the same index. I have a working method but it is not very performant and would like to make it quicker and able to handle megabytes of data.
Edit (for clarity):
stringByReplacingOccurrencesOfString:withString:options:range: will not work. I have to take a string like "ABC" and after replacing "A" with "B" and "B" with "A" end up with "BAC". Successive invocations of stringByReplacingOccurrencesOfString:withString:options:range: would make a string like "AAC" which would be incorrect.
Suggestions would be great, sample code would be even better!
Code:
- (NSString *)stringBySubstitutingCharactersFromString:(NSString *)fromString
toString:(NSString *)toString;
{
NSMutableString *substitutedString = [self mutableCopy];
NSString *aCharacterString;
NSUInteger characterIndex
, stringLength = substitutedString.length;
for (NSUInteger i = 0; i < stringLength; ++i) {
aCharacterString = [NSString stringWithFormat: #"%C", [substitutedString characterAtIndex:i]];
characterIndex = [fromString rangeOfString:aCharacterString].location;
if (characterIndex == NSNotFound) continue;
[substitutedString replaceCharactersInRange:NSMakeRange(i, 1)
withString:[NSString stringWithFormat:#"%C", [toString characterAtIndex:characterIndex]]];
}
return substitutedString;
}
Also this code is executed after every change to text in a text view. It is passed the entire string every time. I know that there is a better way to do it, but I do not know how. Any suggestions for this would be most certainly appreciated!
You can make that kind of string substitution with NSRegularExpression either modifying an mutable string or creating a new immutable string. It will work with any two strings to substitute (even if they are more than one symbol) but you will need to escape any character that means something in a regular expression (like \ [ ( . * ? + etc).
The pattern finds either of the two substrings with the optional "anything" between and than replaces them with the two substrings with each other preserving the optional string between them.
// These string can be of any length
NSString *firstString = #"Axa";
NSString *secondString = #"By";
// Escaping of characters used in regular expressions has NOT been done here
NSString *pattern = [NSString stringWithFormat:#"(%#|%#)(.*?)(%#|%#)", firstString, secondString, firstString, secondString];
NSString *string = #"AxaByCAxaCBy";
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:&error];
if (error) {
// Insert error handling here...
}
NSString *modifiedString = [regex stringByReplacingMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
withTemplate:#"$3$2$1"];
NSLog(#"Before:\t%#", string); // AxaByCAxaCBy
NSLog(#"After: \t%#", modifiedString); // ByAxaCByCAxa