I am aware I can do textView.dataDetectorTypes = UIDataDetectorTypeLink; to auto detect links.
Now this seems straightforward but I can't find a way to check if the UITextView's dataDetector actually found a match.
I'm looking for something like BOOL hasLink = textView.hasLink; or BOOL hasLink = textView.text.hasLink; or maybe even a way to pull a reference to the NSDataDetector(I could probably use NSDataDetector methods to check but I'd have to confirm, just throwing out ideas)
Is there no built in way to check this or do I have to confirm it independently?
Edit:
So I just used my own NSDataDetector to do this, (code below) but the question remains as to whether there really is no way of extracting a detected data type or confirming the existence of a detected data type in a UITextView?
This is what I used to check myself:
-(BOOL) doesTextHaveLink:(NSString *)text {
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray *matches = [linkDetector matchesInString:text options:0 range:NSMakeRange(0, [text length])];
for (NSTextCheckingResult *match in matches) {
if ([match resultType] == NSTextCheckingTypeLink) {
return YES;
}
}
return NO;
}
plz use this code this return yes if textview has url
-(BOOL)checkUrl:(NSString *)strText{
NSString *urlRegEx =
#"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSPredicate *urlTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
return [urlTest evaluateWithObject:strText];
}
Related
I apologize if this is a repeat, but I honestly have done my best to research this and haven't come up with much.
I'm making an iPad reference app that will make several large textbooks searchable (maybe 3-4k pages in total). It's a fairly simple idea: the user can choose any combination of texts to search, put in his term, and the app will find those terms in all the texts and return them, indexed in a table view.
The view controller has a series of switches, the value of which get read by a method into an NSSet and passed to the search controller. That part works.
I have a SearchController class which the view controller instantiates and calls a method on:
-(void)performSearchWithString:(NSString *)searchString andTexts:(NSSet *)texts
{
for (id book in texts) {
if ([book isEqual:kBook1]){
NSError *error = nil;
NSURL *url = [[NSBundle mainBundle] URLForResource:#"neiJing" withExtension:#"txt"];
NSString *text = [NSString stringWithContentsOfURL:url encoding:NSStringEncodingConversionAllowLossy error:&error];
[text enumerateSubstringsInRange:NSMakeRange(0, [text length])
options:NSStringEnumerationByWords
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
NSRange found = [substring rangeOfString:text];
if (found.location != NSNotFound) {
NSLog(#"%#", substring);
} else {
NSLog(#"Not found");
}
}];
}
I seem to have succeeded in enumerating through every word in all the texts, but no return values are found so I just get a never-ending stream of "Not found".
I inserted a test phrase into each text that I know for certain to be there, but it's not coming up.
I have a feeling I'm going about this all wrong. Even if I made this work, the performance hit might be too big for a useable app...I'm still trying to wrap my head around blocks, too. I just haven't found any ready-baked solutions out there for searching large volumes of text and picking out results. If anyone has any hints or references to an open-source library that I might adapt, I would be very grateful.
It looks like you're searching for the whole text inside of each substring passed to the block. This line is the problem (and causes a retain cycle):
NSRange found = [substring rangeOfString:text];
The code needs to look for something it can find:
NSString *findMe = #"A string we expect to find";
[text enumerateSubstringsInRange:NSMakeRange(0, [text length])
options:NSStringEnumerationByWords
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if ([findMe isEqualToString:substring] ) {
NSLog(#"Found %#", substring);
} else {
NSLog(#"Not found");
}
}];
I don't think the block of code is what you want. It loops through each word in the text and you want to find all the search strings. Here is a loop that sets a new search range based on the last successful match.
-(void)performSearchWithString:(NSString *)searchString andTexts:(NSSet *)texts
{
for (id book in texts) {
if ([book isEqual:kBook1]){
NSError *error = nil;
NSURL *url = [[NSBundle mainBundle] URLForResource:#"neiJing" withExtension:#"txt"];
NSString *text = [NSString stringWithContentsOfURL:url encoding:NSStringEncodingConversionAllowLossy error:&error];
NSRange searchRange = NSMakeRange(0, [text length]);
NSRange match = [text rangeOfString:searchString options:0 range:searchRange];
while (match.location != NSNotFound) {
// match is the range of the current successful match
NSLog(#"matching range -- %#", NSStringFromRange(match));
NSUInteger locationOfNextSearchRange = NSMaxRange(match);
searchRange = NSMakeRange(locationOfNextSearchRange, [text length] - locationOfNextSearchRange);
match = [text rangeOfString:searchString options:0 range:searchRange];
}
}
}
i use this function to check if an URL is valid or not. it works for most of the cases, but here the following URL it return that is not Valid!
http://www.iphonedevsdk.com/forum/iphone-sdk-development/11841-stringbytrimmingcharactersinset.html
what can i do to improve the regular expression of my function that it will cover all URL types?
- (BOOL) urlIsValiad: (NSString *) url
{
NSString *regex =
#"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSPredicate *regextest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
if ([regextest evaluateWithObject: url] == YES) {
NSLog(#"URL is valid!");
} else {
NSLog(#"URL is not valid!");
}
return [regextest evaluateWithObject:url];
}
This would be my attempt:
((?:http|https)://)?(?:www\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?
EDIT
Added in a back-reference (?<=/)for extra specificity
EDIT 2
Added optional search for country name in URL (eg www.google.com.in)
Try to change the *regex = line with following.
NSString *regex = "(http|ftp|https)://[\w-_]+(.[\w-_]+)+([\w-.,#?^=%&:/~+#]* [\w-\#?^=%&/~+#])?"
Well, I found something on Microsoft site and with slight changes it's the best expression so far:
#"(?:[A-Za-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[A-Za-z0-9!#$%\\&'*+/=?\\^_`{|}"
#"~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\"
#"x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[A-Za-z0-9](?:[A-Za-"
#"z0-9-]*[A-Za-z0-9])?\\.)+[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?|\\[(?:(?:25[0-5"
#"]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-"
#"9][0-9]?|[A-Za-z0-9-]*[A-Za-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21"
#"-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])";
Old thread but I think my amendments may be helpful to someone else landing here:
I didn't like to check only for a "www" as a hostname, nowadays we may encounter different hostnames... Thus I extended the Regex with:
"((?:http|https)://)?(?:[A-Za-z0-9]{1,25}\\.)?[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?"
-(BOOL)validateUrl{
// NSString *urlRegEx = #"((?:http|https)://)?(www\\.)[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?";
NSString *urlRegEx = #"((?:http|https)://)?(www\\.)[\\w\\d\\-_]+\\.\\w{2,3}(\\.\\w{2})?(/(?<=/)(?:[\\w\\d\\-./_]+)?)?";
NSPredicate *urlTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
if ([urlTest evaluateWithObject: self] == YES) {
NSLog(#"URL is valid!");
} else {
NSLog(#"URL is not valid!");
}
return [urlTest evaluateWithObject:self];
}
I currently have a plist file in my iOS Project which is downloaded from the web when updates are available and it contains a list of news articles along with images.
The application caches the images on the iPhone for offline access, I am currently trying to write a function which will clean the cached files every so often.
Currently I have this code which looks in the Temp folder for images and then deletes them, however for each image found I would like it to check if the file name exists as a value in the plist stored as NSDictionary before deleting, however I am not sure of a quick method to search the NSDictionary without the need for a for statement.
Any tips would be great.
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:TMP error:nil];
if (files == nil) {
// error...
NSLog(#"no files found");
}
for (NSString *file in files) {
NSString *uniquePath = [TMP stringByAppendingPathComponent: file];
if([file rangeOfString: #".png" options: NSCaseInsensitiveSearch].location != NSNotFound)
{
NSLog(#"%#", file);
if ([[NSFileManager defaultManager] removeItemAtPath: uniquePath error: NULL] == YES)
NSLog (#"Remove successful");
else
NSLog (#"Remove failed");
}
}
EDIT
I have currently added this not sure if its the best way to do it but it works.
NSArray *newsArray = [self.newsData allValues];
// Convert the Array into a string
NSString *newsString = [newsArray description];
// Perform Range Search.
NSRange range;
range = [newsString rangeOfString : filename];
if (range.location != NSNotFound) {
NSLog(#"The file exists in the plist %#", filename);
} else {
// Delete the file
}
You could reduce the array so that it only contains the objects you are interested in by using a NSPredicate, then quickly loop over the objects which you wish to delete. Like so:
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:TMP error:nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF CONTAINS[cd] '.png'"];
NSArray *filteredArray = [files filteredArrayUsingPredicate:thePredicate];
for (NSString *file in filteredArray) {
NSString *uniquePath = [TMP stringByAppendingPathComponent:file];
if ([[NSFileManager defaultManager] removeItemAtPath: uniquePath error: NULL])
NSLog (#"Remove successful");
else
NSLog (#"Remove failed");
}
This will mean that the for loop is only looping over objects you are interested in.
Since you do not care about the sequence of files in plist or folder and you obviously won't have duplication, use NSSet rather than NSArray and then use intersect method (intersectsSet:) to find intersection.
I need RegexKitlite in my App as a part of String validation.
Have also added libicucore.A.dylib .
Currently working with xcode 4.2,Base sdk iOS 5.0,Apple LLVM compiler 3.0,architechture armv7.
Adding the regexkit folder to my app, causes too many errors like
Automatic Reference Counting Errors ,
Cast of Objective-C pointer type 'NSString *' to C pointer type 'CFStringRef' etc
Please help;where have I gone wrong.
You can also disable the ARC for the RegexKitLite only by adding a flag:
select the project -> YOUR Target -> on the Tab the "Build Phases" and open the "Compile Sources" and add for "RegexKitLite.m" the flag "-fno-objc-arc".
Update:
If you get:
Undefined symbols:
"_uregex_reset", referenced from:
_rkl_splitArray in RegexKitLite.o
_rkl_replaceAll in RegexKitLite.o
"_uregex_appendTail", referenced from:.......
Then you need to add in the Tab "Build Settings" -> "Linking" -> "Other Linker Flags" the "-licucore"
You aren't doing anything wrong. Regexkit just hasn't been updated to iOS 5 yet. The big change in iOS 5 is there are no longer retains, releases, or autoreleases. Every memory thing is automatic like Java. (Except that it happens at compile time instead of run time. So it's conceptually like Java. Mostly.)
Anyway, instead of waiting for Regexkit to update you can use NSRegularExpression. Using Apple stuff is also future-proof since they keep their own stuff updated version to version.
Good luck!
CBGraham is right. Alternatively, you could disable automatic reference counting (Project > Build settings > search for 'automatic reference counting').
You will obviously have to do manual reference counting, but RegexKitLite should build now...
I replaced RegexKitLite with there two methods.
String Results:
+(NSString*) regExString: (NSString *) pattern withString: (NSString *) searchedString {
NSError *error = nil;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:searchedString options:0 range: NSMakeRange(0, [searchedString length])];
if ([searchedString substringWithRange:[match rangeAtIndex:1]]) {
return [searchedString substringWithRange:[match rangeAtIndex:1]];
} else {
return #"";
}
}
Array of Results:
+(NSArray *) regExArray:(NSString *) pattern withString: (NSString *) searchedString {
NSMutableArray *results = [[NSMutableArray alloc] init];
NSError *error;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
NSArray* matches = [regex matchesInString:searchedString options:0 range: NSMakeRange(0, searchedString.length)];
for (NSTextCheckingResult* match in matches) {
NSMutableArray *result = [NSMutableArray array];
NSRange matchRange = [match range];
NSString *numString = [searchedString substringWithRange:matchRange];
[result addObject:numString];
for (int i=1;i < (int)match.numberOfRanges;i++) {
NSRange range = [match rangeAtIndex:i];
#try {
NSString *numString = [searchedString substringWithRange:range];
[result addObject:numString];
}
#catch (NSException *exception) {
[result addObject:[NSNull null]];
}
}
[results addObject:result];
}
return results;
}
In the context of an iPhone app I am developing, I am parsing some html to extract data to map, using NSRegularExpression. This information is updated whenever the user "pans" the map to a new location.
This works fine the first time or two through, but on the second or third time the function is called, the application hangs. I have used XCode's profiler to confirm I am not leaking memory, and no error is generated (the application does not terminate, it just sits in execution at the point shown below).
When I examine the HTML being parsed, I do not see that it is incomplete or otherwise garbled when the application hangs.
Furthermore, if I replace the regex code with a collection of explicitly address strings, everything works as expected.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// receivedData contains the returned HTML
NSString *result = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
NSError *error = nil;
NSString *pattern = #"description.*?h4>(.*?)<\\/h4>.*?\"address>[ \\s]*(.*?)<.*?zip>.*?(\\d{5,5}), US<";
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:pattern
options:NSRegularExpressionDotMatchesLineSeparators
error:&error];
__block NSUInteger counter = 0;
// the application hangs on the next line after 1-2 times through
[regex enumerateMatchesInString:result options:0 range:NSMakeRange(0, [result length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
NSRange range = [match rangeAtIndex:2];
NSString *streetAddress =[result substringWithRange:range];
range = [match rangeAtIndex:3];
NSString *cityStateZip = [result substringWithRange:range];
NSString *address = [NSString stringWithFormat:#"%# %#", streetAddress, cityStateZip];
EKItemInfo *party = [self addItem:address]; // geocode address and then map it
if (++counter > 4) *stop = true;
}];
[receivedData release];
[result release];
[connection release]; //alloc'd previously, so release here.
}
I realize this is going to be difficult/impossible to duplicate, but I was wondering if anyone has run into a similar issue with NSRegularExpression or if there is something obviously wrong here.
I also have encountered the regular expression exception, too. In my case, the problem was Character Encoding. So that I wrote a code to go well with several character encoding. Maybe this code help you.
+ (NSString *)encodedStringWithContentsOfURL:(NSURL *)url
{
// Get the web page HTML
NSData *data = [NSData dataWithContentsOfURL:url];
// response
int enc_arr[] = {
NSUTF8StringEncoding, // UTF-8
NSShiftJISStringEncoding, // Shift_JIS
NSJapaneseEUCStringEncoding, // EUC-JP
NSISO2022JPStringEncoding, // JIS
NSUnicodeStringEncoding, // Unicode
NSASCIIStringEncoding // ASCII
};
NSString *data_str = nil;
int max = sizeof(enc_arr) / sizeof(enc_arr[0]);
for (int i=0; i<max; i++) {
data_str = [
[NSString alloc]
initWithData : data
encoding : enc_arr[i]
];
if (data_str!=nil) {
break;
}
}
return data_str;
}
You can download the whole category library from GitHub and just run it. I wish this helps you.
https://github.com/weed/p120801_CharacterEncodingLibrary
Maybe the answer to this question can be found at: NSRegularExpression enumerateMatchesInString: [...] usingBlock does never stop .
I had this issue solved by passing NSMatchingReportCompletion as option and by setting stop to YES when the match is nil.