Reading in from a plist, but accepting only certain strings - objective-c

The goal is to have an array where all strings are of length n.
So at the moment what I have my code doing is reading in a plist (which is just 250,000 strings) into an array, and then iterating over the array in order to find which ones are/aren't of length n. Of course, for the sake of efficiency, I'd prefer being able to read in from the plist STRING BY STRING so, as I'm reading in, I may the length then before inserting into the array. I'm just starting to learn objective-c, but I was struggling to Google around for a solution =P
EDIT: Well I just found out I can find much more documentation typing property list rather than plist into google :) so I may be able to figure this out myself

You can parse plist into tree (NSMutableDictionary). Dict will have keys with name of string length.
for example
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *str in [plistDict allObjects]) {
NSString *key = [NSString stringWithFormat:#"%d", [str length]];
NSMutableArray *array = [result objectForKey:key];
if (!array) {
array = [NSMutableArray array];
}
[array addObject:str];
[result setObject:array forKey:key];
}
than you can access array with needed strings length
NSArray *string4Lenght = [result objectForKey:#"4"];

Apple doesn't provide an API for incrementally parsing a plist.
If you store your plist in XML format, you could use NSXMLParser to parse it. The schema is pretty simple and somewhat described in the Property List Programming Guide.
If you want to incrementally parse the binary format, you're going to have to do more work. There's no official documentation for the format. Apple's source code for reading and writing the format is open source (CFBinaryPList.c) and there are some useful comments along with the actual code.
If you really need to do it incrementally, I suggest going the XML route. If you do, you might want to subclass NSInputStream to be able to read from a gzip or bzip2 file and decompress on the fly.

Related

NSMutableArray to NSData to .txt file - output unreadable

I have an NSMutableArray created storing song names and artists. I am trying to convert this array to NSData format so that I can save it to a text file. When attempting to do so, the output I get is unreadable:
bplist00ÔghX$versionX$objectsY$archiverT$top † ¯$
!")-./3459:;?#AEFGKLMQRSWXY]^_cU$nullÒ
R$0V$class€€#Ò
ZNS.objectsª€€€
€
...etc.
The code I'm using to convert from an NSMutableArray to NSData is:
NSFileManager *fm;
NSData *dataCache = [NSKeyedArchiver archivedDataWithRootObject:myList];
fm = [NSFileManager defaultManager];
if ([fm createFileAtPath:#"/users/nicholasvogler/desktop/Mysongs.txt" contents:dataCache attributes:nil] == NO)
{
NSLog (#"Could not create data file");
return 1;
}
All objects in the array are NSStrings and I've added the following method for the strings and the array:
-(void) encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject: songname];
[encoder encodeObject: artist];
}
What exactly are you expecting the output to be? What you have there is a binary plist. That's what archived data (e.g. NSKeyedArchiver) emits. There's an XML format as well, which can be accessed via NSPropertyListSerialization, although nobody uses the XML format for archived data because the actual plist representation is an implementation detail.
So despite the fact that it's an unreadable binary blob to you, it still encodes what you want, and you can decode it again with NSKeyedUnarchiver.
If all objects in your array are strings (or dictionaries containing strings), it seems to me like it would make more sense to save it as JSON.

Objective-c: Initialize NSMutableArray with NSString that is plist structure

If I have a NSString that came back from a web service in the form of a plist structure, how can I initialize a NSMutableArray with this NSString. I want to know if there is a similar way to initWithContentsOfFile for NSString.
My first thought was to save the NSString to a file and then use initWithContentsOfFile; but I am trying to avoid save to file first. It seems like there should be a simpler way.
Untested, should work like this:
NSData *data = [myString dataUsingEncoding:NSUTF8StringEncoding];
NSMuableArray *array = [NSPropertyListSerialization
propertyListWithData:data
options:NSPropertyListMutableContainers
format:NSPropertyListXMLFormat_v1_0
error:NULL];
See the Property List Programming Guide "Reading and Writing Property-List Data". It covers how to turn NSData into a property list. If you already have NSData from the network, just don't convert it to NSString. If you only have an NSString, use dataUsingEncoding: to convert it to NSData.
Check the documentation for the -propertyList and -mutableCopy methods.
You could use NSXMLParser to parse the XML (which is what a plist is) and turn it into a dictionary (and retrieve the array from there)

NSScanner & Array Loops

I am trying to use NSScanner is an NSArray enumeration loop. This however fails:
-[NSXMLElement length]: unrecognized selector sent to instance 0x280da30
No, I am not calling length anywhere. It seems like the value of found gets changed before the scanner completes, hence making it blank, causing the length error.
If I remove the loop and hardcode the string (i don't want that, it's just a test) the code below works. (excluding the enumeration of course)
It never gets as far as calling the NSLogs either...
Here's my code:
for (NSString*found in arr)
{
NSLog(#"Found %#",found);
NSString*search = [NSString stringWithString:found];
NSString *separatorString = #"\"";
NSString *container = nil;
NSScanner *aScanner = [NSScanner scannerWithString:search];
[aScanner setScanLocation:0];
[aScanner scanUpToString:#"src=\"" intoString:nil];
[aScanner scanString:#"src=\"" intoString:nil];
[aScanner scanUpToString:separatorString intoString:&container];
NSLog(#"scanned");
NSLog(#"%#",container);
NSImage*previewImage = [[NSImage alloc] initWithContentsOfFile:[relativeDir stringByAppendingPathComponent:container]];
[preview setImage:previewImage];
[previewImage release];
progressed = progressed + 1;
[convertProgress setDoubleValue:[convertProgress doubleValue] + progressed];
}
How can I get this to work?
Edit:
Here's the input:
NSXMLDocument*doc = [[NSXMLDocument alloc] initWithData:[NSData dataWithContentsOfFile:webPath] options:NSXMLDocumentTidyHTML error:nil];
NSArray*arr = [doc nodesForXPath:#"//img[#src]" error:nil];
From the documentation:
The nodesForXPath:error: method
returns an array of NSXMLNode objects
Your array is not full of strings. It is full of various NSXML* objects. They aren't garbage. This is exactly how it is supposed to work.
You may not be calling length, but stringWithString: is probably calling length to figure out how much space to allocate in the new string.
Conversion to a string can mean a lot of things. An XML node may be a standalone tag, it may be some inline CDATA, it may be a tag with various attributes. As well, an XML document is highly structured. It is rarely meaningful to walk through the contents of a document as a straight enumeration of tags/contents without applying some amount of structural meaning.
You are likely going to need to recurse or otherwise walk the tags and interpret their contents appropriately.
The first step would be to remove everything but that initial NSLog() and see what you got. Then figure out how to walk the tree of nodes. Then figure out how to parse 'em appropriately.
Are you sure that your arr contains NSString objects? The fact that NSXMLElement is throwing an error looks fishy to me.
Edit
Yep, looks like the docs say the array from nodesForXPath doesn't contain NSString objects, but NSXMLElement objects.
http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSXMLNode_Class/Reference/Reference.html#//apple_ref/occ/instm/NSXMLNode/nodesForXPath:error:

Objective-C: How do you append a string to an NSMutableString?

URL Download
http://code.google.com/p/mwiphonesdk/source/browse/#svn/trunk/iMADE/PrepTasks/08
I have code at the location at the link above and I am using NSMutableString to append strings as data is downloaded. But I am finding that using appendString does not increase the length of the mutable string. I must be doing something wrong.
And when I am done I need to convert NSMutableString to NSString to return it. I have looked for examples to do this but I do not see any yet.
I am most familiar with Java and C# where there is a StringBuffer/StringBuilder which allows you to append pieces and them simply call toString at the end to get a String. It does not appear to be this easy in Objective-C.
NSString* str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
#pragma mark TODO Confirm this is appending a value to the mutable string
[self.mutableString appendString:str];
NSLog(#"str length: %d, %d", [str length], [self.mutableString length]);
Above is the section of code that calls appendString.
I see that str has a length > 0 but calling appendString does not increase the length of self.mutableString.
What am I doing wrong here?
As for having an NSMutableString* and needing to return an NSString*, the former is a subclass of the latter so anywhere you see an NSString* an NSMutableString* will suffice as-is.
Your code looks OK from what you've posted. The only thing I can think of is that perhaps there isn't any data to speak of when initializing the str variable. In such a case appending an empty string will do nothing to mutableString.
You'll also want to make sure self.mutableString has been properly allocated and initialized. You can send messages to NSObject*s that are nil which may be misleading when [self.mutableString length] returns 0.
I have fixed the problem. I simply was not initializing the NSMutableString value and it was transparently not doing anything.
Before appending the string I put the following code.
if (_mutableString == nil){
_mutableString = [[NSMutableString alloc] init];
}
Thanks everyone for answering. And it is good to know that I can use NSMutableString in place of NSString. (that is too easy) :)

Parsing XML file using NSXMLParser

I have to parse an XML file using NSXMLParser. There are so many HTML tags in them, so when I am trying to parse it, it will store the string up to that and then again go to found character method and starts to append it.
my code is:
if (Bio_CResults) {
[BioResults appendString: string];
[Info appendString:string];
[stringarr addobject:Info];
NSLog(#"bio==%#",BioResults);
NSLog(#"string==%#",string);
}
and I want to add it in string array, but here it will make create extra object of array. i.e.
stringarr objectAtIndex 0 = abc
stringarr objectAtIndex 1 = def
stringarr objectAtIndex 2 = ghi
but actually I want all of them together in one object because they are actually one string only..
plz help me for that
If you don't even need multiple string objects you can use an NSMutableString instead of an array. Just use the appendString: method to add to the end of the string:
NSMutableString *string = [NSMutableString string];
[string appendString:#"abc"];
[string appendString:#"def"];
NSLog(#"New string is %#", string);
This will log "New string is abcdef".
If you really want an array, use an NSMutableArray instead of an NSArray. That way you can change an object in-place (replace a string with a new string created by appending another string). So for example:
// First create an array with #"abc"
NSMutableArray *mArray = [NSMutableArray arrayWithObject:#"abc"];
// Next get the first object as a string and append #"def" to it in-place
NSString *string = (NSString *)[mArray objectAtIndex:0];
[mArray replaceObjectAtIndex:0 withObject:[string stringByAppendingString:#"def"]];
// Now get the new first object
NSLog(#"First object is %#", (NSString *)[mArray objectAtIndex:0]);
This will log the message "First object is abcdef".
I really dont like NSXmlParser. You might want to read my blog post on using RegxKitLite
http://blog.bluespark.co.nz/?p=51
It might be of some help. Hopefully it wont lead you in the wrong direction.
Cheers, John.
There are so many HTML tags in them…
Be aware that HTML is usually not valid XML. If you're parsing HTML, an XML parser will throw an error some part of the way through the document.
You may be better off creating a WebView, loading the HTML content into it, and then getting a DOMDocument from the view's main frame and traversing the DOM hierarchy. You don't have to put the view into a window; you can just use it to get a DOMDocument.
If you are parsing XML and meant to say “XML tags”, you can disregard this answer.