Сохранить пустой файл в Objective-C - PullRequest
2 голосов
/ 16 января 2012

Я работаю над кодом, который, помимо прочего, должен сохранять заполненный нулями файл. Можно ожидать создания файла размером от 10 МБ до 1 ГБ пустым.

Должно быть похоже на эту команду Unix:

dd if=/dev/zero of=my_image.dsk count=20480

Я мог бы сделать эту работу с небольшими размерами:

int totalSize = 1024*1024;
char buf[totalSize];
strcpy(buf,"");

NSData *theData = [NSData dataWithBytes:buf length:sizeof(buf)];

//NSLog(@"%@", theData);
NSLog(@"%lu", sizeof(buf));

[theData writeToFile:@"/Users/foo/Desktop/my_image.dsk" atomically:YES];

Но если я попробую большее значение (например, 1024 *1024* 10), произойдет сбой. Итак, я попробовал:

NSFileManager *ddd = [[NSFileManager alloc ] init];
[ddd createFileAtPath:@"/Users/foo/Desktop/my_image.dsk" contents:[NSData data] attributes:nil];

Создает пустой файл, но это плохо, потому что размер файла равен нулю. Это не должно быть ноль.

Я часами пытался найти ответ безуспешно. Я хочу сделать это в Obj-C, но C также вариант, прежде чем я сойду с ума.

Пожалуйста, кто-нибудь, дайте мне немного света!

Заранее спасибо!

- Правка -

Спасибо всем, но еще одно: можно ли писать, не выделяя все в память?

Ответы [ 4 ]

4 голосов
/ 16 января 2012

В POSIX API это относительно просто:

int f = open("filename", O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
if (f < 0) {
    perror("open filename");
    exit(1);
}

char empty = 0;

pwrite(f, &empty, sizeof(char), <offset into the file>);

Смещение указывается с использованием целого числа типа off_t, которое может быть недостаточно большим для ваших конкретных потребностей. Обратитесь к документации по системному API, чтобы обнаружить 64-битные файловые интерфейсы, если они недостаточно велики.

Лучшая часть этого подхода заключается в том, что он занимает всего несколько байтов памяти программы - вы не выделяете 10 гигабайт ничего для записи вашего файла.

Еще один более простой подход - использовать системный вызов truncate(2). Не все платформы поддерживают расширение файла с помощью truncate(2), но для тех, кто это делает, это один вызов .

3 голосов
/ 16 января 2012

Вы можете использовать dd. Это позволит вам написать, например, 10 ГБ, не беспокоясь о памяти или адресном пространстве.

NSTask *task = [NSTask new];
long size = ...; // Note! dd multiplies this by 512 by default
NSString *path = ...;
[task setLaunchPath:@"/bin/dd"];
[task setArguments:[NSArray arrayWithObjects:@"dd", @"if=/dev/zero",
    [NSString stringWithFormat:@"of=%s", [path fileSystemRepresentation]],
    [NSString stringWithFormat:@"count=%ld", size],
    nil]];
[task launch];
[task waitUntilExit];
if ([task terminationStatus] != 0) {
    // an error occurred...
}
[task release];

Преимущество состоит в том, что искушенные пользователи могут убить dd, если что-то пойдет не так.

О песочнице: Я подозреваю, что это будет хорошо работать даже в песочнице, но мне было бы интересно узнать наверняка. Согласно документации ( ссылка ), подпроцесс "просто наследует песочницу процесса, который его создал". Именно так вы и ожидаете, что он будет работать в Unix.

Вы можете быть уверены, что dd останется, поскольку Apple утверждает, что OS X соответствует SUSv3.

1 голос
/ 16 января 2012

Спасибо всем. Я пришел к решению. Это в основном набросок кода, который я собираюсь добавить в приложение какао.

Работает нормально, и я думаю, что мне нужны только некоторые меры предосторожности с переменной размера, и этого будет достаточно.

long size = 2000*500;

NSString *path = [[NSString alloc] initWithFormat: @"/Users/foo/Desktop/testeFile.dsk"];

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/dd"];
[task setArguments:[NSArray arrayWithObjects:
                    @"if=/dev/zero",
                    [NSString stringWithFormat:@"of=%s", [path fileSystemRepresentation]],
                    [NSString stringWithFormat:@"count=%ld", size],
                    nil]];
NSLog(@"%@",[task arguments]);
[task launch];

//[task waitUntilExit]; //The app would stuck here.. better use the loop

int cont = 0;
while ([task isRunning]) {
    cont++;
    if(cont%100==0) NSLog(@". %d", cont);
}

if ([task terminationStatus] != 0) {
    NSLog(@"an error occurred...");
}
[task release];
[path release];

NSLog(@"Done!");
1 голос
/ 16 января 2012

Попробуйте этот код:

int totalSize = 1024*1024;
// don't allocate on the stack
char *buf = calloc(totalSize, sizeof(char));

NSData *theData = [NSData dataWithBytesNoCopy:buf length:totalSize];

//NSLog(@"%@", theData);
NSLog(@"%lu", totalSize);

[theData writeToFile:@"/Users/foo/Desktop/my_image.dsk" atomically:YES];

Вы также можете попробовать это, не занимая памяти, и это работает с файлом размером 1 ГБ.

void writeBlankBytes(FILE *file, size_t numKB)
{
    static char *oneKBZero = NULL;

    if (oneKBZero == NULL)
    {
        oneKBZero = calloc(1024, sizeof(char));
    }

    for (int i = 0; i < numKB; i++) {
        fwrite(oneKBZero, 1, 1024, file);
    }
}
...