0

I read file from fcntl(fd, F_GLOBAL_NOCACHE, 1); to read file from Disk not from Cache. After reading file, i can only get string data. Now i want to after reading file, i get byte data of file to NSMutableData. How can i do that? Thanks in advance.

   if (fd)

{
    fcntl(fd, F_GLOBAL_NOCACHE, 1);
    NSMutableData* myData = [[NSMutableData alloc] init];

    while(YES)
    {

        // store the length before addition
        NSUInteger previousLength = [myData length];

        // increase the length so we can write more bytes
        [myData increaseLengthBy:300];

        // read bytes from stream directly at the end of our data
        size_t len = fread([myData mutableBytes] + previousLength, 300, 1, fd);

        // set data length
        [myData setLength:previousLength + len];

        // if end-of-file exit the loop
        if (len == 0) {
            break;
        }
         [myData appendBytes:buffer length:len];
    }
    // use your 'myData'
    NSLog(@"dataFile: %@",myData);
    [myData release];

Please give me suggestions? thanks

UPDATE2: Now i have another problem: i want to read file direct from disk not from Cache. I used below code but it seem not work, it still read from Cache :

     NSString *url= [NSString stringWithFormat:@"%@/demo.abc"];

        const char *c_sd_url = [url UTF8String];
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        FILE * fd = fopen(c_sd_url, "rb");

        if (fd)
        {

            fcntl(fd, F_GLOBAL_NOCACHE, 1);

            fseek(fd, 0, SEEK_END);
            long sz = ftell(fd);
            fseek(fd, 0, SEEK_SET);

            char *buf = malloc(sz);
            NSLog(@"before %s",buf);
            assert(buf != NULL);

            assert(fread(buf, sz, 1, fd) == 1);
            NSLog(@"after %s",buf);
            NSMutableData *data= [NSMutableData dataWithBytesNoCopy:buf length:sz freeWhenDone:YES];
            NSLog(@"%@",data);
}

I used fcntl(fd, F_GLOBAL_NOCACHE, 1); after fopen(). Please give me any suggestion. Thanks much

3
  • 4
    Simplest is to use NSData dataWithContentsOfFile. Commented Aug 26, 2013 at 3:50
  • If use it , it can read file from Disk not from Cache ? Commented Aug 26, 2013 at 3:53
  • @Huynhhttkt You don't appear to have read the documentation of that method, which is bad. Commented Aug 26, 2013 at 4:03

2 Answers 2

1

char in C is guaranteed to be 1 byte at least by standard.

What is an unsigned char?

So you can treat char* as byte-array with proper size multiplication, and you can pass it to -[NSData initWithBytes:length:] method.

char buffer[300];
NSData* data0 = [[NSData alloc] initWithBytes:buffer length:300 * sizeof(char)];

There're several initializer methods, so check them out for your needs. See NSMutableData for procedural style use.

Or you can use NSData method as like @HotLicks said.

NSData* data0 = [NSData dataWithContentsOfFile:@"somefile" options:NSDataReadingUncached error:NULL];
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks but i use NSData* data0 = [[NSData alloc] initWithBytes:buffer length:300 * sizeof(char)];, it returns data not same original file? why?
How are they different?
I think it's some other error on other functions such as fcntl or fgets. The -initWithBytes~ method only handles byte-array already loaded in-memory, and not related to loading process itself at all.
"char in C is guaranteed to be 8-bit at least by standard." - no, it isn't.
1

I guess Hot Licks is right, you probably want to simply use -[NSData dataWithContentsOfFile:], but in the case you want to use C level APIs, you can do:

#define MY_BUFFER_SIZE 1024

FILE * fd = fopen(c_sd_url, "rb");

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

char buffer[MY_BUFFER_SIZE];

if (fd)

{
    fcntl(fd, F_GLOBAL_NOCACHE, 1);

    // if you can predict the capacity it's better for performance
    NSMutableData* myData = [NSMutableData dataWithCapacity:1024];

    while(fgets(buffer, MY_BUFFER_SIZE, fd) != NULL)
    {
        [myData appendBytes:buffer length:strlen(buffer)];
    }
}

// use your 'myData'

[pool release];

Updated: to avoid useless copy of buffer data, and following H2CO3's comment:

It's better to avoid to write data to a buffer and then copy it to the NSMutableData, we can use -[NSData mutableBytes] to access directly the underlying C structure. Also, H2CO3 is completely right, using fread is much better since it gives us the length of the bytes read.

#define MY_BUFFER_SIZE 1024

FILE * fd = fopen(c_sd_url, "rb");

if (fd)
{
    fcntl(fd, F_GLOBAL_NOCACHE, 1);

    // if you can predict the capacity it's better for performance
    NSMutableData* myData = [[NSMutableData alloc] init];

    while(YES)
    {
        // store the length before addition
        NSUInteger previousLength = [myData length];

        // increase the length so we can write more bytes
        [myData increaseLengthBy:MY_BUFFER_SIZE];

        // read bytes from stream directly at the end of our data
        size_t len = fread([myData mutableBytes] + previousLength, 1, MY_BUFFER_SIZE, fd);

        // set data length
        [myData setLength:previousLength + len];

        // if end-of-file exit the loop
        if (len == 0) {
            break;
        }
    }

    // use your 'myData'
    NSLog(@"myData: %@", [[NSString alloc] initWithData:myData encoding:NSASCIIStringEncoding]);

    [myData release];
}

If you want to have a \0 terminated NSData, just add at the end:

[myData appendBytes:"\0" length:1];

Good luck ;)

26 Comments

Thanks much but if it has '\0'at terminal. how to get it?
I just updated my answer, it will fit more your needs + it's more optimized. Anyways if you want to get a \0 terminated data, simply add [myData appendBytes:"\0" length:1] at the end.
@MichaMazaheri Welp. The previous version was better. What I was trying to say is that you do not need a loop. You read into a large enough buffer once, then you initialize the data object with that. Something like this.
@H2CO3 ohh okay! I didn't noticed the name of your variable file_size. I guess all depends on this file_size... but generally speaking it's a bad practice since you may not know in advance the file size (or you may check it before?).
@Huynhhttkt Just tested my second code, it works fine for me. I think your problem is that you're passing simply a NSData to NSLog which makes it be printed with hexadecimal representation. If you want to show as a "normal" string, you need to convert it to NSString before. Just updated the last code for you.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.