parsing text file in objective C - iphone

i am trying to parse a text file saved in doc dir below show is the code for it
NSArray *filePaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *docDirPath=[filePaths objectAtIndex:0];
NSString *filePath=[docDirPath stringByAppendingPathComponent:#"SKU.txt"];
NSError *error;
NSString *fileContents=[NSString stringWithContentsOfFile:filePath];
NSLog(#"fileContents---%#",fileContents);
if(!fileContents)
NSLog(#"error in reading file----%#",error);
NSArray *values=[fileContents componentsSeparatedByString:#"\n"];
NSLog(#"values-----%#",values);
NSMutableArray *parsedValues=[[NSMutableArray alloc]init];
for(int i=0;i<[values count];i++){
NSString *lineStr=[values objectAtIndex:i];
NSLog(#"linestr---%#",lineStr);
NSMutableDictionary *valuesDic=[[NSMutableDictionary alloc]init];
NSArray *seperatedValues=[[NSArray alloc]init];
seperatedValues=[lineStr componentsSeparatedByString:#","];
NSLog(#"seperatedvalues---%#",seperatedValues);
[valuesDic setObject:seperatedValues forKey:[seperatedValues objectAtIndex:0]];
NSLog(#"valuesDic---%#",valuesDic);
[parsedValues addObject:valuesDic];
[seperatedValues release];
[valuesDic release];
}
NSLog(#"parsedValues----%#",parsedValues);
NSMutableDictionary *result;
result=[parsedValues objectAtIndex:1];
NSLog(#"res----%#",[result objectForKey:#"WALM-FT"]);
The problem what i am facing is when i try to print lineStr ie the data of the text file it is printing as a single string so i could not able to get the contents in line by line way please help me solve this issue.

Instead use:
- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
it covers several different newline characters.
Example:
NSArray *values = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *lineStr in values) {
// Parsing code here
}
ALso seperatedValues is over released. First one is created with alloc init, then on the next line it is replaced by the method componentsSeparatedByString. So the first one od lost without being released, that is a leak. Later the seperatedValues created by componentsSeparatedByString is released but it is already auto released by componentsSeparatedByString to that is an over release;
Solve all the retain/release/autorelease problem with ARC (Automatic Reference Counting).
Here is a version that uses convenience methods and omits over release:
NSArray *values = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *lineStr in values) {
NSArray *seperatedValues = [lineStr componentsSeparatedByString:#","];
NSString *key = [seperatedValues objectAtIndex:0];
NSDictionary *valuesDic = [NSDictionary dictionaryWithObject:seperatedValues forKey:key];
[parsedValues addObject:valuesDic];
}
NSLog(#"parsedValues---%#",parsedValues);

Are you sure the line separator used in your text file is \n and not \r (or \r\n)?
The problem may come from this, explaining why you don't manage to split the files into different lines.

Related

How to get first value from NSMutableArray (iPhone)?

Now i am working in simple iphone application, i have stored some value in NSMutableArray like "{54.399, 196}","{-268.246, 273}".so i want to get 54.399 in first indexpath, how to get this, please help me
Thanks in Advance
It seems you have an Array of Arrays, so it would be:
[[myArray objectAtIndex:0] objectAtIndex:0];
Or using subscripting:
myArray[0][0];
Edit:
Ok you have an Array of NSStrings. To do what you want (get the 53.399) do the following:
NSString *myString = [myArray objectAtIndex:0];
NSArray *stringComponents = [myString componentsSeparatedByString:#","];
NSString *myFinalString = [stringComponents objectAtIndex:0];
With subscripting:
NSString *myFinalString = [[myArray[0] componentsSeparatedByString:#","][0];
You can use
[[arrayObj objectAtIndex:0] objectAtIndex:0];

Read/Write plist on iPhone iOS 5

I am studying iPhone development and facing a problem with a reading/writing plist file. I followed an example from a iPhone development book but keep getting an error message when running.
The error message says : 2012-04-26 00:21:09.759 FileHandling[5915:207] -[__NSCFDictionary addObject:]: unrecognized selector sent to instance 0x685ac40
Here is the example code (it seems fine to me...though):
NSString *plistFileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
NSLog(#"Where is the file? => %#", plistFileName);
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileName]) {
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileName];
for (NSString *category in dict) {
NSLog(#"%#", category);
NSLog(#"=========");
NSArray *titles = [dict valueForKey:category];
for (NSString *title in titles) {
NSLog(#"%#", title);
}
}
} else {
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Apps" ofType: #"plist"];
NSLog(#"%#", plistPath);
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile: plistPath];
NSLog(#"Let's take a look : %#", dict);
NSMutableDictionary *copyOfDict = [dict mutableCopy];
NSLog(#"Let's look at the mutable dictationary : %#", copyOfDict);
NSArray *categoriesArray = [[copyOfDict allKeys] sortedArrayUsingSelector: #selector(compare:)];
for (NSString *cateogry in categoriesArray) {
NSArray *titles = [dict valueForKey: cateogry];
NSMutableArray *mutableTitles = [titles mutableCopy];
[mutableTitles addObject: #"New App Title"];
[copyOfDict setObject: mutableTitles forKey:cateogry];
}
NSString *fileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
[copyOfDict writeToFile: fileName atomically:YES];
}
According to the error message, the problem is occurring in the call to addObject: on an __NSCFDictionary. This means that, at runtime, a dictionary received a message to add an object.
However, in this code snippet, addObject: is apparently being sent to an NSMutableArray. This probably means that each object titles you're retrieving from dict in the last for-loop is not an array, but in fact another dictionary, that your code is simply referring to as an array.
Indeed, your code does seem well-formed, so check the well-formedness of your source plist; open it up in a plain text editor. Also, you use a ton of logging, so confirm this way: in the output, dictionaries (including the root entry) are denoted by {curly = braces}, where arrays are denoted by (round parentheses).

write location / gps coordinates to a file

Well, I know it may sounds basic, but I have literally been looking everywhere and could not find a straight answer to that. I am trying to save location coordinates to a file every time I get an update - sounds simple.... I have two problems: one is with the data type (writeToFile seems to save only NSData) and the other one is with appending to the end of the file. I tried to use NSKeyedArchiver but it wrote a bunch of garbage and I could not find how to append to the end of file with it.
Here is my code - if you could help I would greatly appreciate that. Thanks!
....
NSMutableArray *array = [[NSMutableArray alloc] init];
NSNumber *numLat = [NSNumber numberWithFloat:location.coordinate.latitude];
NSNumber *numLong = [NSNumber numberWithFloat:location.coordinate.longitude];
[array addObject:numLat];
[array addObject:numLong];
NSFileHandle *file;
file = [NSFileHandle fileHandleForUpdatingAtPath: #"./location.txt"];
if (file == nil)
NSLog(#"Failed to open file");
[file seekToEndOfFile];
[file writeData: array]; //BTW - this line doesn't work if I replace array with numLat which is an NSNumber - unlike what many people have said in various discussions here
OR - for the saving to file portion (last two lines):
NSString *path = #"./location.txt";
[NSKeyedArchiver archiveRootObject:array toFile:path];
// Get the path to the Documents (this is where your app saves data)
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsPath = [searchPaths objectAtIndex: 0];
[array writeToFile:[documentsPath stringByAppendingPathComponent:#"location"] atomically:YES];
To load the data back into the array, use
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsPath = [searchPaths objectAtIndex: 0];
array = [[NSMutableArray alloc] initWithContentsOfFile:[documentsPath stringByAppendingPathComponent:#"location"];

warning: Attempting to create USE_BLOCK_IN_FRAME variable with block that isn't in the frame

What does mean? I get this error when trying to iterate through a file in Cocoa obj-c.
I can't find any information on the web.
Would appreciate some help. Thanks.
EDIT
I've been following this tutorial (link) to preload Core Data. I've tried creating a Cococa application and have also tried doing this from within my iPhone app. I think all my setup code for Core Data is fine. Whenever this method is called I get EXEC BAD ACCESS.
- (void)loadInitialData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// name ZSTREET_1 ZSTREET_2 ZCITY ZZIP ZURL ZTEL latitude longitude
NSString *path = [[NSBundle mainBundle] pathForResource:#"placesdata" ofType:#"txt"];
NSString *fileString = [NSString stringWithContentsOfFile:path]; // reads file into memory as an NSString
NSArray *lines = [fileString componentsSeparatedByString:#"\r"]; // each line, adjust character for line endings
NSManagedObjectContext *context = [self managedObjectContext];
for (NSString *line in lines)
{
NSLog(line);
NSString* string = [[NSString alloc] initWithUTF8String:line];
NSArray *parts = [string componentsSeparatedByString:#"\t"];
// value mapping
NSString *name = [parts objectAtIndex:0];
NSString *street_1 = [parts objectAtIndex:1];
NSString *street_2 = [parts objectAtIndex:2];
NSString *city = [parts objectAtIndex:3];
NSString *zip = [parts objectAtIndex:4];
NSString *url = [parts objectAtIndex:5];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *latitude = [f numberFromString:[parts objectAtIndex:6]];
NSNumber *longitude = [f numberFromString:[parts objectAtIndex:7]];
[f release];
// splitting the parts to create the objects
Place *place = (Place *)[NSEntityDescription insertNewObjectForEntityForName:#"Place" inManagedObjectContext:context];
Address *address = (Address *)[NSEntityDescription insertNewObjectForEntityForName:#"Address" inManagedObjectContext:context];
Location *location = (Location *)[NSEntityDescription insertNewObjectForEntityForName:#"Location" inManagedObjectContext:context];
// set attributes
[place setValue:name forKey:#"name"];
[address setValue:street_1 forKey:#"street_1"];
[address setValue:street_2 forKey:#"street_2"];
[address setValue:city forKey:#"city"];
[address setValue:zip forKey:"#zip"];
[address setValue:url forKey:#"url"];
[location setValue:latitude forKey:#"latitude"];
[location setValue:longitude forKey:#"longitude"];
// link the objects together
[place setValue:address forKey:#"address"];
[place setValue:location forKeyPath:#"address.location"];
[string release];
}
NSLog(#"Done initial load");
NSError *error;
if (![context save:&error]) {
NSLog(#"Error saving: %#", error);
}
[context release];
[pool drain];
}
For other people running into this problem with entirely different code, this is a bit of a red herring.
The warning is from the debugger itself. The debugger creates a struct containing info for each object in the system. After the EXC_BAD_ACCESS, it tried to create one of these but was unable to. Note that this is a warning and not an error so it may even be expected in situations like this.
In any event, the details surrounding this don't matter. You've got to find the source of your EXC_BAD_ACCESS. A common cause is trying to access an object after it has been released.
For someone who comes across this in the future, I got this problem because I was doing this too much:
NSString* aString = #"";
for(int i=0; i<someLargeNumber; i++) {
aString = [aString stringByAppendingFormat:#"..."];
}
Once I switched to using NSMutableString, the problem was resolved.
This line is wrong and should produce a compiler warning:
NSString* string = [[NSString alloc] initWithUTF8String:line];
The method initWithUTF8String: expects an UTF-8 encoded C string and not a NSString object.
Before you continue you should fix all compiler warnings! And you also should check that the parts array actually contains as many objects as you expect. You also need to use a format string with NSLog, you might even crash there if your line contains any % characters.
I happen to run into EXC_BAD_ACCESS on a fairly regular basis (which is not a good thing), because we haven't enabled ARC yet.
I find the best way to track those errors down by using the Profiler in Zombie modus. This post shows you how to "hunt for zombies":
How do I set up NSZombieEnabled in Xcode 4?

Should I use plist, SQLite or NsMutableArray?

If I want to store 2 strings, and ABContact image (or a variable to call the image) that persist even after restarting the application, which method should I use? NsMutableArray, plist or SQLite?
For very small amounts of data, like a few strings, but not an image, you could also use the much simpler NSUserDefaults. This is usually for saving preferences or some persistent data.
To save it:
[[NSUserDefaults standardUserDefaults] aString forKey:#"aStringKey];
if (![[NSUserDefaults standardUserDefaults] synchronize])
NSLog(#"not successful in writing the default prefs");
To read it:
aString = [[NSUserDefaults standardUserDefaults] stringForKey:#"aStringKey];
Again, I wouldn't go ahead and use this if what you really need is a database or a filesystem.
If you are putting in more data, and not enough to warrant an SQL database or Core Data, then I would write the data to a plist file. If all your data are objects, then I would do this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:kSavedDocFileName];
NSMutableArray *myArrayToSave = [[NSMutableArray alloc] arrayWithCapacity: 48];
// 48 since you said in the comments that you have 48 of these
for (int i = 0; i < 48; i++) {
NSDictionary *myDict = [[NSDictionary alloc] initWithObjectsAndKeys:
sting1[i], #"firstStr", //I'm assuming your strings are in a C array.
string2[i], #"secondStr",// up to you to replace these with the right
url[i], #"myURL", //way to access your 48 pieces of data
nil];
[myArrayToSave addObject:myDict];
[myDict release];
}
if (![myArrayToSave writeToFile:path atomically:YES])
NSLog(#"not successful in saving the unfinished game");
And you can read the data:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:kMySavedDocName];
NSArray *myLoadedData = [[NSArray alloc] initWithContentsOfFile:path];
for (int i = 0; i < 48; i++) {
NSDictionary *myDict = [myLoadedData objectAtIndex:i];
string1[i] = [myDict objectForKey:#"firstStr"];
string2[i] = [myDict objectForKey:#"secondStr"];
url[i] = [myDict objectForKey:#"url"];
}
[myLoadedData release];
Make sure you don't just copy and paste this code since I just typed it in off the top of my head.
Now, if you don't want to mess with a dictionary, you could do that too. It's a little less elegant:
Save:
NSMutableArray *myArrayToSave = [[NSMutableArray alloc] arrayWithCapacity: 48 * 3];
// 48 since you said in the comments that you have 48 of these three objects
for (int i = 0; i < 48; i++) {
[myArrayToSave addObject:string1[i]];
[myArrayToSave addObject:string2[i]];
[myArrayToSave addObject:url[i]];
}
if (![myArrayToSave writeToFile:path atomically:YES])
NSLog(#"not successful in saving the unfinished game");
Load:
NSArray *myLoadedData = [[NSArray alloc] initWithContentsOfFile:path];
for (int i = 0; i < 48; i++) {
string1[i] = [myLoadedData objectAtIndex:i * 3];
string2[i] = [myLoadedData objectAtIndex:i * 3 + 1];
url[i] = [myLoadedData objectAtIndex:i * 3 + 2];
}
[myLoadedData release];
Another important note is that in all these examples, I am assuming that you are dealing with objects (48 times 3 of them). That is why you can just add them to dictionaries and arrays and save them and reload them easily. If you were dealing with non-objects, like ints, or c strings or BOOLs, then you will have to use [NSNumber ...] to turn those into objects and then [object ...] to turn them back into non-ojects to begin with.
Try some of these, and then go find your app folder in ~/Library/Application Support/iPhone Simulator/Applications folder, and look in the Documents folder, open the file you have saved, and verify its contents. It's always reassuring to be able to check the data you write out - and to see how the different methods change the contents.
Good luck!
An NSMutableArray will not persist after the application is restarted.
You may want to look at a plist that stores the two strings and a file URL pointing to the address book image that is cached in the application's Documents sandbox.
If you have lots of these to store and retrieve between application restarts, look into Core Data.
Don't do a database. It's way overkill for that little data.
A plist would be fine. You should probably not use NSUserDefaults, I think that's right on the borderline of too much data for it. Just write a plist to the documents directory inside your application, and load it up when your application restarts. You can store an NSMutableArray in a plist, but it comes back to you as an NSArray. Just re-create it as a mutable array with [NSMutableArray arrayWithArray:] and retain it.
(void)doneAceSpadeSetupClick:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *string1;
NSString *string2;
NSString *string3;
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"card1.plist"];
NSMutableArray *myArrayToSave = [[NSMutableArray alloc] initWithCapacity: 1];
//48 since you said in the comments that you have 48 of these
NSDictionary *myDict = [[NSDictionary alloc] initWithObjectsAndKeys:
string1, characterText.text, //I'm assuming your strings are in a C array.
string2, actionText.text,// up to you to replace these with the right
string3, objectText.text,
nil]; //iPhone simulator crashed at this line
[myArrayToSave addObject:myDict];
if (![myDict writeToFile:path atomically:YES])
NSLog(#"not successful in saving the unfinished game");
[self dismissModalViewControllerAnimated: YES];
}