Strange behaviour of NSScanner on simple whitespace removal - iphone

I'm trying to replace all multiple whitespace in some text with a single space. This should be a very simple task, however for some reason it's returning a different result than expected. I've read the docs on the NSScanner and it seems like it's not working properly!
NSScanner *scanner = [[NSScanner alloc] initWithString:#"This is a test of NSScanner !"];
NSMutableString *result = [[NSMutableString alloc] init];
NSString *temp;
NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
while (![scanner isAtEnd]) {
// Scan upto and stop before any whitespace
[scanner scanUpToCharactersFromSet:whitespace intoString:&temp];
// Add all non whotespace characters to string
[result appendString:temp];
// Scan past all whitespace and replace with a single space
if ([scanner scanCharactersFromSet:whitespace intoString:NULL]) {
[result appendString:#" "];
}
}
But for some reason the result is #"ThisisatestofNSScanner!" instead of #"This is a test of NSScanner !".
If you read through the comments and what each line should achieve it seems simple enough!? scanUpToCharactersFromSet should stop the scanner just as it encounters whitespace. scanCharactersFromSet should then progress the scanner past the whitespace up to the non-whitespace characters. And then the loop continues to the end.
What am I missing or not understanding?

Ah, I figured it out! By default the NSScanner skips whitespace!
Turns out you just have to set charactersToBeSkipped to nil:
[scanner setCharactersToBeSkipped:nil];

Related

NSRegularExpression not getting exact text

I have a string like:
<book>MyBook</book><value>myValue</value>
Now I want to get the text "myValue" out of this string. I want to use NSRegularExpression to do this. I tried this:
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(<book>MyBook</book>\\s*<value>).*?(</value>)"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSArray *textArray = [regex matchesInString:myData options:0 range:NSMakeRange(0, [myData length])];
NSTextCheckingResult * result = [rege firstMatchInString:myData
options:0
range:NSMakeRange(0, [myData length])];
The result is:
<book>MyBook</book><value>myValue</value>
So I get the whole string, but I only want "myValue". How can I do this? What am I missing here?
Thanks in advance!
That happens because you wrote a regex that matches the entire string. I'd reckon that writing a regex that will only match the myValue part of the string is way too complicated to be bothered with (due to the fact that you've got MyBook string that will probably match anything myValue does).
I'd recommend not using regex for this, as they are not intended for the use you've described here. If you don't want to use any XML deserialization, you could use a NSScanner or any of the NSString class methods which will yield a simpler, and easier code to maintain.
For example, using an NSScanner and a few other methods:
NSString *stringToBeScanned = #"<book>MyBook</book><value>myValue</value>";
NSString *myValue;
NSScanner *scanner = [NSScanner scannerWithString:stringToBeScanned];
[scanner scanUpToString:#"<value>" intoString:nil];
// After the above, we've got "<value>myValue</value>" left to scan
[scanner scanUpToString:#"</value>" intoString:&myValue];
// We ended up with a "<value>myValue" type of a string
// This will trim the remaining of the string we don't need
myValue = [myValue stringByReplacingOccurrencesOfString:#"<value>" withString:#""];
The above could probably be written better and I might have made a mistake or two writing it out my head, but the principle should work.

Trying to get text from website and display it in ios?

Ok I have an exsiting app that I am currently working on an update for. What I am trying to do is when the client updates their website, the app will pull the text from the certain page and display the text in an UITextView? I am trying this approach which works fine except it includes the text of the NavBar? So how do I get the text only and no NavBar?
textView.text = [webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.innerText"];
Well you have two choices from the point i see it at. If you know how long the text in the nav bar is and it is the same character length just use:
NSString *webString = [webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.innerText"];
int length = amount of characters to remove from beginning of string;
webString = [webString substringFromIndex:length];
If you dont know the amount you want to remove you can use the NSScanner which is a bit more complicated but is more flexible.
NSString *webString = [webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.innerText"];
NSScanner *stringScanner = [NSScanner scannerWithString:webString];
NSString *content = [[NSString alloc] init];
while ([stringScanner isAtEnd] == NO) {
[stringScanner scanUpToString:#"Start of the text you want" intoString:null];
[stringScanner scanUpToString:#"End of the text you want" intoString:&content];
}
Hope This Helps :D
Here is the code I am trying to use
NSString *webString = [webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.innerText"];
NSScanner *stringScanner = [NSScanner scannerWithString:webString];
NSString *content = [[NSString alloc] init];
while ([stringScanner isAtEnd] == NO) {
[stringScanner scanUpToString:#"Andalee" intoString:NULL];
[stringScanner scanUpToString:#"Eastern Sun Dance Company Rehearsal Mondays 7:00pm # Cal Arts Academy" intoString:&content];
textView.text = webString; }
Maybe I am approaching it wrong.
Here is the webpage that I am trying to pull from http://andalee.com/andalee/CLASSES.html

Unable to print NSString assigned with NSScanner

I am trying to scan for a specific string in an html file, assign it to an NSString then do things with the NSString. If it matters, I am doing this in Cocos2d.
My code looks like this:
NSScanner *scanner = [NSScanner scannerWithString: htmlCodeString];
NSString* string;
[scanner scanUpToString:#"HTML CODE" intoString:NULL];
[scanner scanString:#"HTML CODE" intoString:NULL];
[scanner scanUpToString:#"STRING I NEED" intoString: &string];
NSLog(#"%#", string);
When I run the code, NSLog prints the name of the layer I am executing the code in.
I am confused because I followed this example by Apple to a T:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/Scanners.html#//apple_ref/doc/uid/20000147-BCIEFGHC
(scroll to the bottom)
Any advice would be greatly appreciated.
Check what scanUpToString:intoString returns. If it returns NO, the string wasn't found and the "into" string isn't modified. As you don't initialize your string, it contains some random garbage. You should initialize it to nil and then look into why your string isn't found.

NSString's enumerateSubstrings doesn't include symbols

When using NSString's enumerateSubstringsInRange:options:usingBlock: with the options set as NSStringEnumerationByWords it doesn't include symbols such as /* or // which should be treated similarly to words as they are seperated by spaces.
I also tried using NSStringEnumerationByComposedCharacterSequences but it seems to do exactly the same thing even without this option, it simply goes through every single letter.
Is their no way to enumerate through every substring separated by a space? It sounds so simple by no way to do is provided to do this using enumerateSubstringsInRange:options:usingBlock:.
EDIT
I was also using the option NSEnumerationReverse to got through the substrings backwards.
You could use NSScanner for something like this. It's sort of the long way around, but if the enumerate... messages aren't doing it for you, it might be worth looking at.
For example, you could do something like
NSString *output = nil;
NSCharacterSet *whitespaceCharSet = [NSCharacterSet whitespaceCharacterSet];
NSScanner *scanner = [[NSScanner alloc] initWithString:someString];
// should skip leading whitespace and read everything up to the next whitespace
[scanner scanUpToCharactersFromSet:whitespaceCharSet intoSring:&output];
[scanner release];
Sort of a crude example, but the documentation for NSScanner is fairly simple.
Edit: Alternatively, you could do something like this:
NSString *someString = <...>; // get your string somehow
NSCharacterSet *charSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSArray *components = [someString componentsSeparatedByCharactersInSet:charSet];
[components
enumerateObjectsWithOptions:NSEnumerationReverse
usingBlock:^(id obj, NSUInteger index, BOOL *stop) {
// do stuff
}];

NSString Backslash escaping

I am working on an iPhone OS application that sends an xml request to a webservice. In order to send the request, the xml is added to an NSString. When doing this I have experienced some trouble with quotation marks " and backslashes \ in the xml file, which have required escaping. Is there a complete list of characters that need to be escaped?
Also, is there an accepted way of doing this escaping (ie replacing \ with \\ and " with \") or is it a case of creating a method myself?
Thanks
NSString *escapedString = [unescapedString stringByReplacingOccurrencesOfString:#"\\" withString:#"\\\\"];
escapedString = [escapedString stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
Doesn't fully answer your question, but seems like it might help with the second part...
You can use a NSScanner that will scan for characters from a character set and if found, it will add the escaping \\ to a new string and copy the next substring from the found special character till the next.
NSString *sourceString = /* Some input String*/;
NSMutableString *destString = [#"" mutableCopy];
NSCharacterSet *escapeCharsSet = [NSCharacterSet characterSetWithCharactersInString:#" ()\\"];
NSScanner *scanner = [NSScanner scannerWithString:sourceString];
while (![scanner isAtEnd]) {
NSString *tempString;
[scanner scanUpToCharactersFromSet:escapeCharsSet intoString:&tempString];
if([scanner isAtEnd]){
[destString appendString:tempString];
}
else {
[destString appendFormat:#"%#\\%#", tempString, [sourceString substringWithRange:NSMakeRange([scanner scanLocation], 1)]];
[scanner setScanLocation:[scanner scanLocation]+1];
}
}