How can I read and write an integer to and from a text file, and is it possible to read or write to multiple lines, i.e., deal with multiple integers?
Thanks.
This is certainly possible; it simply depends on the exact format of the text file.
Reading the contents of a text file is easy:
// If you want to handle an error, don't pass NULL to the following code, but rather an NSError pointer.
NSString *contents = [NSString stringWithContentsOfFile:#"/path/to/file" encoding:NSUTF8StringEncoding error:NULL];
That creates an autoreleased string containing the entire file. If all the file contains is an integer, you can just write this:
NSInteger integer = [contents integerValue];
If the file is split up into multiple lines (with each line containing one integer), you'll have to split it up:
NSArray *lines = [contents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *line in lines) {
NSInteger currentInteger = [line integerValue];
// Do something with the integer.
}
Overall, it's very simple.
Writing back to a file is just as easy. Once you've manipulated what you wanted back into a string, you can just use this:
NSString *newContents = ...; // New string.
[newContents writeToFile:#"/path/to/file" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
You can use that to write to a string. Of course, you can play with the settings. Setting atomically to YES causes it to write to a test file first, verify it, and then copy it over to replace the old file (this ensures that if some failure happens, you won't end up with a corrupt file). If you want, you can use a different encoding (though NSUTF8StringEncoding is highly recommended), and if you want to catch errors (which you should, essentially), you can pass in a reference to an NSError to the method. It would look something like this:
NSError *error = nil;
[newContents writeToFile:#"someFile.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (error) {
// Some error has occurred. Handle it.
}
For further reading, consult the NSString Class Reference.
If you have to write to multiple lines, use \r\n when building the newContents string to specify where line breaks are to be placed.
NSMutableString *newContents = [[NSMutableString alloc] init];
for (/* loop conditions here */)
{
NSString *lineString = //...do stuff to put important info for this line...
[newContents appendString:lineString];
[newContents appendString:#"\r\n"];
}
Related
I'm querying a web server which returns a JSON string as NSData. The string is in UTF-8 format so it is converted to an NSString like this.
NSString *receivedString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
However, some UTF-8 escapes remain in the outputted JSON string which causes my app to behave erratically. Things like \u2019 remain in the string. I've tried everything to remove them and replace them with their actual characters.
The only thing I can think of is to replace the occurances of UTF-8 escapes with their characters manually, but this is a lot of work if there's a quicker way!
Here's an example of an incorrectly parsed string:
{"title":"The Concept, Framed, The Enquiry, Delilah\u2019s Number 10 ","url":"http://livebrum.co.uk/2012/05/31/the-concept-framed-the-enquiry-delilah\u2019s-number-10","date_range":"31 May 2012","description":"","venue":{"title":"O2 Academy 3 ","url":"http://livebrum.co.uk/venues/o2-academy-3"}
As you can see, the URL hasn't been completely converted.
Thanks,
The \u2019 syntax isn't part of UTF-8 encoding, it's a piece of JSON-specific syntax. NSString parses UTF-8, not JSON, so doesn't understand it.
You should use NSJSONSerialization to parse the JSON then pull the string you want from the output of that.
So, for example:
NSError *error = nil;
id rootObject = [NSJSONSerialization
JSONObjectWithData:receivedData
options:0
error:&error];
if(error)
{
// error path here
}
// really you'd validate this properly, but this is just
// an example so I'm going to assume:
//
// (1) the root object is a dictionary;
// (2) it has a string in it named 'url'
//
// (technically this code will work not matter what the type
// of the url object as written, but if you carry forward assuming
// a string then you could be in trouble)
NSDictionary *rootDictionary = rootObject;
NSString *url = [rootDictionary objectForKey:#"url"];
NSLog(#"URL was: %#", url);
Is there a way to "auto detect" the encoding of a resource when loading it using stringFromContentsOfURL? The current (non-depracated) method, + (id)stringWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error;, wants a URL encoding. I've noticed that getting it wrong does make a difference for what I want to do. Is there a way to check this somehow and always get it right? (Right now I'm using UTF8.)
I'd try this function from the docs
Returns a string created by reading data from a given URL and returns by reference the encoding used to interpret the data.
+ (id)stringWithContentsOfURL:(NSURL *)url usedEncoding:(NSStringEncoding *)enc error:(NSError **)error
this seems to guess the encoding and then returns it to you
What I normally do when converting data (encoding-less string of bytes) to a string is attempt to initialize the string using various different encodings. I would suggest trying the most limiting (charset wise) encodings like ASCII and UTF-8 first, then attempt UTF-16. If none of those are a valid encoding, you should attempt to decode the string using a fallback encoding like NSWindowsCP1252StringEncoding that will almost always work. In order to do this you need to download the page's contents using NSData so that you don't have to re-download for every encoding attempt. Your code might look like this:
NSData * urlData = [NSData dataWithContentsOfURL:aURL];
NSString * theString = [[NSString alloc] initWithData:urlData encoding:NSASCIIStringEncoding];
if (!theString) {
theString = [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
}
if (!theString) {
theString = [[NSString alloc] initWithData:urlData encoding:NSUTF16StringEncoding];
}
if (!theString) {
theString = [[NSString alloc] initWithData:urlData NSWindowsCP1252StringEncoding];
}
// ...
// use theString here...
// ...
[theString release];
I have a .csv file in my bundle which I need to parse into an NSArray. The problem is when I init an NSString with contents of file (the file is located in my bundle), it returns nil. However, if I change the contents of the file, to anything else (random), it works. Is it possible there's some sort of string/character in the file that might be messing with the initialization?
It's just a simple csv file with 2 columns, a number, a comma, some text and "\n".
Thanks.
CSV => NSArray?
https://github.com/davedelong/CHCSVParser
*disclaimer: I wrote it.
Works for me:
NSStringEncoding usedEncoding = 0;
NSError *csvError = nil;
NSString *raw = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://pastebin.com/raw.php?i=RXPPwpvy"] usedEncoding:&usedEncoding error:&csvError];
NSLog(#"raw: %#", raw);
NSLog(#"%#", [NSArray arrayWithContentsOfCSVString:raw encoding:usedEncoding error:&csvError]);
I'm using a text file to save the changes made by a user on a list (the reason that I'm doing this is so that I can upload the text file to a PC later on, and from there insert it into an Excel spreadsheet). I have 3 data structures: A NSMutableArray of keys, and a NSMutableDictionary who's key values are MSMutableArrays of NSStrings.
I iterate through these data structures and compile a file string that looks much like this:
(Key);(value)\t(value)\t(value):\n(Key);(value).. .so on.
SO, onto the actual question: When I attempt to save it, it fails. I'm 99% sure this is because of the file path that I'm using, but I wanted backup to check this out. Code follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [paths objectAtIndex:0];
NSString *fileString = [NSString stringWithString:[self toFileString]];
if(![fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL]){
NSLog(#"File save failed");
} else {
// do stuff
}
(Code above is re-copied, since the actual code is on a different computer. It compiles, so ignore spelling errors?)
I tried using NSError, but I got bogged down in documentation and figured I might as well ask SO while trying to figure out how to properly use NSError (might be a little bit of an idiot, sorry).
99% sure it's the NSArray *paths line that's tripping it up, but I don't know how else to get the documents directory.
Edit: Problem solved, and one final question: If I save it to the App's document directory, where can I go after I close the app to see if it saved properly? If it works like I think it does, isn't it sandboxed in with the app's installation on the simulator? (i.e. no way of checking it)
NSLog() that filePath string. I think you're trying to write to the directory itself, not to a file.
Try this instead:
filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:#"myfile.txt"];
What is the file name you want to save? The method
NSArray *paths = NSSearchPathForDirectoriesInDomains(...);
NSString *filePath = [paths objectAtIndex:0];
...
if(![fileString writeToFile:filePath ...
means you are saving the string into a file path which has the same name as a folder. This will of course fail. Please give it a name, e.g.
NSString* fileName = [filePath stringByAppendingPathComponent:#"file.txt"];
if(![fileString writeToFile:fileName ...
and try again.
BTW, to use NSError:
NSError* theError = nil;
if(![fileString writeToFile:fileName ... error:&theError]) {
// ^^^^^^^^^
NSLog(#"Failed with reason %#", theError);
// theError is autoreleased.
}
As an example, I want to write out some string value, str, to a file, "yy", as it changes through each iteration of a loop. Below is how I'm currently implementing it:
NSOutputStream *oStream = [[NSOutputStream alloc] initToFileAtPath:#"yy" append:NO];
[oStream open];
while ( ... )
{
NSString *str = /* has already been set up */
NSData *strData = [str dataUsingEncoding:NSUTF8StringEncoding];
[oStream write:r(uint8_t *)[strData bytes] maxLength:[strData length]];
...
}
[oStream close];
Ignoring the UTF-8 encoding, which I do require, is this how NSStrings are typically written to a file? That is, by first converting to an NSData object, then using bytes and casting the result to uint8_t for NSOutputStream's write?
Are there alternatives that are used more often?
EDIT: Multiple NSStrings need to be appended to the same file, hence the loop above.
ctshryock's solution is a bit different than yours, since you're appending as you go, and ctshyrock's write a single file all at once (overwriting the previous version). For a long-running process, these are going to be radically different.
If you do want to write as you go, I typically use NSFileHandle here rather than NSOutputStream. It's just a little bit easier to use in my opinion.
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:aPath];
while ( ... )
{
NSString *str = /* has already been set up */
[fileHandle writeData:[str dataUsingEncoding:NSUTF8StringEncoding]];
}
[fileHandle closeFile];
Note that fileHandle will automatically close when it is dealloced, but I like to go ahead and close it explicitly when I'm done with it.
I typically use NSString's methods writeToFile:atomically:encoding:error: or writeToURL:atomically:encoding:error: for writing to file.
See the String Programming Guide for Cocoa for more info