3

ドキュメントフォルダ内のファイルのヘッダー情報を変更する必要があります。バイナリデータを読み書きするための最も推奨される方法は何ですか?

  1. ドキュメントフォルダバイナリから配列/ストリームにデータを読み取る方法
  2. 配列/ストリームからローカルのiPadドキュメントフォルダにデータを書き戻す方法は?

Objective-cでのバイナリ読み取り/書き込み

4

3 に答える 3

3

これは、もう少し具体的にしなければ答えるのは難しいですが、これを行う最も効率的な方法は、おそらくファイルをまったく変更しないことです。

iOSについて話しているので、アプリケーション自体を除いて、これらのドキュメントへのファイルシステムレベルのアクセスはありません。では、ファイルに関連付けたい追加/カスタマイズされたヘッダーデータをアプリケーション全体のメタデータストア(iTunesやiPhotoなど)に保存し、エクスポート時にのみ実際のファイルヘッダーとインターリーブしてみませんか?

それにもかかわらず、これらのデータを変更するために経営幹部レベルのファイル機能にドロップダウンする説得力のある理由は実際にはわかりません。NSInputStreamストリーミングファイルの読み取りアクセスを提供し、データNSOutputStreamをファイルにストリーミングするために使用できます。

上からの私の提案に従えば、おそらく次のようなAPIになってしまうでしょう。

typedef void (^DataExportHandler)(NSData *resultData, NSError *exportError);

@interface DataStore (FileExport)

/** If you wanted to abort the export, you could pass the stream into the `abort…:`-method
@param  identifier  Something that you use internally to manage your stored files.
@param  error       For good measure…
@return The export stream for the object or `nil` if an error occurred.
*/
- (NSInputStream *)exportStreamForObjectWithIdentifier:(id)identifier error:(NSError * __autoreleasing*)error;

/** If your data are mostly small, it may be more convenient to not consume the exports as streams but as BLOBs, if the sizes vary you could implement this as a convenience…
@param  identifier  Equivalent to the identifier in the method above
@param  handler     Callback that is invoked once some time later when the export finished or failed. **Must not** be `nil`.
*/
@return A cancellation token.
- (id)asynchronouslyExportDataForObjectWithIdentifier:(id)identifier resultHandler:(DataExportHandler)handler;

/**
@param  exportToken  Either a stream from the first method or a token returned from the second one.
*/
- (void)abortAsynchronousExportWithToken:(id)exportToken;

@end

ARCを想定し、追加のメタデータを元のメタデータとインターリーブするために何をしなければならないかわからない場合、実装の定型部分は次のようになります

牛肉は、ここでは明らかに示していない部分にrawDataStream含まれます。元のファイルのデータを使用する場所のデリゲートの実装であり、ヘッダーと追加情報をインターリーブします。これはおそらく別のクラスに分解する必要がありますが、データストアがNSStreamDelegateそれに応じてコールバックを実装することを暗示しています。

ヘッダーの後、ファイルの残りの部分を通過するだけです…</ p>

/// Scribble of another helper class that can be used whenever one needs to consume a stream for its aggregate data:
@interface _StreamConsumer : NSObject <NSStreamDelegate> {
    NSInputStream *_stream;
    DataExportHandler _handler;
    NSMutableData *_data;
}

// initiate the data, set itself as the stream’s delegate, open and schedule the stream in a runloop.
- (id)initWithInputStream:(NSInputStream *)stream resultHandler:(DataExportHandler)handler;

// forward the close to the stream
- (void)close;

// Implementation of the stream delegate callbacks can be more or less copy-pasted from Apple’s Stream Programming Guide (https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Streams/Streams.html)
@end

@implementation DataStore (FileExport)

- (id)asynchronouslyExportDataForObjectWithIdentifier:(id)someUniqueIdentifier resultHandler:(void (^)(NSData *fileData, NSError *exportError))
{
    NSParameterAssert(handler);
    handler = [handler copy];

    NSError *setupError;
    NSInputStream *exportStream = [self exportStreamForObjectWithIdentifier:someUniqueIdentifier error:&setupError];
    if (!exportStream)
    {
        dispatch_async(dispatch_get_current_queue(), ^{
            handler(nil, setupError);
        });

        return nil;
    }

    _StreamConsumer *helper = [[_StreamConsumer alloc] initWithStream:exportStream resultHandler:handler];

    return helper;
}

- (void)abortAsynchronousExportWithToken:(id)exportToken
{
    [exportToken close];
}

- (NSInputStream *)exportStreamForObjectWithIdentifier:(id)identifier error:(NSError * __autoreleasing*)error
{
    // do your thing to retrieve the URL to the actual data-file and then:
    NSInputStream *rawDataStream = [NSInputStream inputStreamWithURL:rawFileURL];

    if (!rawDataStream)
    {
        // populate the error in a meaningful way
        return nil;
    }

    CFReadStream cfExportStream;
    CFWriteStream cfBuffer;
    CFStreamCreateBoundPair(kCFAllocatorDefault, &cfExportStream, &cfBuffer, someValueYouHaveTuned);

    if (!cfExportStream || !cfBuffer)
    {
        // error population
        return nil;
    }

    NSInputStream *exportStream = (__bridge_transfer NSInputStream *)cfExportStream;

    // HACKITY HACK: In reality, you’d want this stuff separated!
    // For the sake of simplicity, take the responsibility for that ourselves
    _exportBuffer = (__bridge_transfer NSOutputStream *)cfBuffer;

    rawDataStream.delegate = self;
    [rawDataStream open];
    [rawDataStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunloopDefaultMode];
    // END: HACKITY HACK

    return exportStream;
}

@end
于 2012-05-21T09:49:01.810 に答える
3

メインバンドルは読み取り専用で、何も書き込むことができません。
書き込み用にドキュメントディレクトリがあります。
これはメインバンドルからファイルを読み取ります

NSString *path= [[NSBundle mainBundle] pathForResource:@"myFile" ofType:@"txt"];

編集
済みあなたが示したように、あなたのヘッダーは1から10バイトです。
あなたのファイルを読んでいる人があなたのヘッダーの正確な長さを知っている方法を教えてください。1から10までの2、3、または7のいずれかになります。特定の長さのヘッダーがあることを示す方法が必要です。これは、ファイルの他の部分の場合と同じです。
この情報がなければ、ヘッダー、本文、またはフッターのサイズを知ることはできないと思います。
このファイルを作成した場合、ヘッダーの最初のバイトをヘッダーの長さとして配置して、誰でもヘッダーを読み取れるようにし、ヘッダーを読み取った後の最初のバイトは、本文のサイズとフッターのサイズになります。

于 2012-05-18T08:56:22.917 に答える
1

Cストリームは非常FILE*に簡単ですfopenfseeko、、、、、。freadfwrite

データがわずか266バイトの場合、それは十分に小さいので、を使用してすべてを読み取り、のメソッド[NSMutableData dataWithContentsOfURL:url]を使用して書き戻す(ファイル全体を上書きする)ことができます。ただし、大きなファイルではこのアプローチを避けたいと思うでしょう。その時点で、Cインターフェイス(上記)を使用するか、、、、、、などを検討する必要があります。NSDatawrite*NSFileHandleNSInputStreamNSOutputStreamCFReadStreamCFWriteStream

于 2012-05-21T07:26:01.023 に答える