Using NSScanner - iphone

I am trying to scan a text, but I do not understand how it works, could anybody help me??
<a class="lightbox" title ="elecciones mexico 2012" href="http://www.myWebpage.com/wp-content/uploads/2012/07/elecciones-mexico-2012.jpg"><img src="http://www.myWebpage.com/wp-content/uploads/2012/07/elecciones-mexico-2012.jpg" alt="" title="elecciones mexico 2012" width="643" height="391" class="aligncenter size-full wp-image-66795" /></a></p>
<p>I need this text</p>
<p> And this text.</p>
<p> Also this text! </p>
<p> I dont want this text </p>]]>
So that my final string would be something like:
I need this text
And this text
Also this text!
Thanks in advance

Wasting your shot. That's why NSXMLParser is there.
#interface TextParser: NSObject {
NSMutableString *text;
}
- (id)init
{
if ((self = [super init]))
{
text = [[NSMutableString alloc] init];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:[string dataUsingEncoding:NSUTF8Stringaencoding];
parser.delegate = self;
[parser parse];
// here text will contain all the text contained by the XML tags
}
return self;
}
- (void)parser:(NSXMLParser *)p foundCharacters:(NSString *)chars
{
[text appendString:chars];
}

Well, the NSScanner that I know (MacOS) isn't particulary suitable for the kind of parsing you are looking for. It just goes through a string and returns "tokens" like numbers or strings defined by the set of characters they are made of. This is not particulary useful to process the tags in your string example, unless you are willing to accept a high chance of errors.
In that case, you could probably do something like "read a string composed of anything but <" and attach that to the result string, then "read a string composed of anything but >" and discard that and so on, until you have reached the end. Depending on what you are actually trying to parse this may or may not work; it's definitely not "the way" to get the plain text from HTML.
It's not XML either (the tags don't match), so using NSXML probably isn't an option either...

Related

How to i parse elements with [CDATA] tags

I'm trying to pull images from my tag using NSScanner, but my tag has a [CDATA] block that makes my NSXLM parser ignore everything inside that tag. How can i parse the data inside my tag, heres a sample of the rss feed , and a sample of the parser code.
<item>
<title>Kendrick Lamar & Lady Gaga in Chi-Town</title>
<link>http://www.motahiphop.com/rap-pix/36-rap-pix/2346-kendrick-lamar-lady-gaga-in-chi-town</link>
<guid isPermaLink="true">http://www.motahiphop.com/rap-pix/36-rap-pix/2346-kendrick-lamar-lady-gaga-in-chi-town</guid>
<description><![CDATA[<p style="text-align: center;"><img src="http://www.motahiphop.com/images/lady-gaga-kendrick-lamar.jpg" width="500" alt="Kendrick Lamar with Lady Gaga at Pitchfork festival" /></p>
After tweeting that she was a Kendrick Lamar fan last week. Lady Gaga caught up with the Compton MC back stage at Chicago's Pitchform Music festival.]]>
gqwebsites#gmail.com (Super User)
Featured
Rap Pix
Rap Pix
Mon, 16 Jul 2012 13:18:45 -0400
Parser code snippet:
if ([elementName isEqualToString:#"item"])
{
elements[#"title"] = title;
elements[#"date"] = date;
elements[#"summary"] = summary;
elements[#"link"] = link;
elements[#"description"] = description;
//NSLog(#"%#", description);
[posts addObject:elements];
}
after some research I found the answer on another post, I used this method
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
NSString *description = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
NSString *storyImageURL = [self getFirstImageUrl:description];
NSLog(#"%#",storyImageURL);
I added the NSLog at the end so you can NSLog the results and check that the data is being pulled. works like a charm

iPhone: what's the best way to display a "report'?

I have an app where I need to display a report of six (6) columns and a variable number of rows (lines).
What's the best approach to do this? I tried UITextView but there is no way to set the font size to make the data fit on each line.
I usually use UIWebViews for this.
A dozen lines to create a html table out of a NSArray and some nice CSS.
Edit: I can show you an example I wrote in the last 5 minutes. There's not much to it if you have a basic understanding of html. Of course you could take it much further than that.
And even if you don't know anything about html I'm sure you can learn the basics for some simple table display within a couple of hours.
- (NSString *)htmlTableRowFromArray:(NSArray *)array withOpenTag:(NSString *)openTag andCloseTag:(NSString *)closeTag {
NSMutableString *rowHtmlStr = [NSMutableString string];
for (NSString *str in array) {
[rowHtmlStr appendFormat:#"%# %# %#\n", openTag, str, closeTag];
}
return rowHtmlStr;
}
- (void)displayReport {
NSArray *dataArray = ...;
NSArray *myHeader = ...;
NSMutableString *htmlStr = [NSMutableString stringWithString:#"<html><head><title>MyTable</title></head><body>"];
// the table starts here
[htmlStr appendString:#"<table width=\"100%\" style=\"background-color:#CCC\" border=\"1\" cellpadding=\"4\" cellspacing=\"0\">"];
// this will create a table header
[htmlStr appendFormat:#"<tr>%#</tr>", [self htmlTableRowFromArray:myHeader withOpenTag:#"<th>" andCloseTag:#"</th>"]];
for (NSArray *line in dataArray) {
// one row for each array in the data
[htmlStr appendFormat:#"<tr>%#</tr>", [self htmlTableRowFromArray:line withOpenTag:#"<td>" andCloseTag:#"</td>"]];
}
// table ends
[htmlStr appendString:#"</table>"];
[htmlStr appendString:#"</body></html>"];
[webView loadHTMLString:htmlStr baseURL:nil];
}
the table should look like this. Imho not that bad for the amount of work.

Ignoring certain strings when sorting an array

I’m making a languages application, and I have a long list of vocabulary relating to that language (German, in case anyone was interested). I have the functionality in my app to switch between sorting the tableview by German words, or by english words.
When I use the following:
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:type];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(compare:)];
it works absolutely perfectly - by which I mean, exactly as expected. What I would like to improve on this however, is that there are certain words, such as verbs or nouns, which are always preceded by prefixes, like “to”, as in “to do something”, or “the” in front of nouns. So what I would like to do is somehow exclude these from my sort, because otherwise I end up with all the verbs being sorted alphabetically under the “t” section in my array, which is not very user friendly.
I’ve looked through the Apple documentation about NSString and NSArray, as this is where the compare function is (unless I’m very much mistaken), and I haven’t found any way that makes sense to me. This is the first time I have done any data handling like this so I may be missing something simple, and so I would really appreciate some help.
Thanks very much
Michaeljvdw
You're on the right track. What you want to use instead of the (built-in) compare method is to write your own method, which can eliminate the "to" or "the" bits if they exist, and then use the existing compare method.
Your call would look something like this:
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(myCompare:)];
Using a custom category you give to NSString with the following methods:
// This method can be exposed in a header
- (NSComparisonResult)myCompare:(NSString*)aString
{
NSString* selfTrimmed = [self removeArticles];
NSString* aStringTrimmed = [s2 removeArticles];
return [self compare:aString];
}
// This method can be kept private in the .m implementation
- (NSString*)removeArticles
{
NSRange range = NSMakeRange(NSNotFound, 0);
if ([self hasPrefix:#"to "])
{
range = [self rangeOfString:#"to "];
}
else if ([self hasPrefix:#"the "])
{
range = [self rangeOfString:#"the "];
}
if (range.location != NSNotFound)
{
return [self substringFromIndex:range.length];
}
else
{
return self;
}
}
You might have some luck with localizedCompare: or localizedStandardCompare:, but I don't think that either of these will strip out articles and prepositions like you want. Instead, you will probably have to define a category on NSString that provides the specific style of sorting you're looking for:
#interface NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other;
#end
#implementation NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other {
NSMutableString *mutableSelf = [NSMutableString stringWithString:self];
[mutableSelf
replaceOccurrencesOfString:#"das"
withString:#""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [mutableSelf length])
];
...
// delete articles from 'other' too
NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *trimmedSelf = [mutableSelf stringByTrimmingCharactersInSet:trimSet];
NSString *trimmedOther = ...;
return [trimmedSelf localizedCaseInsensitiveCompare:trimmedOther];
}
#end
You can then use #selector(compareWithoutArticles:) as your sort selector for NSArray.
First, don't use compare:. Use localizedCompare: instead. This is important, because whether á appears just after a or after z as a separate letter depends on the language. localizedCompare: takes care of that.
--edit
As Justin says, localizedStandardCompare: is the selector to be used! I didn't know that method. As written in the documentation, localizedStandardCompare: does more than localizedCompare:, although the document doesn't say exactly what it does.
--end of edit
If you want more, you need to implement that yourself. You can use category for that purpose. First declare it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string;
#end
and then implement it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string{
...
}
#end
This way you can add methods to an existing class. Then you can use
NSArray *array = [[string componentsSeparatedByString:#"\n"]
sortedArrayUsingSelector:#selector(michaelsSuperCompare:)];
It is important to prefix the method name with something distinctive, not to accidentally crash with internal methods used by Apple.
As for the functionality, you need to implement that yourself, as far as I know. You can get the current locale with [NSLocale currentLocale]. You can implement a nicer behavior for the languages you know, and then default to localizedCompare: for unknown languages.
I would somehow do -replaceOccurancesOfStrings on all the data eg "To" -> "" - and then reload the data. (or this can in a text editor)
Another thing to think about is having eg 'to walk' changed to 'walk (to)' which can be done ahead of time (and will also create less confusion for the user as they are scrolling alphabetically).

How to create a formatted localized string?

I have a localized string which needs to take a few variables. However, in localization it is important that the order of the variables can change from language to language.
So this is not a good idea:
NSString *text = NSLocalizedString(#"My birthday is at %# %# in %#", nil);
In some languages some words come before others, while in others it's reverse. I lack of an good example at the moment.
How would I provide NAMED variables in a formatted string? Is there any way to do it without some heavy self-made string replacements? Even some numbered variables like {%#1}, {%#2}, and so on would be sufficient... is there a solution?
This is why NSLocalizedString takes two parameters. Use the second parameter to include a comment describing the native language meaning of the variables. Then, translators can reorder them using the $ + number construct. See Apple's Notes for Localizers.
However you cannot skip parameters in one language. For example, if you have 3 parameters in English and 4 in French and you don't need the third in English, you cannot format like %1$# %2$# and %4$#. You can only skip the last one.
Formatted localized string example:
NSString *today = [MyHandWatch today];
NSString *msg = [NSString stringWithFormat:NSLocalizedString(#"Today is %#", #""), today];
genstrings will generate this line in your Localizable.strings file:
"Today is %#" = "Today is %#";
I solved this in a project a few weeks back by building my own simple template system with NSScanner. The method uses a template system that finds variables with the syntax ${name}. Variables are supplied to the method through an NSDictionary.
- (NSString *)localizedStringFromTemplateString:(NSString *)string variables:(NSDictionary *)variables {
NSMutableString *result = [NSMutableString string];
// Create scanner with the localized string
NSScanner *scanner = [[NSScanner alloc] initWithString:NSLocalizedString(string, nil)];
[scanner setCharactersToBeSkipped:nil];
NSString *output;
while (![scanner isAtEnd]) {
output = NULL;
// Find ${variable} templates
if ([scanner scanUpToString:#"${" intoString:&output]) {
[result appendString:output];
// Skip syntax
[scanner scanString:#"${" intoString:NULL];
output = NULL;
if ([scanner scanUpToString:#"}" intoString:&output]) {
id variable = nil;
// Check for the variable
if ((variable = [variables objectForKey:output])) {
if ([variable isKindOfClass:[NSString class]]) {
// NSString, append
[result appendString:variable];
} else if ([variable respondsToSelector:#selector(description)]) {
// Not a NSString, but can handle description, append
[result appendString:[variable description]];
}
} else {
// Not found, localize the template key and append
[result appendString:NSLocalizedString(output, nil)];
}
// Skip syntax
[scanner scanString:#"}" intoString:NULL];
}
}
}
[scanner release];
return result;
}
With a localize file looking like this:
"born message" = "I was born in ${birthYear} on a ${birthWeekDay}. ${byebye}";
"byebye" = "Cheers!";
We can accomplish the following results...
NSDictionary *variables = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1986], #"birthYear", #"monday", #"birthWeekDay", nil];
NSString *finalString [self localizedStringFromTemplateString:#"born message" variables:variables];
NSLog(#"%#", finalString); // "I was born in 1986 on a monday. Cheers!"
As you can see, I've added some extra functionality too. Firstly, any variables that aren't found (${byebye} in my example) will be localized and appended to the results. I did this because i load in HTML-files from my application bundle and run them through the localize method (when doing that, I don't localize the input string when creating the scanner though). Also, I added the ability to send in other things than just NSString objects, for some extra flexibility.
This code maybe isn't the best performing or prettiest written, but it does the job without any noticeable performance impacts :)

Send outline as email using MFMailComposeViewController

I'd like to send a text outline via email using MFMailComposeViewController. I'm currently doing this by creating a HTML version of the document and setting this as the body of the message.This works fine when viewing the email in MFMailComposeViewController, but on the receiving end (MobileMail.app, GMail Webinterface, OSX Mail.app) the formatting is lost.
This is my current code:
- (IBAction)showMailController {
if (![MFMailComposeViewController canSendMail]) return;
MFMailComposeViewController *mailComposeViewController = [[MFMailComposeViewController alloc] init];
mailComposeViewController.mailComposeDelegate = self;
NSString *stringRepresentation = #"<html><head></head><body><ul><li>#grocery</li><ul><li>Milk</li><li>Cat food</li><li>Rice</li><li>Tofu</li></ul></ul></body></html>";
[mailComposeViewController setMessageBody:stringRepresentation isHTML:YES];
[self presentModalViewController:mailComposeViewController animated:YES];
[mailComposeViewController release];
}
Am I doing something wrong? Is there an other way to do it?
I also tried exporting as simple text outline with manually indenting (I inserted two spaces as tab didn't work), but text editors don't recognize this as an outline.
NSMutableString *inset = [[NSMutableString alloc] init];
NSUInteger i;
for (i=0; i<insetCount;i++) {
[inset appendString:#" "];
}
NSMutableString *string = [[NSMutableString alloc] initWithString: [NSString stringWithFormat:#"%#%C ", inset, 0x2022]];
Thank you for your help!
In my apps, I only put what is in between the body tags, i.e. NO
html
head
body
tags. Just put the stuff you would normally put in between the body tags and it should work.