David Nedrow David Nedrow - 3 months ago 7x
Objective-C Question

Parsing file with percent signs (%) in Objective-C

I'm writing a parser for fortune files. Fortune is a small app on *nix platforms that just prints out a random "fortune". The fortune files are straight text, with each fortune being separated by a percent sign on its own line. For example:

A little suffering is good for the soul.
-- Kirk, "The Corbomite Maneuver", stardate 1514.0
A man either lives life as it happens to him, meets it head-on and
licks it, or he turns his back on it and starts to wither away.
-- Dr. Boyce, "The Menagerie" ("The Cage"), star date unknown

What I've found is that when parsing the file, stringWithContentsOfFile returns a string with the % signs in place. For example:

@"A little suffering is good for the soul.\n\t\t-- Kirk, \"The Corbomite Maneuver\", stardate 1514.0\n%\nA man either lives life as it happens to him, meets it head-on and\nlicks it, or he turns his back on it and starts to wither away.\n\t\t-- Dr. Boyce, \"The Menagerie\" (\"The Cage\"), stardate unknown\n%"

However, when I call componentsSeparatedByCharactersInSet on the file contents, everything is parsed as a string, with the exception of the percent signs, which are NSTaggedPointerString. When I print out the lines, the percent signs are gone.

Is this because the percent sign is a format specifier for strings? I would think in that case that the initial content pull would escape those.

Here's the code:

NSFileManager *fileManager;
fileManager = [NSFileManager defaultManager];
NSStringEncoding stringEncoding;
// NSString *fileContents = [NSString stringWithContentsOfFile:fileName encoding:NSASCIIStringEncoding error:nil];
NSString *fileContents = [NSString stringWithContentsOfFile:fileName usedEncoding:&stringEncoding error:nil];
NSArray *fileLines = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];

The used encoding ends up being UTF-8. You can see I have also tried specifying plain ASCII, but it yields the same results.

So the question is, how do I retain the percent signs? Or, may I should use it as the separator character and then parse each of the subsequent results individually.


You are calling NSLog() but passing the line strings as the format string. Something like:


Therefore, any percent characters in the line strings are interpreted as format specifiers. You should (almost) never pass strings that come from outside sources — i.e. strings which are not hard-coded in your code — as format strings to any function (NSLog(), printf(), +[NSString stringWithFormat:], etc.). It's not safe and you'll sometimes get unexpected results like you've seen.

You should always log a single string like this:

NSLog(@"%@", lineString);

That is, you need to pass a hard-coded format string and use the foreign string as data for that to format.