How properly extract substrings from an NSString without hardcoding positions? - ios

I have this string returning:
"Monday thru Friday 8:00 AM - 7:30 PM"
I need to eliminate the mon-fri stuff in order to get this:
"8:00AM - 7:30PM"
And then I need to split it into opening time and closing time in order to determine if its open or not:
"8:00AM" && "7:30PM"
But there are a lot of stores and they have different opening and closing times, so I cant just extract 6 characters from 8 or anything like that.
So far I decided to go this route:
NSRange startRange = [storeTime.text rangeOfString:#"-"];
NSString *openString = [storeTime.text substringWithRange:NSMakeRange(16, startRange.location-17)];
NSString *closeString = [storeTime.text substringWithRange:NSMakeRange(startRange.location+2, storeTime.text.length-(startRange.location+2))];
But it just seems like i could break because of the hardcoded start at 16 and it makes me wonder if it could break anywhere else. Any better ideas on how to achieve this?

You can use NSRegularExpression to grab the two time strings out of a string:
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:#"\\d{1,2}:\\d{2} (AM|PM)"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSString *str = #"Monday thru Friday 8:00 AM - 7:30 PM";
NSArray *m = [regex matchesInString:str
options:0
range:NSMakeRange(0, str.length)];

If you know the text before the time interval is always in the format <someday> thru <someday>, then you can find the index of the first numerical character (digit), and get a substring from that index.
Then, split the time strings on #" - " using the componentsSeparatedByString: method.
Example:
NSString *s = #"monday thru sunday, 0:00 - 23:59";
NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet];
int idx = [s rangeOfChatacterFromSet:digits].location;
NSString *timeStr = [s substringFromIndex:idx];
NSArray *timeStrings = [timeStr componentsSeparatedByString:#" - "];

How about first go by replacing occurrences of some strings.
So use the [string stringByReplacingOccurrencesOfString:#"Monday" withString: #""];
remove all the days and probably the "thru" string.
Then you're left with the hours. Don't forget to watch for space characters.
Hope this helps.

Related

Getting called only on first three numbers

I am getting phone numbers in format (888) 478-0041. And I am creating a link for that means if you tap on that phone number, it will automatically make a call. But in this format my call is going only on first three numbers is (888). so what should I do, I am not getting.
I have an string which is coming from a server :
<p>Your 2017 F-150 may need an oil change soon. Call </p><b>(888) 853-6045</b><p> or (888) 478-0041 click</p><b> here</b><p> to schedule online. Jacob Williams, Service Manager</p>
I want to remove any space after tel which comes up to 10 characters in the phone number.
I am using the below code for that.
if ([serviceMessage containsString:#"tel:"]) {
NSUInteger location = [serviceMessage rangeOfString:#"tel:"].location + 4;
serviceMessage = [serviceMessage substringFromIndex:location];
[serviceMessage substringWithRange:NSMakeRange(11,0)];
serviceMessage =[serviceMessage substringToIndex:10];
NSLog(#"New Trimmed string:%#",serviceMessage);
serviceMessage = [serviceMessage stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"Final Trimmed string:%#",serviceMessage);
}
You need to use replaceOccurrencesOfString:withString:options:range: if you just want to replace space within specific range and for multiple searching of tel: use NSRegularExpression.
NSString *serviceMessage = #"<p>Your 2017 F-150 may need an oil change soon. Call </p><b>(888) 853-6045</b><p> or (888) 478-0041 click</p><b> here</b><p> to schedule online. Jacob Williams, Service Manager</p>";
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(tel:)" options:0 error:&error];
NSArray *matches = [regex matchesInString:serviceMessage options:0 range:NSMakeRange(0, serviceMessage.length)];
for (NSTextCheckingResult *match in matches) {
NSRange wordRange = [match rangeAtIndex:1];
NSUInteger location = wordRange.location + 4;
serviceMessage = [serviceMessage stringByReplacingOccurrencesOfString:#" " withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(location, 13)];
}
NSLog(#"%#",serviceMessage);
Output
<p>Your 2017 F-150 may need an oil change soon. Call </p><b>(888) 853-6045</b><p> or (888) 478-0041 click</p><b> here</b><p> to schedule online. Jacob Williams, Service Manager</p>
"Mr. Aditya Pandey" have given a very nice and techfull solution. But if you are not more familiar with the iOS concepts and if you find that solution hard, you can also go with the option below:
if ([responceStr containsString:#"tel:"]) {
NSUInteger location = [responceStr rangeOfString:#"tel:"].location;
NSString *subStr = [responceStr substringFromIndex:location];
NSString *endStr;
if ([subStr containsString:#"\">"])
{
NSUInteger endLocation = [subStr rangeOfString:#"\">"].location;
endStr = [subStr substringToIndex:endLocation];
}
NSLog(#"New Trimed string:%#",endStr);
responceStr = [endStr stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"Final Trimed string:%#",responceStr);
}
You can again start searching for the next tel: from endStr.

Is it possible to detect links within an NSString that have spaces in them with NSDataDetector?

First off, I have no control over the text I am getting. Just wanted to put that out there so you know that I can't change the links.
The text I am trying to find links in using NSDataDetector contains the following:
<h1>My main item</h1>
<img src="http://www.blah.com/My First Image Here.jpg">
<h2>Some extra data</h2>
The detection code I am using is this, but it will not find this link:
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray *matches = [linkDetector matchesInString:myHTML options:0 range:NSMakeRange(0, [myHTML length])];
for (NSTextCheckingResult *match in matches)
{
if ([match resultType] == NSTextCheckingTypeLink)
{
NSURL *url = [match URL];
// does some stuff
}
}
Is this a bug with Apple's link detection here, where it can't detect links with spaces, or am I doing something wrong?
Does anyone have a more reliable way to detect links regardless of whether they have spaces or special characters or whatever in them?
I just got this response from Apple for a bug I filed on this:
We believe this issue has been addressed in the latest iOS 9 beta.
This is a pre-release iOS 9 update.
Please refer to the release notes for complete installation
instructions.
Please test with this release. If you still have issues, please
provide any relevant logs or information that could help us
investigate.
iOS 9 https://developer.apple.com/ios/download/
I will test and let you all know if this is fixed with iOS 9.
You could split the strings into pieces using the spaces so that you have an array of strings with no spaces. Then you could feed each of those strings into your data detector.
// assume str = <img src="http://www.blah.com/My First Image Here.jpg">
NSArray *components = [str componentsSeparatedByString:#" "];
for (NSString *strWithNoSpace in components) {
// feed strings into data detector
}
Another alternative is to look specifically for that HTML tag. This is a less generic solution, though.
// assume that those 3 HTML strings are in a string array called strArray
for (NSString *htmlLine in strArray) {
if ([[htmlLine substringWithRange:NSMakeRange(0, 8)] isEqualToString:#"<img src"]) {
// Get the url from the img src tag
NSString *urlString = [htmlLine substringWithRange:NSMakeRange(10, htmlLine.length - 12)];
}
}
I've found a very hacky way to solve my issue. If someone comes up with a better solution that can be applied to all URLs, please do so.
Because I only care about URLs ending in .jpg that have this problem, I was able to come up with a narrow way to track this down.
Essentially, I break out the string into components based off of them beginning with "http:// into an array. Then I loop through that array doing another break out looking for .jpg">. The count of the inner array will only be > 1 when the .jpg"> string is found. I then keep both the string I find, and the string I fix with %20 replacements, and use them to do a final string replacement on the original string.
It's not perfect and probably inefficient, but it gets the job done for what I need.
- (NSString *)replaceSpacesInJpegURLs:(NSString *)htmlString
{
NSString *newString = htmlString;
NSArray *array = [htmlString componentsSeparatedByString:#"\"http://"];
for (NSString *str in array)
{
NSArray *array2 = [str componentsSeparatedByString:#".jpg\""];
if ([array2 count] > 1)
{
NSString *stringToFix = [array2 objectAtIndex:0];
NSString *fixedString = [stringToFix stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
newString = [newString stringByReplacingOccurrencesOfString:stringToFix withString:fixedString];
}
}
return newString;
}
You can use NSRegularExpression to fix all URLs by using a simple regex to detect the links and then just encode the spaces (if you need more complex encoding you can look into CFURLCreateStringByAddingPercentEscapes and there are plenty of examples out there). The only thing that might take you some time if you haven't worked with NSRegularExpression before is how to iterate the results and do the replacing, the following code should do the trick:
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"src=\".*\"" options:NSRegularExpressionCaseInsensitive error:&error];
if (!error)
{
NSInteger offset = 0;
NSArray *matches = [regex matchesInString:myHTML options:0 range:NSMakeRange(0, [myHTML length])];
for (NSTextCheckingResult *result in matches)
{
NSRange resultRange = [result range];
resultRange.location += offset;
NSString *match = [regex replacementStringForResult:result inString:myHTML offset:offset template:#"$0"];
NSString *replacement = [match stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
myHTML = [myHTML stringByReplacingCharactersInRange:resultRange withString:replacement];
offset += ([replacement length] - resultRange.length);
}
}
Try this regex pattern: #"<img[^>]+src=(\"|')([^\"']+)(\"|')[^>]*>" with ignore case ... Match index=2 for source url.
regex demo in javascript: (Try for any help)
Demo
Give this snippet a try (I got the regexp from your first commentator user3584460) :
NSError *error = NULL;
NSString *myHTML = #"<http><h1>My main item</h1><img src=\"http://www.blah.com/My First Image Here.jpg\"><h2>Some extra data</h2><img src=\"http://www.bloh.com/My Second Image Here.jpg\"><h3>Some extra data</h3><img src=\"http://www.bluh.com/My Third-Image Here.jpg\"></http>";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"src=[\"'](.+?)[\"'].*?>" options:NSRegularExpressionCaseInsensitive error:&error];
NSArray *arrayOfAllMatches = [regex matchesInString:myHTML options:0 range:NSMakeRange(0, [myHTML length])];
NSTextCheckingResult *match = [regex firstMatchInString:myHTML options:0 range:NSMakeRange(0, myHTML.length)];
for (NSTextCheckingResult *match in arrayOfAllMatches) {
NSRange range = [match rangeAtIndex:1];
NSString* substringForMatch = [myHTML substringWithRange:range];
NSLog(#"Extracted URL : %#",substringForMatch);
}
In my log, I have :
Extracted URL : http://www.blah.com/My First Image Here.jpg
Extracted URL : http://www.bloh.com/My Second Image Here.jpg
Extracted URL : http://www.bluh.com/My Third-Image Here.jpg
You should not use NSDataDetector with HTML. It is intended for parsing normal text (entered by an user), not computer-generated data (in fact, it has many heuristics to actually make sure it does not detect computer-generated things which are probably not relevant to the user).
If your string is HTML, then you should use an HTML parsing library. There are a number of open-source kits to help you do that. Then just grab the href attributes of your anchors, or run NSDataDetector on the text nodes to find things not marked up without polluting the string with tags.
URLs really shouldn't contain spaces. I'd remove all spaces from the string before doing anything URL-related with it, something like the following
// Custom function which cleans up strings ready to be used for URLs
func cleanStringForURL(string: NSString) -> NSString {
var temp = string
var clean = string.stringByReplacingOccurrencesOfString(" ", withString: "")
return clean
}

How do I make an NSString that was replaced retain some of its old characters?

My app puts a space after the ":" in case the user forgets to. Like so:
input = [input stringByReplacingOccurrencesOfString:#":" withString:#": "];
This works well if they type in English. It will make "My friends are:James.." into "My friends are: James..." and this is fine.
BUT the problem I'm having is it also ads a space within a time. It will make "12:30" into "12: 30". I could make a bunch of these codes with all the number possibilities to fix, which is too much.
input = [input stringByReplacingOccurrencesOfString:#": 0" withString:#":0"];
What is the easier way to do this? I've tried:
input = [input stringByReplacingOccurrencesOfString:#"\\b\\d\\d?:\\s\\d\\d\\b" withString:#"\\b\\d\\d?:\\d\\d\\b" options: NSRegularExpressionSearch range:NSMakeRange(0, [input length])];
But all that does is change ALL times from "hh: mm" to "bdd?:ddb" and that's it. how do I make an NSString replacement retain it's previous characters? Like how can I make it still keep the same numbers that were previously put? All I want to change is the ": " to be ":" in a time.
I've tried "if" statements with NSNotFound but it wasn't working. I wanted it that if it found a "hh:mm" format, to NOT add a space, but if it didn't, to add a space but that didn't work.
You were on the right lines with the last version, but you need to use NSRegularExpression. The following should do the whole thing for you (adding spaces after colons, but not when sandwiched between double digits or already followed by a space):
NSString *input = #"My friends are:James, John. It's 10:30 right now.";
NSMutableString *workingString = [input mutableCopy];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(((?<!(\\b\\d\\d)):)|(:(?!(\\d\\d\\b))))(?!\\s)" options:0 error:nil];
[regex replaceMatchesInString:workingString options:0 range:NSMakeRange(0, [workingString length]) withTemplate:#": "];
input = [workingString copy];
NSLog(#"%#", input); // Prints "My friends are: James, John. It's 10:30 right now."
You can do it in this way..If you have either time in string or only sentence with no numbers...
NSCharacterSet *s = [NSCharacterSet characterSetWithCharactersInString:#"1234567890"];
NSRange r = [input rangeOfCharacterFromSet:s];
if (r.location != NSNotFound)
{
input = [input stringByReplacingOccurrencesOfString:#":" withString:#": "];
}
Hope it helps you..

Modifying Occurrences of Time Format Regex in a Long String

I have strings containing Time values like 08:30:00AM and/or 12:45:00PM etc.
Now what i need is to remove the part containing 'seconds' value in these strings to make upper strings like 08:30AM and/or 12:45PM etc.
I can't use NSDateFormatter as it's not necessary that the string always contain time string in HH:MM:SS value.. There could be any string value else.. That's why I have to do it through patttern matching.
I know, i can use the following code. But what I need to know is what will be the regex to find and modify the string.
NSString *string = #"string containing 08:30:00AM values and some text";
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"???" options:NSRegularExpressionCaseInsensitive error:&error];
NSString *modifiedString = [regex stringByReplacingMatchesInString:string options:0 range:NSMakeRange(0, [string length]) withTemplate:#""];
NSLog(#"%#", modifiedString);
Thanks in advance..
If it is time, then you can use dateformatter:
NSString *string = #"08:30:00AM"; //This is your input time
NSDateFormatter *df=[NSDateFormatter new];
[df setDateFormat:#"hh:mm:ssa"];
NSDate *nowTime=[df dateFromString:string];
[df setDateFormat:#"hh:mma"];
NSString *outputTime=[df stringFromDate:nowTime];
NSLog(#"time is : %#",outputTime);
Output: 08:30AM
EDIT:
Alternate way:
If you are sure your time is always in given format as 08:30:00AM and 08:30:05AM then you can use:
NSString *string = #"08:30:00AM";
NSString *aMPM=[string substringFromIndex:string.length-2];
NSString *noSecondAMPM= [string substringToIndex:string.length-5];
NSString *myTime=[NSString stringWithFormat: #"%#%#",noSecondAMPM,aMPM];
NSLog(#"time is : %#",myTime);
Just to give an answer along the lines of your own question, this regex pattern will match the time formats you are after and the parenthesis will capture the hour and minute part and allow you to keep them in the modified string:
NSString *timeText = #"This is a text with 08:30:23 and 9:23:54AM to test both formats.";
NSString *timePattern = #"([0-9]{1,2}:[0-9]{2}):[0-9]{2}";
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:timePattern
options:0
error:&error];
NSString *modifiedString = [regex stringByReplacingMatchesInString:timeText
options:0
range:NSMakeRange(0, timeText.length)
withTemplate:#"$1"];
The result is this:
This is a text with 08:30 and 9:23AM to test both formats.
The $n (where n is a digit) in the replacement string refers to the nth capture group (parenthesis) in the regex pattern - in this case the hours, the colon and the minutes.
As you mentioned yourself, using regexes you can do the substitution directly in the text with no further parsing needed.
BTW, there are no letters in this pattern, so you don't need NSRegularExpressionCaseInsensitive.
This site is useful, e.g. the discussion on replacement strings and advanced syntax.

add whitespace to string

I have the following string
NSString *word1=#"hitoitatme";
as you can see, if you were to add a space after every second character, it would be a string of words that contain min/max 2 characters.
NSString *word2=#"hi to it at me";
I want to be able to add a white character space to my string after every 2 characters. How would I go about doing this? So if I have a string such as word1, I can add some code to make it look like word2? I am looking if possible for the most efficient way of doing this.
Thank you in advance
There might be different ways to add white space in the string but one way could be using NSRegularExpression
NSString *originalString = #"hitoitatme";
NSRegularExpression *regexp = [NSRegularExpression regularExpressionWithPattern:#"([a-z])([a-z])" options:0 error:NULL];
NSString *newString = [regexp stringByReplacingMatchesInString:originalString options:0 range:NSMakeRange(0, originalString.length) withTemplate:#"$0 "];
NSLog(#"Changed %#", newString);//hi to it at me
You can do this way:
NSString *word1=#"hitoitatme";
NSMutableString *toBespaced=[NSMutableString new];
for (NSInteger i=0; i<word1.length; i+=2) {
NSString *two=[word1 substringWithRange:NSMakeRange(i, 2)];
[toBespaced appendFormat:#"%# ",two ];
}
NSLog(#"%#",toBespaced);

Resources