readInt() / readFloat() companions for readLine()

stdlib

(Bruno Rocha) #1

Hi people,
This is not a big problem, but it's something that has been bugging me for a while. In command line tools, we often have to parse the input strings to their proper types, such as integers.

But because we only have readLine() which returns a String, we always have to resort to casting. I must have seen and written lines like this hundreds of times:

let number = Int(readLine()!)!
let array: [Int] = readLine()!.components(separatedBy: " ").map { Int($0)! }

We do have Scanner which provides more options, but since it doesn't act directly on the standard input I've found it to be slower than simply retrieving and transforming the lines yourself.

I wanted to know what are your thoughts regarding adding some companions methods to readLine() for common types, such as readInt(), readFloat(), readCharacter() and so on. These could act as simple wrappers on top of readLine() that cast the input, or go the full way and act like Scanners themselves, reading the input until the desired type is found.

let number: Int? = readInt()

(Brent Royal-Gordon) #3

The LosslessStringConvertible protocol may be your friend here:

let number = readLine().flatMap(Int.init(_:))

I suppose we could encapsulate it like this:

func readLine<T: LosslessStringConvertible>(as type: T.Type) -> T? {
  return readLine().flatMap(type.init(_:))
}

let number = readLine(as: Int.self)

(Bruno Rocha) #4

That's great! I didn't know LosslessStringConvertible existed.

I was hoping we could also have options that that partially retrieved lines (like cin >>) to skip the overhead of processing the entire line when other characters are involved. I hacked together an ugly test:

void
swift::swift_stdlib_readInt_stdin(long **ptr) {
    long n;
    int success = scanf("%ld", &n);
    if (success == 1) {
        *ptr = &n;
    } else {
        //scanf error state
    }
}
public func readInt() -> Int? {
    var ptr: UnsafeMutablePointer<Int>?
    swift_stdlib_readInt_stdin(&ptr)
    return linePtrVar?.pointee
}

///

//"3 2"
readInt() // Optional(3)
readInt() // Optional(2)
readInt() // nil

I'm mostly looking for increased performance since I think most of the uses for readLine() today end up parsing the content to something else.