Resolved Binary file read issue

Discussion in 'iOS Programming' started by Angry Bugs, Sep 1, 2012.

  1. Angry Bugs, Sep 1, 2012
    Last edited: Sep 1, 2012

    macrumors newbie

    Joined:
    Sep 1, 2012
    #1
    Hi!,

    I use the following code to open a binary file:

    Code:
    const char* fileName = "myFile.bin";	
    
    FILE* file = NULL;
    
    
    const char* buffer;
    
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSString *path = [NSString stringWithUTF8String:fileName];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    path = [documentsDirectory stringByAppendingPathComponent:path];
    	
    if (path != nil)
        buffer = [path UTF8String];
    
    
    [pool release];
    
    file = fopen(buffer, "rb");
    	
    if (!file)
        return -1;
    
    //Do fread stuff here
    
    fclose(file);
    
    return 0;
    
    

    But sometimes, not very often, the functions returns -1.
    Is there any problem in this code, or a reason why only sometimes this function doesn't find the file?, although the file is present.

    Any help would be appreciated.

    Thanks.
     
  2. macrumors 603

    Joined:
    Aug 9, 2009
    #2
    1. Go to the NSString class reference doc. Find the description of the UTF8String method. Read what it says about "the autorelease context in which the C string is created."


    2. When referring to file paths, you should always use the file-system representation. There are two NSString methods related to this. I recommend the one that copies the data into your provided buffer.
     
  3. Angry Bugs, Sep 1, 2012
    Last edited: Sep 1, 2012

    thread starter macrumors newbie

    Joined:
    Sep 1, 2012
    #3
    Thanks a lot.

    I think replacing this :

    buffer = [path UTF8String];

    with this:

    sprintf(buffer, "%s", [path UTF8String]);

    will do the job.
     
  4. macrumors 603

    Joined:
    Aug 9, 2009
    #4
    If that's the only change you make, then you haven't fixed anything. You've just traded one bug for another.

    The posted sprintf() is just a strange way of performing an strcpy(). I advise you to look up strcpy() to see what it does. I also advise looking at strncpy() to see how it differs, and compare to snprintf(). Consider what might happen if you copy a string that's longer than the buffer memory reserved for it.

    At the point you propose placing that code, what is buffer pointing to? Is it a valid allocated block of memory? Of what length? I suggest using the debugger to step through and see what buffer holds at that point. If buffer isn't pointing at a valid block of memory, what do you suppose will happen if you copy a string into that memory?


    And you're still making the mistake of using UTF8String instead of one of the file-system representation methods. See this post on Apple's cocoa-dev list:
    http://lists.apple.com/archives/cocoa-dev/2012/Aug/msg00821.html
     
  5. macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #5
    Use [path fileSystemRepresentation]

    Is it possible that your file names have non-ascii characters in them?

    Get that [pool release] out of there. You are making your path go away!
     
  6. thread starter macrumors newbie

    Joined:
    Sep 1, 2012
    #6
    Thanks for taking the time to reply.

    I read the link you post, and the changed the code.
    I'm not sure that the length of the buffer is enough.

    Code:
    const char* fileName = "myFile.bin";	
    
    FILE* file = NULL;
    
    
    char buffer[256];
    
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSString *path = [NSString stringWithUTF8String:fileName];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    path = [documentsDirectory stringByAppendingPathComponent:path];
    
    BOOL success;
    	
    if (path != nil)
        success = [path getFileSystemRepresentation:buffer maxLength:256]
    
    if (!success)
        return -1;
    
    [pool release];
    
    file = fopen(buffer, "rb");
    	
    if (!file)
        return -1;
    
    //Do fread stuff here
    
    fclose(file);
    
    return 0;
    
     
  7. thread starter macrumors newbie

    Joined:
    Sep 1, 2012
    #7
    All the characters are ascii, i think the other way could be to use fileSystemRepresentation and move [pool release] to the end of the function, right?
     
  8. macrumors 603

    Joined:
    Aug 9, 2009
    #8
    Why are you so concerned about memory usage that you feel the need to bracket a few strings with their own auto-release pool?


    If you're looking for waste, consider the red-hilited code:
    Code:
    [COLOR="Red"]const char* fileName = "myFile.bin";	[/COLOR]
    
    FILE* file = NULL;
    
    char buffer[256];
    
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [COLOR="red"]NSString *path = [NSString stringWithUTF8String:fileName];[/COLOR]
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    [COLOR="red"]path = [documentsDirectory stringByAppendingPathComponent:path];[/COLOR]
    
    You're starting with a constant C string, converting that to an immutable NSString, then appending that to another NSString. Why all the conversions? Do you realize that Objective-C supports NSString literals? For example, all the red can be replaced by one green line:
    Code:
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    [COLOR="SeaGreen"]NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myFile.bin"];[/COLOR]
    
    Notice that I also removed the extra auto-release pool, so now you can just use fileSystemRepresentation without having to copy it or worry that your local buffer isn't long enough. All the objects that were made will be auto-released when the existing pool is drained. No micro-managing of memory is necessary.


    As another possible bug, consider what happens if path is nil in your code. If you haven't done a static analysis, I think you'll find a reference to an uninitialized buffer being used by fopen(). You may even find that the compiler is giving you a warning about this.
     
  9. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #9
    If we're talking waste, how bout plain ol' :

    Code:
    FILE * file;
    NSString * path = [[NSBundle mainBundle] pathForResource: @"myFile" ofType: @"bin"];
    if(path)
    {
          file = fopen([path fileSystemRepresentation], "rb");
    
          if(file)
          {
                /* Do Stuff */
                fclose(file);
          }
    }
    
    No need for 50 URLs/paths/strings here, strictly speaking if we're discussing iOS programming. I don't think you have access outside your app bundle anyhow.
     
  10. thread starter macrumors newbie

    Joined:
    Sep 1, 2012
    #10
    You're right.

    Again, thanks a lot for your reply, it really helps me a lot.
     

Share This Page