2

我正在编写一个代码,除其他外,它必须保存一个零填充文件。它可以期望创建一个从 10MB 到甚至 1GB 空白的文件。

它必须类似于以下 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 回答 4

4

这在 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,它可能不足以满足您的特定需求。如果 64 位文件接口不够大,请检查您的系统 API 文档以发现它。

这种方法最好的部分是它只需要几个字节的程序内存——你没有分配 10 GB 的空间来写入你的文件。

另一种更简单的方法是使用truncate(2)系统调用。并非所有平台都支持使用 扩展文件truncate(2),但对于那些支持的平台,这是一个调用

于 2012-01-16T01:00:59.573 回答
3

您可以使用dd. 这将使您可以写入,例如 10 GB,而不必担心内存或地址空间。

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。

于 2012-01-16T01:04:00.287 回答
1

试试这个代码:

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 GB 的文件。

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);
    }
}
于 2012-01-16T00:55:26.880 回答
1

谢谢大家。我找到了一个解决方案。这基本上是我将要放入可可应用程序的代码草图。

工作正常,我认为我只需要对 size 变量采取一些预防措施就足够了。

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!");
于 2012-01-16T19:46:20.247 回答