Domanda

Sto cercando di fare un flusso di input personalizzato in base al largo di quello da Dave DeLong qui che permette anche per la lettura di dati da un server tramite NSURL. Finora, ho questo approccio, che funziona bene per i file locali:

@interface RJRStreamReader : NSObject {
    @private
    NSFileHandle *fileHandle;
    NSStringEncoding encoding;
    NSString *lineDelimiter;
    unsigned long long currentOffset;
    unsigned long long totalFileLength;
    int chunkSize;
}

@property(readwrite, assign) NSStringEncoding encoding;
@property(readwrite, assign) unsigned long long currentOffset;
@property(readwrite, copy) NSString *lineDelimiter;
@property(readwrite, assign) int chunkSize;
@property(readonly) unsigned long long totalFileLength;

-(id) initWithLocalFile:(NSString *) fileName;

-(id) initWithURL:(NSURL *) remoteURL;

-(id) initWithFileHandle:(NSFileHandle *) fh;

-(NSString *) readToEnd;

-(NSString *) readLine;

/**
 @summary Reads a block of bytes from a stream
 @param blockLen the number of bytes to read
 @returns a string containing the data from the bytes read
 */
-(NSString *) readBlock:(int) blockLen;

@end

E la mia realizzazione:

@implementation RJRStreamReader

@synthesize currentOffset, lineDelimiter, encoding, chunkSize, totalFileLength;

-(id) initWithLocalFile:(NSString *)fileName
{
    if (self = [super init])
    {
        fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileName];
        if (fileHandle == nil)
        {
            [self release];
            return nil;
        }

        chunkSize = 10;
        encoding = NSUTF8StringEncoding;
        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        [fileHandle retain];
        currentOffset = 0ULL;
        [fileHandle seekToEndOfFile];
        totalFileLength = [fileHandle offsetInFile];
    }

    return self;
}

-(id) initWithURL:(NSURL *)remoteURL
{
    if (self = [super init])
    {
        NSError *err = nil;
        fileHandle = [NSFileHandle fileHandleForReadingFromURL:remoteURL error:&err];
        if (err)
        {
            NSLog(@"Error occurred, aborting. Details: %@", err);
            [self release];
            return nil;
        }

        chunkSize = 10;
        encoding = NSUTF8StringEncoding;
        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        [fileHandle retain];
        currentOffset = 0ULL;
        [fileHandle seekToEndOfFile];
        totalFileLength = [fileHandle offsetInFile];
    }

    return self;
}

-(id) initWithFileHandle:(NSFileHandle *) fh
{
    if (self = [super init])
    {
        fileHandle = fh;
        if (!fh)
        {
            [self release];
            [NSException raise:@"FH cannot be nil!" format:@"FH cannot be nil!"];
            return nil;
        }

        chunkSize = 10;
        encoding = NSUTF8StringEncoding;
        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        [fileHandle retain];
        currentOffset = 0ULL;
        [fileHandle seekToEndOfFile];
        totalFileLength = [fileHandle offsetInFile];
    }

    return self;
}

-(NSString *) readBlock:(int)blockLen
{
    if (currentOffset >= totalFileLength) { return nil; }

    [fileHandle seekToFileOffset:currentOffset];
    NSData *data = [fileHandle readDataOfLength:blockLen];
    currentOffset += blockLen;
    [fileHandle seekToFileOffset:currentOffset];

    return [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
}

-(NSString *) readLine
{
    /* 
       if you want to see the code for this method, visit this link: 
       https://stackoverflow.com/questions/3707427/how-to-read-data-from-nsfilehandle-line-by-line
       it is exactly the same
     */
}

-(NSString *) readToEnd
{
    if (currentOffset >= totalFileLength) { return nil; }

    [fileHandle seekToFileOffset:currentOffset];
    NSData *data = [fileHandle readDataToEndOfFile];
    currentOffset = totalFileLength;
    [fileHandle seekToEndOfFile];

    return [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
}

-(void) dealloc
{
    [fileHandle closeFile];
    [fileHandle release];
    [lineDelimiter release];
    [super dealloc];
}

@end

Il problema è che quando cerco di usare in questo modo:

RJRStreamReader *stream = [[RJRStreamReader alloc] initWithURL:[NSURL URLWithString:@"http://www.stackoverflow.com/robots.txt"];
NSString *s = [stream readToEnd];

s sarà sempre vuoto perchè il NSFileHandle restituito da [NSFileHandle fileHandleForReadingFromURL:remoteURL] sembra sempre di tornare a zero, senza errori.

Si tratta di un bug nel mio codice o di una funzionalità non documentata di loro?

Grazie

È stato utile?

Soluzione

OK, questo è il mio problema:

Secondo la Documentazione

  

fileHandleForReadingFromURL: errore:   Restituisce un handle di file inizializzato per la lettura del file, dispositivo o nome presa all'URL specificato.

Non fa leggere da un URL server, che mi ha portato a questo approccio (mi piacerebbe che per le persone a modificare questo per me, come ha bisogno di lazy loading, ottimizzazione, ecc.)

RJRStreamReader.h:

@interface RJRStreamReader : NSObject {
    @private
    NSData *data;
    NSStringEncoding encoding;
    NSString *lineDelimiter;
    unsigned long long currentOffset;
    unsigned long long totalFileLength;
    int chunkSize;
}

@property(readwrite, assign) NSStringEncoding encoding;
@property(readwrite, assign) unsigned long long currentOffset;
@property(readwrite, copy) NSString *lineDelimiter;
@property(readwrite, assign) int chunkSize;
@property(readonly) unsigned long long totalFileLength;

-(id) initWithLocalFile:(NSString *) fileName;

-(id) initWithURL:(NSURL *) remoteURL;

-(id) initWithFileHandle:(NSFileHandle *) fh;

-(id) initWithData:(NSData *) theData;

-(NSString *) readToEnd;

-(NSString *) readLine;

/**
 @summary Reads a block of bytes from a stream
 @param blockLen the number of bytes to read
 @returns a string containing the data from the bytes read
 */
-(NSString *) readBlock:(int) blockLen;

@end

RJRStreamReader.m:

@implementation RJRStreamReader

@synthesize currentOffset, lineDelimiter, encoding, chunkSize, totalFileLength;

-(id) initWithLocalFile:(NSString *)fileName
{
    if (self = [super init])
    {
        NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileName];

        if (fileHandle == nil)
        {
            [self release];
            return nil;
        }

        data = [[fileHandle readDataToEndOfFile] retain];
        chunkSize = 10;
        encoding = NSUTF8StringEncoding;
        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        currentOffset = 0ULL;
        [fileHandle seekToEndOfFile];
        totalFileLength = [fileHandle offsetInFile];
        [fileHandle closeFile];
    }

    return self;
}

-(id) initWithURL:(NSURL *)remoteURL
{
    if (self = [super init])
    {
        NSError *err = nil;
        NSURLResponse *resp = nil;

        data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:remoteURL] returningResponse:&resp error:&err];

        if (err)
        {
            NSLog(@"Error occurred, aborting. Details: %@", err);
            [self release];
            return nil;
        }

        chunkSize = 10;
        encoding = NSUTF8StringEncoding;
        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        [data retain];
        currentOffset = 0ULL;
        totalFileLength = [data length];
    }

    return self;
}

-(id) initWithFileHandle:(NSFileHandle *) fh
{
    if (self = [super init])
    {
        if (!fh)
        {
            [self release];
            [NSException raise:@"FH cannot be nil!" format:@"FH cannot be nil!"];
            return nil;
        }

        unsigned long long pos = [fh offsetInFile];

        data = [[fh readDataToEndOfFile] retain];       
        chunkSize = 10;
        encoding = NSUTF8StringEncoding;
        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        currentOffset = 0ULL;
        [fh seekToEndOfFile];
        totalFileLength = [fh offsetInFile];
        [fh seekToFileOffset:pos];
    }

    return self;
}

-(id) initWithData:(NSData *)theData
{
    if (self = [super init])
    {
        data = [theData retain];    
        chunkSize = 10;
        encoding = NSUTF8StringEncoding;
        lineDelimiter = [[NSString alloc] initWithString:@"\n"];
        currentOffset = 0ULL;
        totalFileLength = [data length];        
    }

    return self;
}

-(NSString *) readBlock:(int)blockLen
{
    if (currentOffset >= totalFileLength) { return nil; }

    NSData *tmpdata = [data subdataWithRange:NSMakeRange(currentOffset, blockLen)];
    currentOffset += blockLen;

    return [[[NSString alloc] initWithData:tmpdata encoding:encoding] autorelease];
}

-(NSString *) readLine
{
    if (currentOffset >= totalFileLength) { return nil; }

    NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    //[fileHandle seekToFileOffset:currentOffset];
    NSMutableData * currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    NSAutoreleasePool * readPool = [[NSAutoreleasePool alloc] init];
    while (shouldReadMore) {
        if (currentOffset >= totalFileLength) { break; }
        NSData *chunk = [data subdataWithRange:NSMakeRange(currentOffset, chunkSize)];
        NSRange newLineRange = [chunk rangeOfData_dd:newLineData];
        if (newLineRange.location != NSNotFound) {

            //include the length so we can include the delimiter in the string
            chunk = [chunk subdataWithRange:NSMakeRange(0, newLineRange.location)];
            shouldReadMore = NO;
        }
        [currentData appendData:chunk];
        currentOffset += [chunk length];
    }
    [readPool release];

    NSString * line = [[NSString alloc] initWithData:currentData encoding:NSUTF8StringEncoding];
    [currentData release];
    return [line autorelease];
}

-(NSString *) readToEnd
{
    if (currentOffset >= totalFileLength) { return nil; }

    NSData *tmpdata = [data subdataWithRange:NSMakeRange(currentOffset, totalFileLength - currentOffset)];
    currentOffset = totalFileLength;

    return [[[NSString alloc] initWithData:tmpdata encoding:encoding] autorelease];
}

-(void) dealloc
{
    [data release];
    [lineDelimiter release];
    [super dealloc];
}

@end
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top