Replacing instances of a character with two different characters in Objective-C - iphone

I have a huge amount of NSStrings in a database that get passed to a view controller in an iOS app. They are formatted as "This is a message with $specially formatted$ content".
However, I need to change the '$' at the start of the special formatting with a '[' and the '$' at the end with ']'. I have a feeling I can use an NSScanner but so far all of my attempts have produced wackily concatenated strings!
Is there a simple way to recognise a substring encapsulated by '$' and swap them out with start/end characters? Please note that a lot of the NSStrings have multiple '$' substrings.
Thanks!

You can use regular expressions:
NSMutableString *str = [#"Hello $World$, foo $bar$." mutableCopy];
NSRegularExpression *regex;
regex = [NSRegularExpression regularExpressionWithPattern:#"\\$([^$]*)\\$"
options:0
error:NULL];
[regex replaceMatchesInString:str
options:0
range:NSMakeRange(0, [str length])
withTemplate:#"[$1]"];
NSLog(#"%#", str);
// Output:
// Hello [World], foo [bar].
The pattern #"\\$([^$]*)\\$" searches for
$<zero_or_more_characters_which_are_not_a_dollarsign>$
and all occurrences are then replaced by [...]. The pattern contains so many backslashes because the $ must be escaped in the regular expression pattern.
There is also stringByReplacingMatchesInString if you want to create a new string instead of modifying the original string.

I think replaceOccurrencesOfString: won't work cause you have start$ and end$.
But if you seperate the Strings with [string componentsSeperatedByString:#"$"] you get an Array of substrings, so every second string is your "$specially formatted$"-string

This should work!
NSString *str = #"This is a message with $specially formatted$ content";
NSString *original = #"$";
NSString *replacement1 = #"[";
NSString *replacement2 = #"]";
BOOL start = YES;
NSRange rOriginal = [str rangeOfString: original];
while (NSNotFound != rOriginal.location) {
str = [str stringByReplacingCharactersInRange: rOriginal withString:(start?replacement1:replacement2)];
start = !start;
rOriginal = [str rangeOfString: original];
}
NSLog(#"%#", str);
Enjoy Programming!

// string = #"This is a $special markup$ sentence."
NSArray *components = [string componentsSeparatedByString:#"$"];
// sanity checks
if (components.count < 2) return; // maybe no $ characters found
if (components.count % 2) return; // not an even number of $s
NSMutableString *out = [NSMutableString string];
for (int i=0; i< components.count; i++) {
[out appendString:components[i]];
[out appendString: (i % 2) ? #"]" : #"[" ];
}
// out = #"This is a [special markup] sentence."

Try this one
NSMutableString *string=[[NSMutableString alloc]initWithString:#"This is a message with $specially formatted$ content. This is a message with $specially formatted$ content"];
NSMutableString *string=[[NSMutableString alloc]initWithString:#"This is a message with $specially formatted$ content. This is a message with $specially formatted$ content"];
BOOL open=YES;
for (NSUInteger i=0; i<[string length];i++) {
if ([string characterAtIndex:i]=='$') {
if (open) {
[string replaceCharactersInRange:NSMakeRange(i, 1) withString:#"["];
open=!open;
}
else{
[string replaceCharactersInRange:NSMakeRange(i, 1) withString:#"]"];
open=!open;
}
}
}
NSLog(#"-->%#",string);
Output:
-->This is a message with [specially formatted] content. This is a message with [specially formatted] content

Related

Objective-C: Find consonants in string

I have a string that contains words with consonants and vowels. How can I extract only consonants from the string?
NSString *str = #"consonants.";
Result must be:
cnsnnts
You could make a character set with all the vowels (#"aeiouy")
+ (id)characterSetWithCharactersInString:(NSString *)aString
then use the
- (NSString *)stringByTrimmingCharactersInSet:(NSCharacterSet *)set
method.
EDIT: This will only remove vowels at the beginning and end of the string as pointed out in the other post, what you could do instead is use
- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
then stick the components back together. You may also need to include capitalized versions of the vowels in the set, and if you want to also deal with accents (à á è è ê ì etc...) you'll probably have to include that also.
Unfortunately stringByTrimmingCharactersInSet wont work as it only trim leading and ending characters, but you could try using a regular expression and substitution like this:
[[NSRegularExpression
regularExpressionWithPattern:#"[^bcdefghjklmnpqrstvwx]"
options:NSRegularExpressionCaseInsensitive
error:NULL]
stringByReplacingMatchesInString:str
options:0
range:NSMakeRange(0, [str length])
withTemplate:#""]
You probably want to tune the regex and options for your needs.
Possible, for sure not-optimal, solution. I'm printing intermediate results for your learning. Take care of memory allocation (I didn't care). Hopefully someone will send you a better solution, but you can copy and paste this for the moment.
NSString *test = #"Try to get all consonants";
NSMutableString *found = [[NSMutableString alloc] init];
NSInteger loc = 0;
NSCharacterSet *consonants = [NSCharacterSet characterSetWithCharactersInString:#"bcdfghjklmnpqrstvwxyz"];
while(loc!=NSNotFound && loc<[test length]) {
NSRange r = [[test lowercaseString] rangeOfCharacterFromSet:consonants options:0 range:NSMakeRange(loc, [test length]-loc)];
if(r.location!=NSNotFound) {
NSString *temp = [test substringWithRange:r];
NSLog(#"Range: %# Temp: %#",NSStringFromRange(r), temp);
[found appendString:temp];
loc=r.location+r.length;
} else {
loc=NSNotFound;
}
}
NSLog(#"Found: %#",found);
Here is a NSString category that does the job:
- (NSString *)consonants
{
NSString *result = [NSString stringWithString:self];
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:#"aeiou"];
while(1)
{
NSRange range = [result rangeOfCharacterFromSet:characterSet options:NSCaseInsensitiveSearch];
if(range.location == NSNotFound)
break;
result = [result stringByReplacingCharactersInRange:range withString:#""];
}
return result;
}

Match NSArray of characters Objective-C

I have to match the number of occurrences of n special characters in a string.
I thought to create an array with all these chars (they are 20+) and create a function to match each of them.
I just have the total amount of special characters in the string, so I can make some math count on them.
So in the example:
NSString *myString = #"My string #full# of speci#l ch#rs & symbols";
NSArray *myArray = [NSArray arrayWithObjects:#"#",#"#",#"&",nil];
The function should return 5.
Would it be easier match the characters that are not in the array, take the string length and output the difference between the original string and the one without special chars?
Is this the best solution?
NSString *myString = #"My string #full# of speci#l ch#rs & symbols";
//even in first continuous special letters it contains -it will return 8
//NSString *myString = #"#&#My string #full# of speci#l ch#rs & symbols";
NSArray *arr=[myString componentsSeparatedByCharactersInSet:[NSMutableCharacterSet characterSetWithCharactersInString:#"##&"]];
NSLog(#"resulted string : %# \n\n",arr);
NSLog(#"count of special characters : %i \n\n",[arr count]-1);
OUTPUT:
resulted string : (
"My string ",
full,
" of speci",
"l ch",
"rs ",
" symbols"
)
count of special characters : 5
You should utilize an NSRegularExpression, its perfect for your scenario. You can create one like this:
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(#|&)" options:NSRegularExpressionCaseInsensitive error:&error];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:string options:0 range:NSMakeRange(0, [string length])];
Caveat: I ripped the code from the Apple Developer site. And I'm no regex guru so you will have to tweak the pattern. But you get the gist.
You should look also at NSRegularExpression:
- (NSUInteger)numberOfCharacters:(NSArray *)arr inString:(NSString *)str {
NSMutableString *mutStr = #"(";
for(i = 0; i < [arr count]; i++) {
[mutStr appendString:[arr objectAtIndex:i]];
if(i+1 < [arr count]) [mutStr appendString:#"|"];
}
[mutStr appendString:#")"];
NSRegularExpression *regEx = [NSRegularExpression regularExpressionWithPattern:mutStr options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger *occur = [regExnumberOfMatchesInString:str options:0 range:NSMakeRange(0, [string length])];
[mutStr release];
return occur;
}
Usage example:
NSString *myString = #"My string #full# of speci#l ch#rs & symbols";
NSArray *myArray = [NSArray arrayWithObjects:#"#",#"#",#"&",nil];
NSLog(#"%d",[self numberOfCharacters:myArray inString:myString]); // will print 5

NSString to NSArray

I want to split an NSString into an NSArray. For example, given:
NSString *myString=#"ABCDEF";
I want an NSArray like:
NSArray *myArray={A,B,C,D,E,F};
How to do this with Objective-C and Cocoa?
NSMutableArray *letterArray = [NSMutableArray array];
NSString *letters = #"ABCDEF𝍱क्";
[letters enumerateSubstringsInRange:NSMakeRange(0, [letters length])
options:(NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[letterArray addObject:substring];
}];
for (NSString *i in letterArray){
NSLog(#"%#",i);
}
results in
A
B
C
D
E
F
𝍱
क्
enumerateSubstringsInRange:options:usingBlock: available for iOS 4+ can enumerate a string with different styles. One is called NSStringEnumerationByComposedCharacterSequences, what will enumerate letter by letter but is sensitive to surrogate pairs, base characters plus combining marks, Hangul jamo, and Indic consonant clusters, all referred as Composed Character
Note, that the accepted answer "swallows" 𝍱and breaks क् into क and ्.
Conversion
NSString * string = #"A B C D E F";
NSArray * array = [string componentsSeparatedByString:#" "];
//Notice that in this case I separated the objects by a space because that's the way they are separated in the string
Logging
NSLog(#"%#", array);
This is what the console returned
NSMutableArray *chars = [[NSMutableArray alloc] initWithCapacity:[theString length]];
for (int i=0; i < [theString length]; i++) {
NSString *ichar = [NSString stringWithFormat:#"%C", [theString characterAtIndex:i]];
[chars addObject:ichar];
}
This link contains examples to split a string into a array based on sub strings and also based on strings in a character set. I hope that post may help you.
here is the code snip
NSMutableArray *characters = [[NSMutableArray alloc] initWithCapacity:[myString length]];
for (int i=0; i < [myString length]; i++) {
NSString *ichar = [NSString stringWithFormat:#"%c", [myString characterAtIndex:i]];
[characters addObject:ichar];
}
Without loop you can use this:
NSString *myString = #"ABCDEF";
NSMutableString *tempStr =[[NSMutableString alloc] initWithString:myString];
if([myString length] != 0)
{
NSError *error = NULL;
// declare regular expression object
NSRegularExpression *regex =[NSRegularExpression regularExpressionWithPattern:#"(.)" options:NSMatchingReportCompletion error:&error];
// replace each match with matches character + <space> e.g. 'A' with 'A '
[regex replaceMatchesInString:tempStr options:NSMatchingReportCompletion range:NSMakeRange(0,[myString length]) withTemplate:#"$0 "];
// trim last <space> character
[tempStr replaceCharactersInRange:NSMakeRange([tempStr length] - 1, 1) withString:#""];
// split into array
NSArray * arr = [tempStr componentsSeparatedByString:#" "];
// print
NSLog(#"%#",arr);
}
This solution append space in front of each character with the help of regular expression and uses componentsSeparatedByString with <space> to return an array
Swift 4.2:
String to Array
let list = "Karin, Carrie, David"
let listItems = list.components(separatedBy: ", ")
Output : ["Karin", "Carrie", "David"]
Array to String
let list = ["Karin", "Carrie", "David"]
let listStr = list.joined(separator: ", ")
Output : "Karin, Carrie, David"
In Swift, this becomes very simple.
Swift 3:
myString.characters.map { String($0) }
Swift 4:
myString.map { String($0) }

NSXMLParser Encoded by Windows-1252

So NSXMLParser has problems parsing strings using the Windows-1252 encoder. Now I did find a solution on this page to convert it to NSUTF8StringEncoding. But now it bumps into characters it can't parse.
So I figured out that it will work if I would escape special characters and then transfer it back after parsing it. For example:
string = [string stringByReplacingOccurrencesOfString:#":" withString:#"__58__"];
Since it is allowed to use the _ character without getting a parser error and in NSXMLParser I can transfer the value back to it's proper character.
So is there a way I can loop through all ASCII character so I can replace all special characters (Except <, > and _ of course)?
Totally untested. I don't even know if it compiles, but it may get you on the right track. string needs to be an NSMutableString.
NSRange r = NSMakeRange(0, [string length]);
while (r.location < [string length])
{
r = [string rangeOfCharactersFromSet:[NSCharacterSet symbolCharacterSet] options:0 range:r];
if (r.location != NSNotFound)
{
NSMutableString *replacement = [[NSMutableString alloc] initWithCapacity:6];
for (NSUInteger i = r.location; i <= NSMaxRange(r); i++)
{
unichar c = [string characterAtIndex:i];
if (c != "_")
{
[replacement appendFormat:#"__%d__", (unsigned)c];
}
}
[string replaceCharactersInRange:r withString:replacement];
[replacement release]; replacement = nil;
r.location = r.location + [string length] + 1;
r.length = [string length] - r.location;
}
}
Assuming you have a NSMutableString str, you can do the following:
NSMutableString *str = ...;
[str replaceOccurrencesOfString:":" withString:#"__58__"
options:NSLiteralSearch
range:NSMakeRange(0, [str length])];
[str replaceOccurrencesOfString:"&" withString:#"__38__"
options:NSLiteralSearch
range:NSMakeRange(0, [str length])];
You see the pattern!
You can also just use XML entities for these values, e.g. replace & with &.
Thanks for the help everybody, this code actually solved my problem:
for (unichar asciiChar = 1; asciiChar <= 255; asciiChar++) {
NSString *stringWithAsciiChar = [NSString stringWithCharacters:&asciiChar length:1];
if (stringWithAsciiChar == nil) continue;
string = [string stringByReplacingOccurrencesOfString:stringWithAsciiChar withString:[NSString stringWithFormat:#"__%d__", asciiChar]];
}

Replace multiple characters in a string in Objective-C?

In PHP I can do this:
$new = str_replace(array('/', ':', '.'), '', $new);
...to replace all instances of the characters / : . with a blank string (to remove them)
Can I do this easily in Objective-C? Or do I have to roll my own?
Currently I am doing multiple calls to stringByReplacingOccurrencesOfString:
strNew = [strNew stringByReplacingOccurrencesOfString:#"/" withString:#""];
strNew = [strNew stringByReplacingOccurrencesOfString:#":" withString:#""];
strNew = [strNew stringByReplacingOccurrencesOfString:#"." withString:#""];
Thanks,
matt
A somewhat inefficient way of doing this:
NSString *s = #"foo/bar:baz.foo";
NSCharacterSet *doNotWant = [NSCharacterSet characterSetWithCharactersInString:#"/:."];
s = [[s componentsSeparatedByCharactersInSet: doNotWant] componentsJoinedByString: #""];
NSLog(#"%#", s); // => foobarbazfoo
Look at NSScanner and -[NSString rangeOfCharacterFromSet: ...] if you want to do this a bit more efficiently.
There are situations where your method is good enough I think matt.. BTW, I think it's better to use
[strNew setString: [strNew stringByReplacingOccurrencesOfString:#":" withString:#""]];
rather than
strNew = [strNew stringByReplacingOccurrencesOfString:#"/" withString:#""];
as I think you're overwriting an NSMutableString pointer with an NSString which might cause a memory leak.
Had to do this recently and wanted to share an efficient method:
(assuming someText is a NSString or text attribute)
NSString* someText = #"1232342jfahadfskdasfkjvas12!";
(this example will strip numbers from a string)
[someText stringByReplacingOccurrencesOfString:#"[^0-9]" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, [someText length])];
Keep in mind that you will need to escape regex literal characters using Obj-c escape character:
(obj-c uses a double backslash to escape special regex literals)
...stringByReplacingOccurrencesOfString:#"[\\\!\\.:\\/]"
What makes this interesting is that NSRegularExpressionSearch option is little used but can lead to some very powerful controls:
You can find a nice iOS regex tutorial here and more on regular expressions at regex101.com
Essentially the same thing as Nicholas posted above, but if you want to remove everything EXCEPT a set of characters (say you want to remove everything that isn't in the set "ABCabc123") then you can do the following:
NSString *s = #"A567B$%C^.123456abcdefg";
NSCharacterSet *doNotWant = [[NSCharacterSet characterSetWithCharactersInString:#"ABCabc123"] invertedSet];
s = [[s componentsSeparatedByCharactersInSet: doNotWant] componentsJoinedByString: #""];
NSLog(#"%#", s); // => ABC123abc
Useful in stripping out symbols and such, if you only want alphanumeric.
+ (NSString*) decodeHtmlUnicodeCharactersToString:(NSString*)str
{
NSMutableString* string = [[NSMutableString alloc] initWithString:str]; // #&39; replace with '
NSString* unicodeStr = nil;
NSString* replaceStr = nil;
int counter = -1;
for(int i = 0; i < [string length]; ++i)
{
unichar char1 = [string characterAtIndex:i];
for (int k = i + 1; k < [string length] - 1; ++k)
{
unichar char2 = [string characterAtIndex:k];
if (char1 == '&' && char2 == '#' )
{
++counter;
unicodeStr = [string substringWithRange:NSMakeRange(i + 2 , 2)]; // read integer value i.e, 39
replaceStr = [string substringWithRange:NSMakeRange (i, 5)]; // #&39;
[string replaceCharactersInRange: [string rangeOfString:replaceStr] withString:[NSString stringWithFormat:#"%c",[unicodeStr intValue]]];
break;
}
}
}
[string autorelease];
if (counter > 1)
return [self decodeHtmlUnicodeCharactersToString:string];
else
return string;
}
Here is an example in Swift 3 using the regularExpression option of replacingOccurances.
Use replacingOccurrences along with a the String.CompareOptions.regularExpression option.
Example (Swift 3):
var x = "<Hello, [play^ground+]>"
let y = x.replacingOccurrences(of: "[\\[\\]^+<>]", with: "7", options: .regularExpression, range: nil)
print(y)
Output:
7Hello, 7play7ground777
If the characters you wish to remove were to be adjacent to each other you could use the
stringByReplacingCharactersInRange:(NSRange) withString:(NSString *)
Other than that, I think just using the same function several times isn't that bad. It is much more readable than creating a big method to do the same in a more generic way.
Create an extension on String...
extension String {
func replacingOccurrences(of strings:[String], with replacement:String) -> String {
var newString = self
for string in strings {
newString = newString.replacingOccurrences(of: string, with: replacement)
}
return newString
}
}
Call it like this:
aString = aString.replacingOccurrences(of:['/', ':', '.'], with:"")