Objective-C: How to extract part of a String (e.g. start with '#') - objective-c

I have a string as shown below,
NSString * aString = #"This is the #substring1 and #subString2 I want";
How can I select only the text starting with '#' (and ends with a space), in this case 'subString1' and 'subString2'?
Note: Question was edited for clarity

You can do this using an NSScanner to split the string up. This code will loop through a string and fill an array with substrings.
NSString * aString = #"This is the #substring1 and #subString2 I want";
NSMutableArray *substrings = [NSMutableArray new];
NSScanner *scanner = [NSScanner scannerWithString:aString];
[scanner scanUpToString:#"#" intoString:nil]; // Scan all characters before #
while(![scanner isAtEnd]) {
NSString *substring = nil;
[scanner scanString:#"#" intoString:nil]; // Scan the # character
if([scanner scanUpToString:#" " intoString:&substring]) {
// If the space immediately followed the #, this will be skipped
[substrings addObject:substring];
}
[scanner scanUpToString:#"#" intoString:nil]; // Scan all characters before next #
}
// do something with substrings
[substrings release];
Here is how the code works:
Scan up to a #. If it isn't found, the scanner will be at the end of the string.
If the scanner is at the end of the string, we are done.
Scan the # character so that it isn't in the output.
Scan up to a space, with the characters that are scanned stored in substring. If either the # was the last character, or was immediately followed by a space, the method will return NO. Otherwise it will return YES.
If characters were scanned (the method returned YES), add substring to the substrings array.
GOTO 1

[aString substringWithRange:NSMakeRange(13, 10)]
would give you substring1
You can calculate the range using:
NSRange startRange = [aString rangeOfString:#"#"];
NSRange endRange = [original rangeOfString:#"1"];
NSRange searchRange = NSMakeRange(startRange.location , endRange.location);
[aString substringWithRange:searchRange]
would give you substring1
Read more:
Position of a character in a NSString or NSMutableString
and
http://iosdevelopertips.com/cocoa/nsrange-and-nsstring-objects.html

Pretty simple, easy to understand version avoiding NSRange stuff:
NSArray * words = [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSMutableArray * mutableWords = [NSMutableArray new];
for (NSString * word in words){
if ([word length] > 1 && [word characterAtIndex:0] == '#'){
NSString * editedWord = [word substringFromIndex:1];
[mutableWords addObject:editedWord];
}
}

Assuming that you are looking to find the first string that starts with a pound, and ends with a space, this might work. I don't have XCode in front of me, so forgive me if there's a syntax error or length off by 1 somewhere:
-(NSString *)StartsWithPound:(NSString *)str {
NSRange range = [str rangeOfString:#"#"];
if(range.length) {
NSRange rangeend = [str rangeOfString:#" " options:NSLiteralSearch range:NSMakeRange(range.location,[str length] - range.location - 1)];
if(rangeend.length) {
return [str substringWithRange:NSMakeRange(range.location,rangeend.location - range.location)];
}
else
{
return [str substringFromIndex:range.location];
}
}
else {
return #"";
}
}

Another simple solution:
NSRange hashtag = [aString rangeOfString:#"#"];
NSRange word = [[aString substringFromIndex:hashtag.location] rangeOfString:#" "];
NSString *hashtagWord = [aString substringWithRange:NSMakeRange(hashtag.location, word.location)];

This is what I'd do:
NSString *givenStringWithWhatYouNeed = #"What you want to look through";
NSArray *listOfWords = [givenStringWithWhatYouNeed componentsSeparatedByString:#" "];
for (NSString *word in listOfWords) {
if ([[word substringWithRange:NSMakeRange(0, 1)]isEqualToString:#"#"]) {
NSString *whatYouWant = [[word componentsSeparatedByString:#"#"]lastObject];
}
}
Then you can do what you need with the whatYouWant instances. If you want to know which string it is (if it's the substring 1 or 2), check the index of of word string in the listOfWords array.
I hope this helps.

A general and simple code to select all the words starting with "#" in a NSString is:
NSString * aString = #"This is the #substring1 and #subString2 ...";
NSMutableArray *selection=#[].mutableCopy;
while ([aString rangeOfString:#"#"].location != NSNotFound)
{
aString = [aString substringFromIndex:[aString rangeOfString:#"#"].location +1];
NSString *item=([aString rangeOfString:#" "].location != NSNotFound)?[aString substringToIndex:[aString rangeOfString:#" "].location]:aString;
[selection addObject:item];
}
if you still need the original string you can do a copy.
The inline conditional is used in case your selected item is the last word

Related

Add 1 to a number in an NSString that contains characters Objective-C

I am new to learning Objective-C (my first programming language!) and trying to write a little program that will add 1 to a number contained within a string. E.g. AA1BB becomes AA2BB.
.
So far I have tried to extract the number and add 1. Then extract the letters and add everything back together in a new string. I have had some success but can't manage to get back to the original arrangement of the initial string.
The code I have so far gives a result of 2BB and disregards the characters before the number which is not what I am after (the result I am trying for with this example would be AA2BB). I can't figure out why!
NSString* aString = #"AA1BB";
NSCharacterSet *numberCharset = [NSCharacterSet characterSetWithCharactersInString:#"0123456789-"]; //Creating a set of Characters object//
NSScanner *theScanner = [NSScanner scannerWithString:aString];
int someNumbers = 0;
while (![theScanner isAtEnd]) {
// Remove Letters
[theScanner scanUpToCharactersFromSet:numberCharset
intoString:NULL];
if ([theScanner scanInt:&someNumbers]) {}
}
NSCharacterSet *letterCharset = [NSCharacterSet characterSetWithCharactersInString:#"ABCDEFGHIJKLMNOPQRSTUVWXYZ"];
NSScanner *letterScanner = [NSScanner scannerWithString:aString];
NSString* someLetters;
while (![letterScanner isAtEnd]) {
// Remove numbers
[letterScanner scanUpToCharactersFromSet:letterCharset
intoString:NULL];
if ([letterScanner scanCharactersFromSet:letterCharset intoString:&someLetters]) {}
}
++someNumbers; //adds +1 to the Number//
NSString *newString = [[NSString alloc]initWithFormat:#"%i%#", someNumbers, someLetters];
NSLog (#"String is now %#", newString);
This is an alternative solution with Regular Expression.
It finds the range of the integer (\\d+ is one or more digits), extracts it, increments it and replaces the value at the given range.
NSString* aString = #"AA1BB";
NSRange range = [aString rangeOfString:#"\\d+" options:NSRegularExpressionSearch];
if (range.location != NSNotFound) {
NSInteger numericValue = [aString substringWithRange:range].integerValue;
numericValue++;
aString = [aString stringByReplacingCharactersInRange:range withString:[NSString stringWithFormat:#"%ld", numericValue]];
}
NSLog(#"%#", aString);

Check if NSString only contains one character repeated

I want to know a simple and fast way to determine if all characters in an NSString are the same.
For example:
NSString *string = "aaaaaaaaa"
=> return YES
NSString *string = "aaaaaaabb"
=> return NO
I know that I can achieve it by using a loop but my NSString is long so I prefer a shorter and simpler way.
you can use this, replace first character with null and check lenght:
-(BOOL)sameCharsInString:(NSString *)str{
if ([str length] == 0 ) return NO;
return [[str stringByReplacingOccurrencesOfString:[str substringToIndex:1] withString:#""] length] == 0 ? YES : NO;
}
Here are two possibilities that fail as quickly as possible and don't (explicitly) create copies of the original string, which should be advantageous since you said the string was large.
First, use NSScanner to repeatedly try to read the first character in the string. If the loop ends before the scanner has reached the end of the string, there are other characters present.
NSScanner * scanner = [NSScanner scannerWithString:s];
NSString * firstChar = [s substringWithRange:[s rangeOfComposedCharacterSequenceAtIndex:0]];
while( [scanner scanString:firstChar intoString:NULL] ) continue;
BOOL stringContainsOnlyOneCharacter = [scanner isAtEnd];
Regex is also a good tool for this problem, since "a character followed by any number of repetitions of that character" is in very simply expressed with a single back reference:
// Match one of any character at the start of the string,
// followed by any number of repetitions of that same character
// until the end of the string.
NSString * patt = #"^(.)\\1*$";
NSRegularExpression * regEx =
[NSRegularExpression regularExpressionWithPattern:patt
options:0
error:NULL];
NSArray * matches = [regEx matchesInString:s
options:0
range:(NSRange){0, [s length]}];
BOOL stringContainsOnlyOneCharacter = ([matches count] == 1);
Both these options correctly deal with multi-byte and composed characters; the regex version also does not require an explicit check for the empty string.
use this loop:
NSString *firstChar = [str substringWithRange:NSMakeRange(0, 1)];
for (int i = 1; i < [str length]; i++) {
NSString *ch = [str substringWithRange:NSMakeRange(i, 1)];
if(![ch isEqualToString:firstChar])
{
return NO;
}
}
return YES;

Replace characters in NSString

I am trying to replace all characters except last 4 in a String with *'s.
In objective-c there is a method in NSString class replaceStringWithCharactersInRange: withString: where I would give it range (0,[string length]-4) ) with string #"*". This is what it does: 123456789ABCD is modified to *ABCD while I am looking to make ********ABCD.
I understand that it replaced range I specified with string object. How to accomplish this ?
NSError *error;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\d" options:NSRegularExpressionCaseInsensitive error:&error];
NSString *newString = [regex stringByReplacingMatchesInString:string options:0 range:NSMakeRange(0, [string length]) withTemplate:#"*"];
This looks like a simple problem... get the first part string and return it with the last four characters appended to it.
Here is a function that returns the needed string :
-(NSString *)neededStringWithString:(NSString *)aString {
// if the string has less than or 4 characters, return nil
if([aString length] <= 4) {
return nil;
}
NSUInteger countOfCharToReplace = [aString length] - 4;
NSString *firstPart = #"*";
while(--countOfCharToReplace) {
firstPart = [firstPart stringByAppendingString:#"*"];
}
// range for the last four
NSRange lastFourRange = NSMakeRange([aString length] - 4, 4);
// return the combined string
return [firstPart stringByAppendingString:
[aString substringWithRange:lastFourRange]];
}
The most unintuitive part in Cocoa is creating the repeating stars without some kind of awkward looping. stringByPaddingToLength:withString:startingAtIndex: allows you to create a repeating string of any length you like, so once you have that, here's a simple solution:
NSInteger starUpTo = [string length] - 4;
if (starUpTo > 0) {
NSString *stars = [#"" stringByPaddingToLength:starUpTo withString:#"*" startingAtIndex:0];
return [string stringByReplacingCharactersInRange:NSMakeRange(0, starUpTo) withString:stars];
} else {
return string;
}
I'm not sure why the accepted answer was accepted, since it only works if everything but last 4 is a digit. Here's a simple way:
NSMutableString * str1 = [[NSMutableString alloc]initWithString:#"1234567890ABCD"];
NSRange r = NSMakeRange(0, [str1 length] - 4);
[str1 replaceCharactersInRange:r withString:[[NSString string] stringByPaddingToLength:r.length withString:#"*" startingAtIndex:0]];
NSLog(#"%#",str1);
You could use [theString substringToIndex:[theString length]-4] to get the first part of the string and then combine [theString length]-4 *'s with the second part. Perhaps their is an easier way to do this..
NSMutableString * str1 = [[NSMutableString alloc]initWithString:#"1234567890ABCD"];
[str1 replaceCharactersInRange:NSMakeRange(0, [str1 length] - 4) withString:#"*"];
NSLog(#"%#",str1);
it works
The regexp didn't work on iOS7, but perhaps this helps:
- (NSString *)encryptString:(NSString *)pass {
NSMutableString *secret = [NSMutableString new];
for (int i=0; i<[pass length]; i++) {
[secret appendString:#"*"];
}
return secret;
}
In your case you should stop replacing the last 4 characters. Bit crude, but gets the job done

Separating NSString into NSArray, but allowing quotes to group words

I have a search string, where people can use quotes to group phrases together, and mix this with individual keywords. For example, a string like this:
"Something amazing" rooster
I'd like to separate that into an NSArray, so that it would have Something amazing (without quotes) as one element, and rooster as the other.
Neither componentsSeparatedByString nor componentsSeparatedByCharactersInSet seem to fit the bill. Is there an easy way to do this, or should I just code it up myself?
You probably will have to code some of this up yourself, but the NSScanner should be a good basis on which to build. If you use the scanUpToCharactersInSet method to look for everything up to your next whitespace or quote character to can pick off words. Once you encounter a quite character, you could continue to scan using just the quote in the character set to end at, so that spaces within the quotes don't result in the end of a token.
I made a simple way to do this using NSScanner:
+ (NSArray *)arrayFromTagString:(NSString *)string {
NSScanner *scanner = [NSScanner scannerWithString:string];
NSString *substring;
NSMutableArray *array = [[NSMutableArray alloc] init];
while (scanner.scanLocation < string.length) {
// test if the first character is a quote
unichar character = [string characterAtIndex:scanner.scanLocation];
if (character == '"') {
// skip the first quote and scan everything up to the next quote into a substring
[scanner setScanLocation:(scanner.scanLocation + 1)];
[scanner scanUpToString:#"\"" intoString:&substring];
[scanner setScanLocation:(scanner.scanLocation + 1)]; // skip the second quote too
}
else {
// scan everything up to the next space into the substring
[scanner scanUpToString:#" " intoString:&substring];
}
// add the substring to the array
[array addObject:substring];
//if not at the end, skip the space character before continuing the loop
if (scanner.scanLocation < string.length) [scanner setScanLocation:(scanner.scanLocation + 1)];
}
return array.copy;
}
This method will convert the array back to a tag string, re-quoting the multi-word tags:
+ (NSString *)tagStringFromArray:(NSArray *)array {
NSMutableString *string = [[NSMutableString alloc] init];
NSRange range;
for (NSString *substring in array) {
if (string.length > 0) {
[string appendString:#" "];
}
range = [substring rangeOfString:#" "];
if (range.location != NSNotFound) {
[string appendFormat:#"\"%#\"", substring];
}
else [string appendString:substring];
}
return string.description;
}
I ended up going with a regular expression as I was already using RegexKitLite, and creating this NSString+SearchExtensions category.
.h:
// NSString+SearchExtensions.h
#import <Foundation/Foundation.h>
#interface NSString (SearchExtensions)
-(NSArray *)searchParts;
#end
.m:
// NSString+SearchExtensions.m
#import "NSString+SearchExtensions.h"
#import "RegexKitLite.h"
#implementation NSString (SearchExtensions)
-(NSArray *)searchParts {
__block NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:5];
[self enumerateStringsMatchedByRegex:#"\\w+|\"[\\w\\s]*\"" usingBlock: ^(NSInteger captureCount,
NSString * const capturedStrings[captureCount],
const NSRange capturedRanges[captureCount],
volatile BOOL * const stop) {
NSString *result = [capturedStrings[0] stringByReplacingOccurrencesOfRegex:#"\"" withString:#""];
NSLog(#"Match: '%#'", result);
[items addObject:result];
}];
return [items autorelease];
}
#end
This returns an NSArray of strings with the search strings, removing the double quotes that surround the phrases.
If you'll allow a slightly different approach, you could try Dave DeLong's CHCSVParser. It is intended to parse CSV strings, but if you set the space character as the delimiter, I am pretty sure you will get the intended behavior.
Alternatively, you can peek into the code and see how it handles quoted fields - it is published under the MIT license.
I would run -componentsSeparatedByString:#"\"" first, then create a BOOL isPartOfQuote, initialized to YES if the first character of the string was a ", but otherwise set to NO.
Then create a mutable array to return:
NSMutableArray* masterArray = [[NSMutableArray alloc] init];
Then, create a loop over the array returned from the separation:
for(NSString* substring in firstSplitArray) {
NSArray* secondSplit;
if (isPartOfQuote == NO) {
secondSplit = [substring componentsSeparatedByString:#" "];
}
else {
secondSplit = [NSArray arrayWithObject: substring];
}
[masterArray addObjectsFromArray: secondSplit];
isPartOfQuote = !isPartOfQuote;
}
Then return masterArray from the function.

How to get values after "\n" character?

I want to take all values after a new line character \n from my string. How can I get those values?
Try this:
NSString *substring = nil;
NSRange newlineRange = [yourString rangeOfString:#"\n"];
if(newlineRange.location != NSNotFound) {
substring = [yourString substringFromIndex:newlineRange.location];
}
Take a look at method componentsSeparatedByString here.
A quick example taken from reference:
NSString *list = #"Norman, Stanley, Fletcher";
NSArray *listItems = [list componentsSeparatedByString:#", "];
this will produce a NSArray with strings separated: { #"Norman", #"Stanley", #"Fletcher" }
Here is similar function which splits the string by delimeter and return array with two trimmed values.
NSArray* splitStrByDelimAndTrim(NSString *string, NSString *delim)
{
NSRange range = [string rangeOfString: delim];
NSString *first;
NSString *second;
if(range.location == NSNotFound)
{
first = #"";
second = string;
}
else
{
first = [string substringToIndex: range.location];
first = [first stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
second = [string substringFromIndex: range.location + 1];
second = [second stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
}
return [NSArray arrayWithObjects: first, second, nil];
}