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

Huynh httkt
  • 19
  • 1
  • 4

2 Answers2

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 ;)

Micha Mazaheri
  • 3,481
  • 1
  • 21
  • 26
  • Thanks much but if it has '\0'at terminal. how to get it? – Huynh httkt Aug 26 '13 at 04:13
  • 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. – Micha Mazaheri Aug 26 '13 at 04:21
  • 1
    @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](http://pastie.org/8269971). –  Aug 26 '13 at 04:23
  • @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?). – Micha Mazaheri Aug 26 '13 at 04:29
  • @MichaMazaheri Have you even clicked the link in my comment? –  Aug 26 '13 at 04:30
  • @MichaMazaheri: i tried your code but data of myData is not same original file? – Huynh httkt Aug 26 '13 at 04:31
  • @H2CO3 I'm sorry somehow I didn't see it... Using `fseek` and `ftell` is smart :) Thanks! Though instead of `malloc` the data, I would rather use `-initWithLength:` and then use `mutableBytes` as the `fread` buffer (or maybe you don't want to have a mutable data at all...). – Micha Mazaheri Aug 26 '13 at 04:41
  • @MichaMazaheri Yeah, I used `malloc()` only because OP didn't mention he needed a mutable data; but otherwise using that is just fine. –  Aug 26 '13 at 04:43
  • @Huynhhttkt which code have you tried? Please test with second one it's probably more reliable. Of if you don't want a look at all, use **H2CO3**'s code. – Micha Mazaheri Aug 26 '13 at 04:43
  • @MichaMazaheri: i updated my code in my question. Please check it . thank so much – Huynh httkt Aug 26 '13 at 04:46
  • 1
    @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. – Micha Mazaheri Aug 26 '13 at 04:55
  • Then, make sure your file descriptor is pointing on the right file, that file has the right content, you don't have any others calls on that file descriptor that could mess up with this one... it works fine for me when I just copy/paste it (and change just the `c_sd_url` part). – Micha Mazaheri Aug 26 '13 at 05:04
  • content of file is right. I tried open file and see data of it correct but run above code, it only show myData: 0. I don't know why so – Huynh httkt Aug 26 '13 at 05:06
  • if i use your code 1, it can show correct data of file but it can not read '\0'. – Huynh httkt Aug 26 '13 at 05:10
  • It's really weird. Please try this check: http://pastie.org/8270064 it passes for me with a 48 MB random video. – Micha Mazaheri Aug 26 '13 at 05:43
  • @MichaMazaheri: Now i have another problem, can you help me one more time? I want to read file from disk(SD Card) not from Cache. – Huynh httkt Aug 26 '13 at 12:31
  • @H2CO3: Thanks your code, it very helpful. Can you show me how to read file from disk not from cache ? Thanks much – Huynh httkt Aug 26 '13 at 12:32
  • @Huynhhttkt What is this "cache" you keep talking about? This code reads from files on the iDevice's filesystem. If you want to access an external SD card, then you're out of luck (basically). –  Aug 26 '13 at 12:49
  • @H2CO3: Yes, i want to read file from external SD Card,I tried above code, it can not read from SD Card. – Huynh httkt Aug 26 '13 at 12:50
  • @Huynhhttkt No, it doesn't. iOS devices don't even support SD cards. If yours does, then either it's a Chinese pseudo-iDevice, or it has some sort of a custom SD card reader device, for which you need to google a manual. –  Aug 26 '13 at 12:53
  • @H2CO3: I don't implement for iOS, i implement for MAC OS. Does not have another way? – Huynh httkt Aug 26 '13 at 13:01
  • @Huynhhttkt If it's for OS X, then you just let the OS mount the disk/card/whatever, then read from `/Volumes//path/to/file`... –  Aug 26 '13 at 13:02
  • @H2CO3: Sorry for i'm a newbie. Can you tell me more detail or show example code? Thanks so much – Huynh httkt Aug 26 '13 at 13:07
  • @Huynhhttkt What other kind of example code do you need? Sorry, but are you **really** unable to substitute `/Volumes/MYSDCARD/foo.txt` in place of `filename.ext` in the pastie? –  Aug 26 '13 at 13:08
  • I only want to read file (example : test.trk) directly from SD Card, because my SD Card auto update data, so that i need my MAC app read data from SD Card. – Huynh httkt Aug 26 '13 at 13:15
  • Ok. Go check my pastie again: http://pastie.org/8270064. And at the first line, replace the URL I wrote by whatever is the path to you SD card. SD card, hard drive, USB flash drive, SSD or whatever is your disk it's absolutely same. All is about files and directories, the OS/file system does the lower level stuff. To find path to your SD card, go to Terminal and type: `cd /Volumes/; ls -l;` and you'll see list of all the disks that are mounted on your system. Then do `cd NAME_OF_YOUR_DISK` and then `pwd` to get the system path to that disk. And replace the URL line 1 by this path + filename. – Micha Mazaheri Aug 27 '13 at 02:58
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];
Community
  • 1
  • 1
eonil
  • 83,476
  • 81
  • 317
  • 516
  • Thanks but i use NSData* data0 = [[NSData alloc] initWithBytes:buffer length:300 * sizeof(char)];, it returns data not same original file? why? – Huynh httkt Aug 26 '13 at 04:20
  • How are they different? – eonil Aug 26 '13 at 04:23
  • 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. – eonil Aug 26 '13 at 04:26
  • 2
    "`char` in C is guaranteed to be 8-bit at least by standard." - no, it isn't. –  Aug 26 '13 at 04:31