Remove parentheses without regex - ios

I need to turn something like this
NSString *stringWithParentheses = #"This string uses (something special)";
Into this, programmatically.
NSString *normalString = #"This string uses";
The issue is I don't want to use all these weird libraries, regex, etc.

If you change your mind about the regex, here's a short, clean solution:
NSString *foo = #"First part (remove) (me (and ((me)))))) (and me) too))";
NSRegularExpression *expr = [NSRegularExpression regularExpressionWithPattern:#"\\(.*\\)" options:0 error:NULL];
NSString *bar = [expr stringByReplacingMatchesInString:foo options:0 range:NSMakeRange(0, foo.length) withTemplate:#""];
Everything between ( and ) gets removed, including any nested parentheses and unmatched parentheses within parentheses.

Just find the first open parentheses, note its index, find the closing one, note its index, and remove the characters between the indexes (including the indexes themselves).
To find the character use:
[string rangeOfString:#"("];
To remove a range:
[string stringByReplacingCharactersInRange:... withString:#""];
Here is a solution:
NSString* str = #"This string uses (something special)";
NSRange rgMin = [str rangeOfString:#"("];
NSRange rgMax = [str rangeOfString:#")"];
NSRange replaceRange = NSMakeRange(rgMin.location, rgMax.location-rgMin.location+1);
NSString* newString = str;
if (rgMin.location < rgMax.location)
{
newString = [str stringByReplacingCharactersInRange:replaceRange withString:#""];
}
It won't work on nested parentheses. Or multiple parentheses. But it works on your example. This is to be refined to your exact situation.

A way would be to find the position of the first occurrence of the '(' character and the last occurrence of the ')' character, and to build a substring by eliminating all the characters between these ranges. I've made an example:
NSString* str= #"This string uses (something special)";
NSRange r1=[str rangeOfString: #"("];
NSRange r2= [str rangeOfString: #")" options: NSBackwardsSearch];
NSLog(#"%#",[str stringByReplacingCharactersInRange: NSMakeRange(r1.location, r2.location+r2.length-r1.location) withString: #""]);

Related

Using a regex to remove a specific #tag in Objective-C

I am trying to parse a string in Objective-C to delete an exact match of a specific #tagged word. I can create a regular expression and delete a specific word without issue, but when I try to remove a string with a leading "#" it's not working.
Here's my code:
NSString *originalString = #"This is just a #test that isn't working";
NSString *hashTag = #"#test";
NSString *placeholder = #"\\b%#\\b";
NSString *pattern = [NSString stringWithFormat:placeholder, hashTag];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
NSString *modifiedString = [regex stringByReplacingMatchesInString:originalString
options:0
range:NSMakeRange(0, [originalString length])
withTemplate:#""];
The issue is that even if the original string includes the string #test, it's not removed. If I swap "#test" with "test", everything works fine, but that's not what I'm trying to do. What am I missing?
Because there isn't a word character exists between a space and #. Both are non-word characters. So i suggest you to remove the starting \\b
NSString *placeholder = #"%#\\b";
OR
Use a negative lookbehind.
NSString *placeholder = #"(?<!\\S)%#\\b";
(?<!\\S) Negative lookbehind which asserts that there isn't a non-space character exists before the match.
To do an exact string match, i suggest you to use this #"(?<!\\S)%#(?!\\S)" regex. (?!\\S) Negative lookahead which asserts that the match won't be followed by a non-space character.
DEMO
is it even necessary to use a regex, would this function not suffice?
[yourstring stringByReplacingOccurrencesOfString:#"#test" withString:#""];

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..

Removing \ from NSString (from escape sequences only)

I have tried (searching for) various possible solutions here on SO, in vain. Most of them simply replace all occurrences of backslashes, and don't respect backslashes that should otherwise be untouched.
For instance, if I have a Hi, it\'s me. How\'re you doing?, it should be Hi, it's me. How're you doing?. However, if someone tries to get creative with ASCII art, like
\\// \\// \\//
//\\ //\\ //\\
(WOW even SO won't let me add text as is, the above text needed extra backslashes to be displayed correctly.)
I cannot use [myString stringByReplacingOccurrencesOfString:#"\\" withString:#""]; since it will replace ALL backslashes. I do not want that.
I would like the string to be displayed as is.
NOTE: The strings in question here are values in NSDictionarys received as JSON from a web service. The use is in a service like a chat client, so it is important that text is handled correctly.
ULTRA IMPORTANT NOTE: I'm open to all ideas like library functions, regular expressions, human sacrifices, as long it gets the job done.
try this ...i cannot understand your question but it may help full for you,i think so
- (void)remove:(NSString*)str
{
NSString* const pattern = #"(\"[^\"]*\"|[^, ]+)";
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern
options:0
error:nil];
NSRange searchRange = NSMakeRange(0, [str length]);
NSArray *matches = [regex matchesInString:str
options:0
range:searchRange];
for (NSTextCheckingResult *match in matches) {
NSRange matchRange = [match range];
NSLog(#"%#", [str substringWithRange:matchRange]);
}
NSLog(#"%#",str);
}
call this method..
NSString* str = #"Hi, it\'s me. How\'re you doing?";
[self remove:str];
then the output is
Hi, it's me. How're you doing?

Regex to find strings in iOS source code

I need a regex to find string literals in iOS source code.
I want to collect them and then use in other place.
I need the regex to be able to parse next cases. Comments will be stripped beforehand:
char c = '"'; /* Not a string*/ NSString * *str1 = #"string"; // Here's a string
NSString *str2 = #"\"A\" class";
NSString *str3 = #"Long long"
#"long long string";
NSString *str4 = #"Another Long long"
"long long string";
NSString *str5 = #"Long untrimmed \
string";
I'm not very good with regex. I've tried this one:
#"(?<!')\"((\\\\\")*[^\"\\r\\n]*(\\\\\")*)*\"'{0}"
Unfortunately it hangs on such strings.
Hello here is your solution "(\\.|[^"])*\" use this regex to parse string literals in any text. you can check this regex on sample text on http://regexpal.com/
hope is helps !
#(\".*\";|\".*\n.*\";|\".*\")
Check http://rubular.com/r/6dxtBzTE7P . I could not do 3rd one fully correct.
I end up with following regex (^|[^'])(#?\"([^\"\\\\]|(\\\\.)|(\\\\\n)|(\"\\s*#?\"))*\"), example:
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:#"(^|[^'])(#?\"([^\"\\\\]|(\\\\.)|(\\\\\n)|(\"\\s*#?\"))*\")" options:NSRegularExpressionAnchorsMatchLines error:&error];
[regex enumerateMatchesInString:source options:0 range:NSMakeRange(0, [source length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSLog(#"%#\n\n", [source substringWithRange:[result rangeAtIndex:2]]);
}];
It works fine for any cases that I have in my mind plus it also works for c-string.
How it works:
(^|[^']) Any character except ' (because '"') or beginning of line
#?\"([^\"\\\\]|(\\\\.)|(\\\\\n)|(\"(\\s*\\n*\\s*)*#?\"))*\" starts with #" or " and ends with ".
Between can be any sequence of character except " or \ ([^\"\\\\])
it can be \ char then skip next element too, it escaped anyway (\\\\.)
it can be escaped next line symbol (\\\\\n)
it can be multiline string, so we skip all whitespaces and new line symbols. Next line in string can be both from " and #" (#?\"\\s*#?\")
Check online editor: http://regex101.com/r/gG0sF2

Search for any characters in an NSString

I have the following code to search an NSString:
for (NSDictionary *obj in data) {
NSString *objQuestion = [obj objectForKey:#"Question"];
NSRange dataRange = [objQuestion rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (dataRange.location != NSNotFound) {
[filteredData addObject:obj];
}
}
This works fine, but there is a problem. If objQuestion is: "Green Yellow Red" and I search for "Yellow Green Red", the object will not show up as my search is not in the correct order.
How would I change my code so that no matter what order I search the words in, the object will show?
You should be breaking your search text into words and search each word.
NSArray *wordArray= [searchText componentsSeparatedByString: #" "];
for (NSDictionary *obj in data) {
NSString *objQuestion = [obj objectForKey:#"Question"];
BOOL present = NO;
for (NSString *s in wordArray) {
if (s) {
NSRange dataRange = [objQuestion rangeOfString:s options:NSCaseInsensitiveSearch];
if (dataRange.location != NSNotFound) {
present = YES;
}
}
}
if (present) {
[filteredData addObject:obj];
}
}
So you want to basically do a keyword search? I would recommend doing a regular expression search. where the words can be in any order.
Something like this.
(your|test|data)? *(your|test|data)? *(your|test|data)?
Which you can use in a NSRegularExpressoin
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(your|test|data)? *(your|test|data)? *(your|test|data)?" options:NSRegularExpressionCaseInsensitive error:&error];
int numMatches = [regex numberOfMatchesInString:searchString options:0 range:NSMakeRange(0, [searchString length])];];
This will match any ordering in an efficient manner.
Not sure if regex is okay for Obj C, because I do not have a mac in front of me right now, but it should be okay.
You might want to consider that the search input string is not always as clean as you expect, and could contain punctuation, brackets, etc.
You'd also want to be lax with accents.
I like to use regular expressions for this sort of problem, and since you are looking for a solution that allows arbitrary ordering of the search terms, we'd need to re-work the search string. We can use regular expressions for that, too - so the pattern is constructed by a regex substitution, just out of principle. You may want to document it thoroughly.
So here is a code snippet that will do these things:
// Use the Posix locale as the lowest common denominator of locales to
// remove accents.
NSLocale *enLoc = [[NSLocale alloc] initWithLocaleIdentifier: #"en_US_POSIX"];
// Mixed bag of genres, but for testing purposes we get all the accents we need
NSString *orgString = #"Beyoncé Motörhead Händel";
// Clean string by removing accents and upper case letters in Posix encoding
NSString *string = [orgString stringByFoldingWithOptions: NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch
locale: enLoc ];
// What the user has typed in, with misplaced umlaut and all
NSString *orgSearchString = #"handel, mötorhead, beyonce";
// Clean the search string, too
NSString *searchString = [orgSearchString stringByFoldingWithOptions: NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch
locale: enLoc ];
// Turn the search string into a regex pattern.
// Create a pattern that looks like: "(?=.*handel)(?=.*motorhead)(?=.*beyonce)"
// This pattern uses positive lookahead to create an AND logic that will
// accept arbitrary ordering of the words in the pattern.
// The \b expression matches a word boundary, so gets rid of punctuation, etc.
// We use a regex to create the regex pattern.
NSString *regexifyPattern = #"(?w)(\\W*)(\\b.+?\\b)(\\W*)";
NSString *pattern = [searchString stringByReplacingOccurrencesOfString: regexifyPattern
withString: #"(?=.*$2)"
options: NSRegularExpressionSearch
range: NSMakeRange(0, searchString.length) ];
NSError *error;
NSRegularExpression *anyOrderRegEx = [NSRegularExpression regularExpressionWithPattern: pattern
options: 0
error: &error];
if ( !anyOrderRegEx ) {
// Regex patterns are tricky, programmatically constructed ones even more.
// So we check if it went well and do something intelligent if it didn't
// ...
}
// Match the constructed pattern with the string
NSUInteger numberOfMatches = [anyOrderRegEx numberOfMatchesInString: string
options: 0
range: NSMakeRange(0, string.length)];
BOOL found = (numberOfMatches > 0);
The use of the Posix locale identifier is discussed in this tech note from Apple.
In theory there is an edge case here if the user enters characters with a special meaning for regexes, but since the first regex removes non-word characters it should be solved that way. A bit of an un-planned positive side effect, so could be worth verifying.
Should you not be interested in a regex-based solution, the code folding may still be useful for "normal" NSString-based searching.

Resources