I'm trying to save and load a custom file format in an iOS app. The format stores various data, but among it is the name a user entered for a map. I found that at random, as I'm trying to read this map name (stored as char
s in the file), my NSData object will read garbage. Here is my code for reading the map name (start
is already set to the correct start location):
NSData* data = ....;
uint mapNameLength;
char* mapNameChar;
NSString* mapNameString;
[data getBytes:&mapNameLength range:NSMakeRange(start, 4)];
start += 4;
mapNameChar = (char *)malloc(sizeof(char) * mapNameLength);
[data getBytes:mapNameChar range:NSMakeRange(start, mapNameLength)];
mapNameString = [NSString stringWithUTF8String:mapNameChar];
NSLog(@"mapNameLength: %u, mapNameChar: %s, Map name string: %@", mapNameLength, mapNameChar, mapNameString);
As you can see, I am reading the length of the name, then reading that many char
values, then converting it into an NSString. Here is the output of the NSLog when it works (I just hit the keyboard a bunch to make a long name):
mapNameLength: 49, mapNameChar: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdv, Map name string: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdv
Here is the output of the NSLog when it doesn't work:
mapNameLength: 49, mapNameChar: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdvfi?p?, Map name string: (null)
Those "?" are actually special characters that don't display here, so I added them. The first is "ETX" and the second is "SOH" in Sublime Text.
To create the file, this is what I am doing:
NSString* mapName = ....;
uint mapNameLength = (uint)mapName.length;
NSMutableData* data = ....;
//...
//Write file type and version here
//...
[data appendBytes:&mapNameLength length:4];
[data appendData:[mapName dataUsingEncoding:NSUTF8StringEncoding]];
//...
//Write other stuff
//...
NSString* path = [FileManager applicationDocumentsDirectory];
path = [path stringByAppendingFormat:@"/%@", fileName];
BOOL success = [data writeToFile:path options:NSDataWritingAtomic error:&error];
I've only written the file once, so I know the data is always the same. Why then, would my data object sometimes get random bytes from it?
I am not certain, however I don't think you are storing a terminating NUL
character, so you need to add one to the buffer once you've read the string in:
mapNameChar = (char *)malloc(sizeof(char) * (mapNameLength + 1)); // Add +1
[data getBytes:mapNameChar range:NSMakeRange(start, mapNameLength)];
mapNameChar[mapNameLength] = '\0'; // Terminate with NUL
mapNameString = [NSString stringWithUTF8String:mapNameChar];
Better still forget about malloc()
and the C-String and create the NSString
directly from the NSData
object:
mapNameString = [[NSString alloc] initWithBytes:(const char *)([data bytes]) + start
length:mapNameLength
encoding:NSUTF8StringEncoding];