streamReader for server URLs

2019-07-15 14:28发布

I've been working with the code form this answer, provided by Martin R. The code is awesome and it is very useful. However, it doesn't work with the links, while working fine with files. After putting some NSLogs and breaks, I have actually found that problem is in this code block:

init?(path: String, delimiter: String = "\n", encoding: UInt = NSUTF8StringEncoding, chunkSize : Int = 4096) {
    self.chunkSize = chunkSize
    self.encoding = encoding

    self.fileHandle = NSFileHandle(forReadingFromURL: NSURL(string: path)!, error: nil)
    println("PATH IS \(path)")
    println("FILE HANDLE IS \(fileHandle)")
    if self.fileHandle == nil {
        println("FILE HANDLE IS NIL!")
        return nil
    }

The code above actually contains some minor changes, compared with Martin's answer. Also Apple says that it is possible to use fileHandle with forReadingFromURL and it shouldn't return nil. But here is the console output:

PATH IS http://smth.com
   FILE HANDLE IS nil
   FILE HANDLE IS NIL!!!!!

The question is what's wrong?

UPDATE

As Martin R has kindly explained to me, this code won't work with URLs, this answer states the same, so I have rewritten the code, guiding by previous answers:

import Foundation
import Cocoa

class StreamReader  {

let encoding : UInt
let chunkSize : Int
var atEof : Bool = false
var streamData : NSData!
var fileLength : Int
var urlRequest : NSMutableURLRequest
var currentOffset : Int
var streamResponse : NSString

var fileHandle : NSFileHandle!
let buffer : NSMutableData!
let delimData : NSData!

var reponseError: NSError?
var response: NSURLResponse?

init?(path: NSURL, delimiter: String = "\n", encoding: UInt = NSUTF8StringEncoding, chunkSize : Int = 10001000) {
    println("YOUR PATH IS \(path)")

    self.chunkSize = chunkSize
    self.encoding = encoding
    self.currentOffset = 0
    urlRequest = NSMutableURLRequest(URL: path)
    streamData = NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse:&response, error:&reponseError)
    streamResponse = NSString(data:streamData!, encoding:NSUTF8StringEncoding)!
    self.fileLength = streamData.length
    //println("WHAT IS STREAMDATA \(streamData)")
    //println("WHAT IS URLREQUEST \(urlRequest)")

    if streamData == nil {
        println("LINK HAS NO CONTENT!!!!!")
    }

            self.fileLength = streamResponse.length
    println("FILE LENGTH IS \(fileLength)")
    self.buffer = NSMutableData(capacity: chunkSize)!

    // Create NSData object containing the line delimiter:
    delimData = delimiter.dataUsingEncoding(NSUTF8StringEncoding)!
    println("WHAT DOES THE DELIMITER \(delimiter)LOOK LIKE?")
    println("WHAT IS DELIMDATA \(delimData)")
}

deinit {
    self.close()
}

/// Return next line, or nil on EOF.
func nextLine() -> String? {

    if atEof {
        println("AT THE END OF YOUR FILE!!!")
        return nil
    }
    // Read data chunks from file until a line delimiter is found:

    if currentOffset >= fileLength {
        return nil
    }
    var blockLength : Int = buffer.length

    var range = buffer.rangeOfData(delimData, options: NSDataSearchOptions(0), range: NSMakeRange(currentOffset, blockLength))
            //println("STREAM DATA \(streamData)")

    println("RANGE IS \(range)")
    while range.location == NSNotFound {
        var nRange = NSMakeRange(currentOffset, chunkSize)
        println("nRange is \(nRange)")
        var tmpData = streamData.subdataWithRange(nRange)
        //println("TMP data length \(tmpData.length)")
        currentOffset += blockLength
        //println("TMPDATA is \(tmpData)")
        if tmpData.length == 0 {
            // EOF or read error.
            println("ERROR ????")
            atEof = true
            if buffer.length > 0 {
                // Buffer contains last line in file (not terminated by delimiter).
                let line = NSString(data: buffer, encoding: encoding);
                buffer.length = 0
                println("THE LINE IS \(line)")
                return line
            }
            // No more lines.
            return nil
        }
        buffer.appendData(tmpData)
        range = buffer.rangeOfData(delimData, options: NSDataSearchOptions(0), range: NSMakeRange(0, buffer.length))
    }

    // Convert complete line (excluding the delimiter) to a string:
    let line = NSString(data: buffer.subdataWithRange(NSMakeRange(0, range.location)),
        encoding: encoding)
    // Remove line (and the delimiter) from the buffer:
    buffer.replaceBytesInRange(NSMakeRange(0, range.location + range.length), withBytes: nil, length: 0)

    return line
}

/// Start reading from the beginning of file.
func rewind() -> Void {
    //streamData.seekToFileOffset(0)
    buffer.length = 0
    atEof = false
}

/// Close the underlying file. No reading must be done after calling this method.
func close() -> Void {
    if streamData != nil {
        streamData = nil
    }
}
}

extension StreamReader : SequenceType {
func generate() -> GeneratorOf<String> {
    return GeneratorOf<String> {
        return self.nextLine()
    }
}
}

But actually this code is very far from being perfect and I would like to see any recommendations on improving it. Please, be kind. I am very amateur and very inexperienced( but sooner or later I will learn it)

Finally, it is working. And probably the last problem is left, the code doesn't stop, it continues to read file from the beginning.

So now the question is probably more close to 'What's wrong with my code?', compared with previous: 'What's wrong?'

UPDATE

I have rewritten last parts of code like this:

let line = NSString(data: buffer.subdataWithRange(NSMakeRange(0, range.location + 1)),
        encoding: encoding)
    buffer.replaceBytesInRange(NSMakeRange(0, range.location + range.length), withBytes: nil, length: 0)
    println("COMPLETE LINE IS \(line)")
    if line!.containsString("\n"){
        println("CONTAINS NEW LINE")
        //println("BUFFER IS \(buffer)")
        //
        println("COMPLETE LINE IS \(line)")
        return line
    }
    else {

        println("NO LINE!")
        atEof == true
        return nil
    }

The idea is to go through all the lines which contain \n and to exclude one line, which is the last one and shouldn't have the \n. But! Despite the fact that I have checked non-printing characters and there was no \n, here is the surprising console output: Optional("lastline_blablabla\n") Probably now the question is, how to stop at the last line even if it contains \n?

If you need to retrieve data from the URL my code above (the one under first update) will work

But my own problem with \n has several ways to be solved. One of which I used in my code. As it's very individual I won't post the solution to \n at the end of file issue. Also I am not sure that my both solutions for urlStreamReader and \n are the best, so if you advice any better solution it will be much appreciated by me and probably some other people.

Great thanks to Martin R, who explained a lot, wrote great code and was very nice

0条回答
登录 后发表回答