2012-01-16 12 views
2

私は、とりわけゼロで埋められたファイルを保存する必要があるコードを作成しています。 10MBから1GBの空白のファイルを作成することも期待できます。空白のファイルをObjective-Cに保存します

それは、このUnixコマンドのようになります。私は大きな値(* 1024 1024 * 10をしようとした場合

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

しかし:私は小さなサイズで、この1つの作業を行うことができ

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

たとえば、)、クラッシュします。 は、だから私は試してみました:

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

それは空のファイルを作成しますが、ファイルサイズがゼロであるので、良いではありません。ゼロであってはなりません。

私は何時間も成功を収めて答えを見つけようと過ごしました。私はObj-Cでこれをやりたいと思っていますが、Cがオプションになります。

お願いします。

ありがとうございます!

- 編集 -

みんなありがとう、しかし、もう一つは:それはメモリ上のすべてを割り当てることなく書くことは可能でしょうか?

+0

スタックに割り当てるためクラッシュします。あなたはスタック上に限られたメモリしか持っていません。スタックに1 MBをそのまま割り当てようとしていますが、mallocとmemsetの呼び出しを使う方が良いです。 –

+0

@ RichardJ.RossIII:一般的には 'malloc'と' memset'は使わず、代わりに 'calloc'を使うべきです。あなたはそのように多くのメモリを節約できます。 –

+0

@DietrichEpp本当ですか?私は他の方法を聞いたことがあり、memsetはcallocコールよりも優れています。私が聞いただけで、どちらかの方法を教えてもらえません。 –

答えて

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を殺すことができるということです。

サンドボックスについて:サンドボックスでもうまくいくことが疑問ですが、私は確かに知りたいのです。ドキュメント(link)によれば、サブプロセスは「作成したプロセスのサンドボックスを単に継承します」。これは、あなたがUnix上で動作することを期待する方法とまったく同じです。

AppleがOS XがSUSv3に準拠していると主張しているので、ddが固執することは確実です。

+0

興味深い..あなたはデフォルトでエラーで "殺す"アクションを実装するのはあまりにも多くの仕事になると思いますか? – Apollo

+0

エラーが発生しました: 'Program received signal:" SIGABRT "とログ:' 2012-01-16 01:56:05.977 File handler [2448:903] ***キャッチされていない例外 'NSInvalidArgumentException'理由: '起動パスがアクセスできない' ***最初の呼び出し時のコールスタック: – Apollo

+0

@GiancarloMariot:書かれているように、 'dd'が終了するとエラーが検出されるため、' dd'を終了する必要はありません。 'dd'プログラムは通常、エラーが発生すると直ちに終了します。 –

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ギガバイトのファイルで動作可能性があります。

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); 
    } 
} 
+0

'totalSize'が10GBに近づくと、このアプローチはどうなりますか? – aroth

+0

それでは、10GB以上のRAMを持っている根深い理由がない限り、あなたはうんざりしています。 –

+0

@ RichardJ.RossIII: 'malloc' /' memset'の代わりに 'calloc'を使用した場合(常に*すべき*)、RAMがあまりない場合でも64ビットコンピュータでうまくいくかもしれません。 –

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ギガバイトを割り当てないことです。

さらに簡単なアプローチは、truncate(2)システムコールを使用することです。すべてのプラットフォームがtruncate(2)でファイルの拡張をサポートしているわけではありませんが、実行するプラットフォームではの1つの呼び出しです。

+0

時には古い方法が最も効果的です。 – aroth

+1

すべてのアプリケーションがスパースファイルを作成しないことに注意してください。権限を0666に変更し、ユーザに自分自身の 'umask'を設定させたいかもしれません。 –

+0

偉大な、私はメモリ上のすべてを割り当てていない何かを探していたので、これは良いスタートです。しかし、あなたはこのオフセットで私を失ってしまった。xD – Apollo

1

ありがとうございます。私は解決策に出た。これは基本的に私がココアアプリに入れようとしているコードのスケッチです。

うまく動作し、私はサイズの変数でいくつかの予防措置が必要と思うだけで十分です。

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!"); 
+0

これは、 'dd'が利用可能で、特に'/bin/dd'に置かれていることに注意してください。どちらかの要件が満たされなければ、プログラムは失敗します。これはおそらく何年もあなたのために働くでしょうが、私はこれらの依存関係を持たないより簡単な方法が利用可能であると思います。 – sarnold

+0

@sarnold私はあなたが言うように良い方法を見つけることができればよいと思いますが、保存する前にすべてのファイルをメモリにスローします。ユーザーが1GBファイルを要求すると、おそらくアプリケーションがクラッシュしたり、少なくともゾウになります。 私はシンプルなソリューションをどこからでも探していましたが、私が見つけたソリューションはすべてここにあります。=( 新しい提案があればそれは素晴らしいでしょう!=) – Apollo

+0

[Dietrich's答え](http://stackoverflow.com/a/8874811/377270)は良いです。新しいプロセスを開始することに対する 'NPROC'' setrlimit(2)'の制約に遭う可能性はかなり低く、提供したサンドボックスガイドへのリンクは、あなたが_user_ではなくアプリケーションの作成者として責任を負うことを示していますサンドボックスを提供するために、あなたのアプリケーションにとって本当の障害になる可能性は低いです。私の提案は、(あなたが望むものではないかもしれない)スパースファイルを作成し、別のAPIレベルで作業する必要があります。 – sarnold

関連する問題