我需要更改文档文件夹中文件的标题信息。 读取和写回二进制数据的最佳推荐方法是什么?
答案 0 :(得分:3)
主要包是只读的,你不能写任何东西。
为了写作,我们有文件目录
这将从主包中读取文件
NSString *path= [[NSBundle mainBundle] pathForResource:@"myFile" ofType:@"txt"];
已编辑
正如您所示,您的标题是1到10个字节..
你能不能告诉我一个人如何阅读你的文件,知道你的标题的确切长度是多少。它可以是1到10之间的2,3或7个。必须有一种方法来告诉它具有特定长度的标题,并且文件的其他部分也是如此。
没有这些信息我不认为你会知道你的标题,正文或页脚的大小。
如果我创建了这个文件,我可能会把标题的第一个字节作为标题的长度,这样任何人都可以读取标题,然后在读取标题之后,第一个字节的主体大小和页脚相同。
答案 1 :(得分: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
答案 2 :(得分:1)
C流非常简单:FILE*
,fopen
,fseeko
,fread
,fwrite
。
如果你的数据只有266个字节,那么它足够小,你可以使用[NSMutableData dataWithContentsOfURL:url]
全部读取它,然后使用NSData
{{1}将其写回(覆盖整个文件) }} 方法。但是,您需要避免使用较大文件的此方法。此时,您将需要使用C接口(上图),或考虑write*
,NSFileHandle
,NSInputStream
,NSOutputStream
,CFReadStream
等。