2012-05-18 66 views
3

我需要更改文檔文件夾中文件的標頭信息。 什麼是讀取和寫回二進制數據最值得推薦的方式?更改標頭信息二進制並將其寫回

  1. 如何從文件夾的二進制數據讀取到陣列/流
  2. 如何將數據寫入從Array /流本地iPad的文件夾回?

Binary reading/writing in objective-c

回答

3

這一個是硬而你沒有更具體一點回答,但最有效的方法來做到這一點可以說是根本不改變文件。

由於我們在談論iOS,除了應用程序本身之外,沒有文件系統級訪問這些文檔的權限。那麼,爲什麼不保存在應用程序範圍內的元數據存儲(如iTunes或iPhoto)中與您的文件關聯的附加/自定義標題數據,並在導出期間將它們與實際文件標題進行交織?

無論如何,我真的沒有看到一個令人信服的理由,下降到C級文件功能來更改這些數據: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回調。

你只是通過文件的其餘部分頭後...

/// 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 
+0

嗨。感謝你的回答。也許你可以顯示在哪裏寫入樣本,以11開頭的索引讀取字節?直到讀到字節索引255? – Nasenbaer

3

主束是隻讀的,你不能寫有什麼。
對於寫作我們有文檔目錄。
這將從主束

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

編輯
讀取文件當你表現出你的頭是從1到10個字節..
你能告訴我一個覺得怎麼一個誰正在讀取您的文件知道什麼是您的標題的確切長度。它可以是2到3或7到1到10之間的任何值。必須有一種方法來告訴它它具有特定長度的標題,文件的其他部分也是如此。
沒有這個信息,我不認爲人會知道您的標題,身體或頁腳的大小。
如果我已經創建了這個文件,我可能會將頭的第一個字節作爲頭的長度,這樣任何人都可以讀取頭,並在讀取頭之後的第一個字節之後,爲主體的大小做出相同的處理。

+0

感謝。這回答了問題的一部分。你能夠給出有關讀取/寫入文件碎片的兩個問題的任何答案嗎? – Nasenbaer

+0

感謝您更新您的答案。但是最祕密的問題仍然不清楚如何將文件分成三部分。 – Nasenbaer

1

Ç流是很容易:FILE*fopenfseekofreadfwrite

如果你的數據的只是266個字節,這是足夠小,你可以閱讀所有使用[NSMutableData dataWithContentsOfURL:url],然後將它寫回用NSDatawrite*方法(覆蓋整個文件)。但是,您將希望避免使用較大文件的方法。在這一點上,你會想用C接口(上圖),或考慮NSFileHandleNSInputStreamNSOutputStreamCFReadStreamCFWriteStream等。