8

How to read user input or character stream from standard input in Swift for Linux?

masoud
  • 55,379
  • 16
  • 141
  • 208

3 Answers3

10

readLine() works on Ubuntu 15:

[Readline] Returns Characters read from standard input through the end of the current line or until EOF is reached, or nil if EOF has already been reached.

Example:

print("\nEnter your name:\n")
if let name = readLine() {
    print("\nHello \(name)!\n")
}

ed@swiftux:~/Swift/Scripts$ ./testReadline

Enter your name:

Eric

Hello Eric!

readline() also works with | (the pipe):

ed@swiftux:~/Swift/Scripts$ echo "Mike" | ./testReadline

Enter your name:

Hello Mike!


I've also tried the classic way with NSFileHandle but it's not implemented yet:

fatal error: availableData is not yet implemented: file Foundation/NSObjCRuntime.swift

Community
  • 1
  • 1
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • 1
    NSFileHandle is part of the Foundation framework. Remember, Swift is the language that was open sourced by Apple, not iOS or any of the other frameworks that build up their proprietary OS – Edwin May 23 '16 at 17:54
  • @Edwin Foundation is being implemented in Swift, open source, on GitHub. That's why I said NSFileHandle is not implemented *yet*, I was of course talking about Foundation, not about anything else and certainly not - you fool - about iOS or its SDKs. :p This is a Swift and Linux answer, nobody is talking about iOS here except you. – Eric Aya May 23 '16 at 18:07
  • thanks for educating me on that, I should have read your reply to the end, I am surely lesser of a *fool* right now. – Edwin May 24 '16 at 09:48
1

If you import Glibc on Linux and Darwin.C on macOS, then you can read one line at a time using the getline function.

This requires a wee bit of c interop, but you can wrap it up to present a more Swifty interface. For instance, the following function returns a generator that iterates over lines read from a file stream:

#if os(Linux)
  import Glibc
#else
  import Darwin.C
#endif

func lineGenerator(file:UnsafeMutablePointer<FILE>) -> AnyIterator<String>
{
  return AnyIterator { () -> String? in
    var line:UnsafeMutablePointer<CChar>? = nil
    var linecap:Int = 0
    defer { free(line) }
    let charactersWritten = getline(&line, &linecap, file)

    if charactersWritten > 0 {
      guard let line = line else { return nil }
      return String(validatingUTF8: line)
    }
    else {
      return nil
    }
  }
}

This works on Swift 3. You can find a small swiftecho example project which exercises this from the command line.

algal
  • 27,584
  • 13
  • 78
  • 80
0

I've found a way to read characters from stdin, however it's not beautiful but it shows what we can do by Glibc.

import Glibc

var buf = UnsafeMutablePointer<CChar>.alloc(32)

fread(buf, 1, 32, stdin)

// or

fgets(buf, 32, stdin)

and buf has the input characters.

masoud
  • 55,379
  • 16
  • 141
  • 208