Detect whole word in NSStrings - ios

How do I detect if an NSString contains a specific word, e.g. is.
If the NSString is Here is my string. His isn't a mississippi isthmus. It is...? The method should detect the word is and return YES.
However, if the NSString is His isn't a mississipi isthmus, it should return NO.
I tried using if ([text rangeOfString:#"is" options:NSCaseInsensitiveSearch].location != NSNotFound) { ... } but it detects characters not words.

Use "regular expression" search with the "word boundary pattern" \b:
NSString *text = #"Here is my string. His isn't a mississippi isthmus. It is...";
NSString *pattern = #"\\bis\\b";
NSRange range = [text rangeOfString:pattern options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
if (range.location != NSNotFound) { ... }
This works also for cases like "Is it?" or "It is!", where the word is not surrounded by spaces.
In Swift 2 this would be
let text = "Here is my string. His isn't a mississippi isthmus. It is..."
let pattern = "\\bis\\b"
if let range = text.rangeOfString(pattern, options: [.RegularExpressionSearch, .CaseInsensitiveSearch]) {
print ("found:", text.substringWithRange(range))
}
Swift 3:
let text = "Here is my string. His isn't a mississippi isthmus. It is..."
let pattern = "\\bis\\b"
if let range = text.range(of: pattern, options: [.regularExpression, .caseInsensitive]) {
print ("found:", text.substring(with: range))
}
Swift 4:
let text = "Here is my string. His isn't a mississippi isthmus. It is..."
let pattern = "\\bis\\b"
if let range = text.range(of: pattern, options: [.regularExpression, .caseInsensitive]) {
print ("found:", text[range])
}
Swift 5 (using the new raw string literals):
let text = "Here is my string. His isn't a mississippi isthmus. It is..."
let pattern = #"\bis\b"#
if let range = text.range(of: pattern, options: [.regularExpression, .caseInsensitive]) {
print ("found:", text[range])
}

Use NSRegularExpressionSearch option with \b to match word boundary characters.
Like this:
NSString *string = #"Here is my string. His isn't a mississippi isthmus. It is...";
if(NSNotFound != [string rangeOfString:#"\\bis\\b" options:NSRegularExpressionSearch].location) {//...}

What about
if ([text rangeOfString:#" is " options:NSCaseInsensitiveSearch].location != NSNotFound) { ... }

You could use regular expressions, as suggested, or you could analyze the words linguistically:
NSString *string = #"Here is my string. His isn't a mississippi isthmus. It is...";
__block BOOL containsIs = NO;
[string enumerateLinguisticTagsInRange:NSMakeRange(0, [string length]) scheme:NSLinguisticTagSchemeTokenType options:NSLinguisticTaggerOmitPunctuation | NSLinguisticTaggerOmitWhitespace | NSLinguisticTaggerOmitOther orthography:nil usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop){
NSString *substring = [string substringWithRange:tokenRange];
if (containsIs)
if ([substring isEqualToString:#"n't"])
containsIs = NO; // special case because "isn't" are actually two separate words
else
*stop = YES;
else
containsIs = [substring isEqualToString:#"is"];
}];
NSLog(#"'%#' contains 'is': %#", string, containsIs ? #"YES" : #"NO");

Related

Regular Expression and NSAttributedString to change colour of all the same words UILabel [duplicate]

There is a substring that occurs in a string several times. I use rangeOfString, but it seems that it can only find the first location. How can I find all the locations of the substring?
NSString *subString1 = #"</content>";
NSString *subString2 = #"--\n";
NSRange range1 = [newresults rangeOfString:subString1];
NSRange range2 = [newresults rangeOfString:subString2];
int location1 = range1.location;
int location2 = range2.location;
NSLog(#"%i",location1);
NSLog(#"%i",location2);
You can use rangeOfString:options:range: and set the third argument to be beyond the range of the first occurrence. For example, you can do something like this:
NSRange searchRange = NSMakeRange(0,string.length);
NSRange foundRange;
while (searchRange.location < string.length) {
searchRange.length = string.length-searchRange.location;
foundRange = [string rangeOfString:substring options:0 range:searchRange];
if (foundRange.location != NSNotFound) {
// found an occurrence of the substring! do stuff here
searchRange.location = foundRange.location+foundRange.length;
} else {
// no more substring to find
break;
}
}
Swift 3.0
Find all locations of substring i
let text = "This is the text and i want to replace something"
let mutableAttributedString = NSMutableAttributedString(string: text)
var searchRange = NSRange(location: 0, length: text.characters.count)
var foundRange = NSRange()
while searchRange.location < text.characters.count {
searchRange.length = text.characters.count - searchRange.location
foundRange = (text as NSString).range(of: "i", options: NSString.CompareOptions.caseInsensitive, range: searchRange)
if foundRange.location != NSNotFound {
// found an occurrence of the substring! do stuff here
searchRange.location = foundRange.location + foundRange.length
mutableAttributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: foundRange)
}
else {
// no more substring to find
break
}
}
//Apply
textLabel.attributedText = mutableAttributedString;
And this output-
This is my solution. Basically, the algorithm traverses the string looking for substring matches and returns those matches in an array.
Since an NSRange is a struct it cannot be added to the array directly. By using NSValue, I can encode the match first and then add it to the array. To retrieve the range, I then decode the NSValue object to an NSRange.
#import <Foundation/Foundation.h>
NSRange makeRangeFromIndex(NSUInteger index, NSUInteger length) {
return NSMakeRange(index, length - index);
}
NSArray<NSValue *> * allLocationsOfStringMatchingSubstring(NSString *text, NSString *pattern) {
NSMutableArray *matchingRanges = [NSMutableArray new];
NSUInteger textLength = text.length;
NSRange match = makeRangeFromIndex(0, textLength);
while(match.location != NSNotFound) {
match = [text rangeOfString:pattern options:0L range:match];
if (match.location != NSNotFound) {
NSValue *value = [NSValue value:&match withObjCType:#encode(NSRange)];
[matchingRanges addObject:value];
match = makeRangeFromIndex(match.location + 1, textLength);
}
}
return [matchingRanges copy];
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *text = #"TATACCATGGGCCATCATCATCATCATCATCATCATCATCATCACAG";
NSString *pattern = #"CAT";
NSArray<NSValue *> *matches = allLocationsOfStringMatchingSubstring(text, pattern);
NSLog(#"Text: %#", text);
NSLog(#"Pattern: %#", pattern);
NSLog(#"Number of matches found: %li", matches.count);
[matches enumerateObjectsUsingBlock:^(NSValue *obj, NSUInteger idx, BOOL *stop) {
NSRange match;
[obj getValue:&match];
NSLog(#" Match found at index: %li", match.location);
}];
}
return 0;
}
Passing nil to [string rangeOfString:substring options:nil range:searchRange]; shows a warning.
To get rid of the warning, put in an enum from this group
enum {
NSCaseInsensitiveSearch = 1,
NSLiteralSearch = 2,
NSBackwardsSearch = 4,
NSAnchoredSearch = 8,
NSNumericSearch = 64,
NSDiacriticInsensitiveSearch = 128,
NSWidthInsensitiveSearch = 256,
NSForcedOrderingSearch = 512,
NSRegularExpressionSearch = 1024
};
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/doc/constant_group/Search_and_Comparison_Options
Here is a version in Swift 2.2 of PengOne's answer with input from kevinlawler and Gibtang
Note: string and substring are of type NSString
let fullStringLength = (string as String).characters.count
var searchRange = NSMakeRange(0, fullStringLength)
while searchRange.location < fullStringLength {
searchRange.length = fullStringLength - searchRange.location
let foundRange = string.rangeOfString(substring as String, options: .CaseInsensitiveSearch, range: searchRange)
if foundRange.location != NSNotFound {
// found an occurrence of the substring! do stuff here
searchRange.location = foundRange.location + 1
} else {
// no more strings to find
break
}
}
I suggest using regular expression because it's a more declarative way and has fewer lines of code to write.
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"%#" options:nil error:nil];
NSString *toSearchStr = #"12312 %# Text %# asdsa %#";
__block int occurs = 0;
[regex enumerateMatchesInString:toSearchStr options:0 range:NSMakeRange(0, toSearchStr.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
occurs++;
}];
// occurs == 3

Should change the values if zero is present while moving to other viewController

I have to enter a phone number in UITextfield when I press the nextButton,that number should appear in the next view controller in UILabel .But the problem is if I enter a number starting with 00(two zeros) in the textfield it should change to ++ in the next viewcontroller. Any help would be appreciated.Thanks
Do this
if([oldString hasPrefix:#"00"]) {
NSRange range = NSMakeRange(0,2);
NSString *newText = [oldString stringByReplacingCharactersInRange:range withString:#"++"];
}
This will replace the occurence of 00 in the beginning with ++
For Swift
let str = "00044400"
if str.hasPrefix("00") {
let i = 2
let range = str.index(str.startIndex, offsetBy: 0)..<str.index(str.startIndex, offsetBy: i)
print(str.replacingCharacters(in: range, with:"++"))
}
For Objective-C
NSString *str = #"00044004";
NSString *newString = #"";
if ([str hasPrefix:#"00"]) {
NSRange range = NSMakeRange(0, 2);
newString = [str stringByReplacingCharactersInRange:range withString:#"++"];
NSLog(#"NewString = %#", newString);
}

How to get the first alphabet character of a string in iOS

I have an example NSString in iOS
NSString* str = #"-- This is an example string";
I want to get the first alphabet letter. The result of above situation is letter "T" from word "This". Some characters before letter "T" is not alphabet letter so it returns the first alphabet letter is "T".
How can I retrieve it? If the string not contain any alphabet letter, it can return nil.
Besides, the result can be a NSRange
NSRange range = [string rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]];
First create a NSCharecterSet as a global variable and write this code
-(void)viewDidLoad{
NSCharacterSet *s = [NSCharacterSet characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
s = [s invertedSet];
NSString *myString = #"--- This is a string";
NSArray *arrayOfStrings = [myString componentsSeparatedByString:#" "];
for(int i=0;i<arrayOfStrings.count){
NSString *current = [arrayOfStrings objectAtIndex:i];
char c = [self returnCharacter:current];
if(c == nil){
//that means first word is not with alphabets;
}
else {
NSLog(#"%c",c);
//your output.
}
}
}
And here is the method
-(char)returnChracter:(NSString*)string{
NSRange r = [string rangeOfCharacterFromSet:s];
if (r.location != NSNotFound) {
NSLog(#"the string contains illegal characters");
return nil;
}
else {
//string contains all alphabets
char firstLetter = [string charAtIndex:0];
return firstLetter;
}
}
You can use the following function. Pass a string and get first character as a string.
-(NSString*)getFirstCharacter:(NSString*)string
{
for(int i=0;i<string.length;i++)
{
unichar firstChar = [string characterAtIndex:i];
NSCharacterSet *letters = [NSCharacterSet letterCharacterSet];
if ([letters characterIsMember:firstChar]) {
return [NSString:stringWithFormat:#"%c",firstChar];
}
}
return nil;
}

iphone sdk : Break chinese sentence into words and letters

I have Chinese news feed and I want to break the sentence into smaller chunks to pass to the API.
How can I do it in ios? I have set character length of 50 characters for English language.
Currently I am using rangeOfString: function to find dot, comma and break into sentence.
NSString *str = nil, *rem = nil;
str = [final substringToIndex:MAX_CHAR_Private];
rem = [final substringFromIndex:MAX_CHAR_Private];
NSRange rng = [rem rangeOfString:#"?"];
if (rng.location == NSNotFound) {
rng = [rem rangeOfString:#"!"];
if (rng.location == NSNotFound) {
rng = [rem rangeOfString:#","];
if (rng.location == NSNotFound) {
rng = [rem rangeOfString:#"."];
if (rng.location == NSNotFound) {
rng = [rem rangeOfString:#" "];
}
}
}
}
if (rng.location+1 + MAX_CHAR_Private > MAXIMUM_LIMIT_Private) {
rng = [rem rangeOfString:#" "];
}
if (rng.location == NSNotFound) {
remaining = [[final substringFromIndex:MAX_CHAR_Private] retain];
}
else{
//NSRange rng = [rem rangeOfString:#" "];
str = [str stringByAppendingString:[rem substringToIndex:rng.location]];
remaining = [[final substringFromIndex:MAX_CHAR_Private + rng.location+1] retain];
}
This is not working correctly for chinese and japanese characters.
Check NSLinguisticTagger, It should work with Chinese:
From Apple: "The NSLinguisticTagger class is used to automatically segment natural-language text and tag it with information, such as parts of speech. It can also tag languages, scripts, stem forms of words, etc."
Apple documentation NSLinguisticTagger Class Reference
Also see NSHipster NSLinguisticTagger.
Also see objc.io issue 7
NSString provides that out of the box with NSStringEnumerationBySentences enumeration option:
[string enumerateSubstringsInRange:NSMakeRange(0, [string length])
options:NSStringEnumerationBySentences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop)
{
NSString *sentence = [substring stringByTrimmingCharactersInSet:whiteSpaceSet];
// process sentence
}
];

How can I extract a URL from a sentence that is in a NSString?

What I'm trying to accomplish is as follows. I have a NSString with a sentence that has a URL within the sentience. I'm needing to be able to grab the URL that is presented within any sentence that is within a NSString so for example:
Let's say I had this NSString
NSString *someString = #"This is a sample of a http://example.com/efg.php?EFAei687e3EsA sentence with a URL within it.";
I need to be able to extract http://example.com/efg.php?EFAei687e3EsA from within that NSString. This NSString isn't static and will be changing structure and the url will not necessarily be in the same spot of the sentence. I've tried to look into the three20 code but it makes no sense to me. How else can this be done?
Use an NSDataDetector:
NSString *string = #"This is a sample of a http://example.com/efg.php?EFAei687e3EsA sentence with a URL within it.";
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray *matches = [linkDetector matchesInString:string options:0 range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches) {
if ([match resultType] == NSTextCheckingTypeLink) {
NSURL *url = [match URL];
NSLog(#"found URL: %#", url);
}
}
This way you don't have to rely on an unreliable regular expression, and as Apple upgrades their link detection code, you get those improvements for free.
Edit: I'm going to go out on a limb here and say you should probably use NSDataDetector as Dave mentions. Far less prone to error than regular expressions.
Take a look at regular expressions. You can construct a simple one to extract the URL using the NSRegularExpression class, or find one online that you can use. For a tutorial on using the class, see here.
The code you want essentially looks like this (using John Gruber's super URL regex):
NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:#"(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))" options:NSRegularExpressionCaseInsensitive error:NULL];
NSString *someString = #"This is a sample of a http://example.com/efg.php?EFAei687e3EsA sentence with a URL within it.";
NSString *match = [someString substringWithRange:[expression rangeOfFirstMatchInString:someString options:NSMatchingCompleted range:NSMakeRange(0, [someString length])]];
NSLog(#"%#", match); // Correctly prints 'http://example.com/efg.php?EFAei687e3EsA'
That will extract the first URL in any string (of course, this does no error checking, so if the string really doesn't contain any URL's it won't work, but take a look at the NSRegularExpression class to see how to get around it.
Use Like This:
NSError *error = nil;
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink
error:&error];
[detector enumerateMatchesInString:someString
options:0
range:NSMakeRange(0, someString.length)
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
{
if (result.resultType == NSTextCheckingTypeLink)
{
NSString *str = [NSString stringWithFormat:#"%#",result.URL];
NSLOG(%#,str);
}
}];
This will Output the all links in your someString one by one
Swift 2 :
let input = "This is a test with the URL https://www.hackingwithswift.com to be detected."
let detector = try! NSDataDetector(types: NSTextCheckingType.Link.rawValue)
let matches = detector.matchesInString(input, options: [], range: NSMakeRange(0, input.characters.count))
for match in matches {
let url = (input as NSString).substringWithRange(match.range)
print(url)
}
Source
use this:
NSURL *url;
NSArray *listItems = [someString componentsSeparatedByString:#" "];
for(int i=0;i<[listItems count];i++)
{
NSString *str=[listItems objectAtIndex:i];
if ([str rangeOfString:#"http://"].location == NSNotFound)
NSLog(#"Not url");
else
url=[NSURL URLWithString:str];
}
you need two things:
A category that adds regex to NSString (i.e. RegexKit)
Matching Regex for URLS.
regards,
Funny you mention three20, that was the first place I was going to go look for the answer. Here's the method from three20:
- (void)parseURLs:(NSString*)string {
NSInteger index = 0;
while (index < string.length) {
NSRange searchRange = NSMakeRange(index, string.length - index);
NSRange startRange = [string rangeOfString:#"http://" options:NSCaseInsensitiveSearch
range:searchRange];
if (startRange.location == NSNotFound) {
NSString* text = [string substringWithRange:searchRange];
TTStyledTextNode* node = [[[TTStyledTextNode alloc] initWithText:text] autorelease];
[self addNode:node];
break;
} else {
NSRange beforeRange = NSMakeRange(searchRange.location, startRange.location - searchRange.location);
if (beforeRange.length) {
NSString* text = [string substringWithRange:beforeRange];
TTStyledTextNode* node = [[[TTStyledTextNode alloc] initWithText:text] autorelease];
[self addNode:node];
}
NSRange searchRange = NSMakeRange(startRange.location, string.length - startRange.location);
NSRange endRange = [string rangeOfString:#" " options:NSCaseInsensitiveSearch
range:searchRange];
if (endRange.location == NSNotFound) {
NSString* URL = [string substringWithRange:searchRange];
TTStyledLinkNode* node = [[[TTStyledLinkNode alloc] initWithText:URL] autorelease];
node.URL = URL;
[self addNode:node];
break;
} else {
NSRange URLRange = NSMakeRange(startRange.location,
endRange.location - startRange.location);
NSString* URL = [string substringWithRange:URLRange];
TTStyledLinkNode* node = [[[TTStyledLinkNode alloc] initWithText:URL] autorelease];
node.URL = URL;
[self addNode:node];
index = endRange.location;
}
}
}
}
Every time it does [self addNode:node]; after the first if part, it's adding a found URL. This should get you started! Hope this helps. :)
Using Swift 2.2 - NSDataDetector
let string = "here is the link www.google.com"
let types: NSTextCheckingType = [ .Link]
let detector = try? NSDataDetector(types: types.rawValue)
detector?.enumerateMatchesInString(string, options: [], range: NSMakeRange(0, (string as NSString).length)) { (result, flags, _) in
if(result?.URL != nil){
print(result?.URL)
}
}
Swift 4.x
Xcode 12.x
let string = "This is a test with the URL https://www.hackingwithswift.com to be detected. www.example.com"
let types: NSTextCheckingResult.CheckingType = [ .link]
let detector = try? NSDataDetector(types: types.rawValue)
detector?.enumerateMatches(in: string, options: [], range: NSMakeRange(0, (string as NSString).length)) { (result, flags, _) in
if(result?.url != nil){
print(result?.url)
}
}

Resources