3

我需要更改文件夹中文件的标题信息。最推荐的读取和写回二进制数据的方法是什么?

  1. 如何从文件夹二进制读取数据到数组/流
  2. 如何将数据从数组/流写回本地 iPad 文件夹?

Objective-C中的二进制读/写

4

3 回答 3

3

主包是只读的,你不能在那里写任何东西。
对于写作,我们有文档目录。
这将从主包中读取文件

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

已编辑
正如您所展示的,您的标头是 1 到 10 字节。
您能否告诉我一个正在阅读您文件的人如何知道您的标头的确切长度是多少。它可以是 1 到 10 之间的 2、3 或 7。必须有一种方法来判断它具有特定长度的标题,并且文件的其他部分也是如此。
如果没有这些信息,我认为人们永远不会知道您的页眉、正文或页脚的大小。
如果我创建了这个文件,我可能会将标题的第一个字节作为标题的长度,以便任何人都可以读取标题,并在读取标题之后的第一个字节用于正文的大小和页脚的大小。

于 2012-05-18T08:56:22.917 回答
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相应地实现了回调。

在标题之后,您只需通过文件的其余部分......</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 回答
1

C 流很简单:FILE*, fopen, fseeko, fread, fwrite.

如果您的数据只有 266 个字节,那么它足够小,您可以使用's方法将其全部读取[NSMutableData dataWithContentsOfURL:url],然后将其写回(覆盖整个文件) 。但是,对于较大的文件,您将希望避免这种方法。那时,您将需要使用 C 接口(如上),或考虑, , , ,等。NSDatawrite*NSFileHandleNSInputStreamNSOutputStreamCFReadStreamCFWriteStream

于 2012-05-21T07:26:01.023 回答