how to write NSDate in text file - objective-c

I'm using [str writeToFile:path atomically encoding error]
to write in file. It works successfully for NSString but doesn't work for NSDate/NSData.
Please tell me how to write in file.

Simple, cheater solution: put it inside a structure that does support writing to a file.
NSDate* variable = ...;
[#[ variable ] writeToFile:path atomically:YES];
That's to say, put it inside an array or dictionary.
If you want to output it to a text file (as you title suggests but you don't mention in the question), you need to convert the date into a string first. Use NSDateFormatter.
Without knowing what's inside your NSData it's not really possible to say how to output to a text file. Maybe convert to base 64 first?

Related

NSMutableArray Meta-Data when exported to CSV file

Looking for some advice with an iOS application.
Essentially what my app does is generate a CSV file that logs certain events within a period of time. So users can press a button and an entry will be added to the log saying "Event of type X happened at Time T"
The way I'm doing this is by maintaining an NSMutableArray which stores NSStrings. Each event adds a string to the NSMutableArray.
When the user is done with a session, they can "export" the file. I'm using the NSMutableArray's writeToFile; then I use an e-mail interface to send that file as a CSV to a target e-mail.
It all works, except the CSV file that is generated has some meta-data in it. Specifically, I believe at the top of the file I see and at the beginning of each row of cells when opened in excel.
Is this something inherent in the data structure (NSMutableArray) or data type (NSString) that I am using? Is there a way for me to just get the raw data?
I can upload code if need be (I'm not near the work computer now though, so I'm testing the waters to see if there is something simple I can do to stop seeing this meta-data).
Thank you!
CSV is a very simple format. You can separate the strings with semi-colons and then write everything to a file using NSOutputStream.
This code assumes you already have a string array with CSV rows:
NSOutputStream* csvoutput = [NSOutputStream outputStreamToFileAtPath:filepath append:NO];
[csvoutput open];
for (NSString* str in array) {
NSString* tempStr = [str stringByAppendingString:#"\n"]; //new line
NSData *strData = [tempStr dataUsingEncoding:NSUTF8StringEncoding];
[csvoutput write:(uint8_t *)[strData bytes] maxLength:[strData length]];
}
[csvoutput close];
You better create a model class (Event) and fill the array with Event-instances instead of strings. Thats cleaner and more efficient. Then you would create the CSV-strings when exporting to a file.

I need help parsing a .csv file based on if a string contains a given substring and saving that string to a new file in Objective C

Basically, I am searching through a list of foods. The fields I am using in the .csv file are: the name of the food, the type of the food represented by a number (type of food would be meat, vegetable, fruit, etc). I want to go through the .csv file line by line, and if the type of food is say a fruit (represented by the number 1), then I want to save that entire line in a new file. I want to go through the ENTIRE list of foods and save each to the new file. How could I go about doing this in objective c? My idea was to search the string for a substring (in this case, the number 1) and if it contains the substring, then I want to save the entire line in a new file, called fruit. I understand this in theory, but am having trouble understanding how to parse a .csv file in this way.
NJones is right - CSV files suck and you don't see them often in Objective-C, so try to avoid them.
However, you can load a CSV file with the following code:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"food" ofType:#"csv"];
NSString *fileContents = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
If you have the contents of a CSV stored in memory, either from a file on disk or from a web service, you can turn it into an array of line records:
NSArray *lines = [fileContents componentsSeparatedByString:#"\n"];
The first line in a CSV file is typically you're header, but after removing it (or ignoring it), it's simply a matter of enumerating over the remaining lines.
for (NSString *record in lines) {
NSArray *values = [record componentsSeparatedByString:#","];
//values is now an array of the contents of the CSV file. Do stuff with it.
}
This code assumes a lot about the CSV file structure, such as the fact that there are no commas in your food names, etc. In general, avoid CSV files whenever possible. Take a look into persisting data on disk as JSON or, better yet, as a Core Data store.
-(BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString )typeName error:(NSError *)outError
{
NSString *fileString = [NSString stringWithContentsOfURL:absoluteURL
encoding:NSUTF8StringEncoding error:outError];
NSLog(#"------- file string ----- %#",fileString);
if ( nil == fileString )
return NO;
NSArray* allLinedStrings =
[fileString componentsSeparatedByCharactersInSet:
[NSCharacterSet newlineCharacterSet]];
NSLog(#"------- file string ----- %#",allLinedStrings);
// write your code how you want display
}
If your csv file is fairly simple with a well defined format, you may have the best luck with your own code based upon the suggestion from #Ash Furrow.
If you are facing a more complicated import, you may find the tutorial and CSV parsing class created by Matt Gallagher to be very helpful. He did a really nice job creating a nice way to deal with CSV files. You can check it out here: http://cocoawithlove.com/2009/11/writing-parser-using-nsscanner-csv.htmlhttp://cocoawithlove.com/2009/11/writing-parser-using-nsscanner-csv.html

NSString writeToFile with URL encoded string

I have a Mac application that keeps it's own log file. It appends info to the file using NSString's writeToFile method. One of the things that it logs are URL's of web services that it is interacting with. To encode the URL, I'm doing this:
searchString = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)searchString, NULL, (CFStringRef)#"!*'();:#&=+$,/?%#[]", kCFStringEncodingUTF8 );
The app then appends searchString to the rest of the URL and writes it to the log file. Now the problem is that after adding that URL encoding line, nothing seems to be getting written to the file. The program functions as expected otherwise however. Removing the line of code above results in all of the correct information being logged to the file (removing that line is not an option because searchString must be URL encoded).
Oh and I am using NSUTF8StringEncoding when writing the NSString to the file.
Thanks for any help.
EDIT: I know there's also a similar function to CFURLCreateStringByAddingPercentEscapes in NSString, but I've read that it doesn't always work. Can anyone shed some light on this if my original question cannot be answered? Thanks! (EDIT: same problem occurs when using stringByAddingPercentEscapesUsingEncoding:)
EDIT 2: Here's the code that I'm using to append messages to the log file.
+(void)logText:(NSString *)theString{
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask,YES) objectAtIndex:0];
NSString *path = [docsDirectory stringByAppendingPathComponent:#"Folder/File.log"];
NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:path] autorelease];
if([fileContents lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= 204800){
fileContents = #"";
}
NSString *timeStamp = [[NSDate date] description];
timeStamp = [timeStamp stringByAppendingString:#": "];
timeStamp = [timeStamp stringByAppendingString:theString];
fileContents = [fileContents stringByAppendingString:timeStamp];
fileContents = [fileContents stringByAppendingString:#"\n"];
[fileContents writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
}
Because after almost a whole day no one else has offered any answers, I'm going to post a wild guess here: you're not accidentally using the string you want to output (with percent characters in it) as a format string are you?
That is, making the mistake of doing:
NSLog(#"In format strings you can use %# as a placeholder for an object, and %i for a plain C integer.")
Instead of:
NSLog(#"%#", #"In format strings you can use %# as a placeholder for an object, and %i for a plain C integer.");
But I'm going to be surprised if this turns out to be the cause of your problem, as it usually causes random-looking output, rather than absolutely no output. And in some cases, Xcode also gives compiler warnings about it (when I tried NSLog(myString), I got "warning: format not a string literal and no format arguments").
So don't shoot me down if this answer doesn't help. It would be easier to answer your question if you could show us more of your logging code. As for the one line you provided, I can't detect anything wrong with it.
Edit: Oops, I kind of missed that you mentioned you're using writeToFile:atomically:encoding:error: to write the string to the file, so it's even more unlikely you're accidentally treating it as a format string somewhere. But I'm going to leave this answer up for now. Again, you should really show us more of your code though ...
Edit: Regarding your question on a method in NSString that has similar percent encoding functionality, that would be stringByAddingPercentEscapesUsingEncoding:. I'm not sure what kind of problems you're thinking of when you say you've heard it doesn't always work. But one thing is that CFURLCreateStringByAddingPercentEscapes allows you to specify extra characters that don't normally have to be escaped but which you still want to be escaped, while the method of NSString doesn't allow you to specify this.

Open .string files as an NSDictionary

How do I open a .string file as an NSDictionary?
Edit: I want to be able to use it like this:
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:#"dict.plist"];
NSDictionary *strings = [[NSDictionary alloc] initWithContentsOfFile:#"Strings.strings"];
If you really want it in a dictionary, you can load it using [NSDictionary dictionaryWithContentsOfFile:], since it is in “Old-style ASCII” format. I have used this technique before on Mac OS X, but I'm not sure you can do the same for iOS.
However, if you want a translation for a particular string, there are at least two ways to do it:
NSLocalizedStringFromTable() will allow you to load strings from files other than the normal Localizable.strings file. Provide the name of your strings file (without the extension).
NSBundle's localizedStringForKey:value:table: method. This essentially performs the same operations as the method above, and as above, provide the name of your strings file without the extension.
.string files just store key/value pairs, like:
"StringKey" = "some localized text";
you can get the text for a specific key using NSLocalizedString. If you want to get all the strings in a file, I suppose you could read the Localizable.strings file and parse it.

How to use string hash more than once?

My application generates loads of images and in order to save memory, I write these files to the temporary directory and read them when needed. I write two versions of the same image to the tmp folder one the thumbnail version at lower resolution and the other is full size. To make the file names unpredictable, I add a string hash at the end.
For instance I want to have two images one called "ThumbnailImage.fgl8bda" and the other "FullImage.fgl8bda"
This is the code I use:
NSString *fileName = #"Image.XXXXXXX";
NSString *thumbName = [#"Thumbnail" stringByAppendingFormat:#"%#", fileName];
NSString * thumbPath = [self writeToTempFile:thumbNailImage andName: thumbName];
NSString *fullName = [#"Full" stringByAppendingFormat:#"%#", fileName];
NSString *fullPath = [self writeToTempFile:fullImage andName: fullName];
However, the two files come out with different names, i.e. each time I use the fileName variable the hash is regenerated. For instance, my two images are called "ThumbnailImage.jhu078l" and "FullImage.ksi9ert".
Any idea how I can use the same hash more than once?
It is generally not safe to reuse a temporary file name suffix, because even if you can ensure A.ashkjd does not exist, there is a chance B.ashkjd is occupied.
You could construct a folder and store the two images in it, e.g.
char tmpl[] = "Image.XXXXXX";
char* res = mkdtemp(tmpl);
if (res != NULL) {
NSString* dirName = [NSString stringWithUTF8String:res];
NSString* thumbPath = [dirName stringByAppendingPathComponent:#"Thumbnail.png"];
[thumbImage writeToFile:thumbPath atomically:YES];
NSString* fullPath = [dirName stringByAppendingPathComponent:#"Full.png"];
[fullImage writeToFile:fullPath atomically:YES];
}
(Note: you need to remove the folder manually.)
#KennyTM has a correct solution, but he didn't explain the cause.
writeToTempFile does not use a hash to fill in the unique part of the name. Instead, it uses a new unique random string for each call.
#TimG
It doesn't really make a difference. That too ends up in different names.
When I write
NSString *fileName = #"Image.XXXXXXX";
The XXXXXXX part of the fileName is replaced by 7 random characters so that the filenames are not predictable.
Cheers
I'm new to objc so apologies if this is a stupid suggestion. What happens if you use stringByAppendingString instead of description that you're invoking:
NSString *thumbName = [#"Thumbnail" stringByAppendingString:fileName];
I don't see why the two aren't equivalent for this usage, but still.
Also, how/where are you generating the hash?
EDIT
#ar06 I think you're saying that your (I'm assuming it's your) writeToTempFile method does a replace on the XXXXX' in the fileName parameter to a random value. If so then there's your problem - it generates a new random every time it's called, and you're calling it twice. The code fragment you posted does work, because fileName is immutable; it will save with a 'XXXXX' extension.
Do you need to refer to these random suffixes later? Whatever mechanism you use for tracking them could be also used by writeToTempFile to attach the same value to the thumb and the fullsize.
FWIW I agree that Kenny's approach is better since you can't guarantee uniqueness.