Input from the keyboard in command line application

SwiftMacosCocoaCommand Line

Swift Problem Overview


I am attempting to get the keyboard input for a command line app for the new Apple programming language Swift.

I've scanned the docs to no avail.

import Foundation

println("What is your name?")
???

Any ideas?

Swift Solutions


Solution 1 - Swift

The correct way to do this is to use readLine, from the Swift Standard Library.

Example:

let response = readLine()

Will give you an Optional value containing the entered text.

Solution 2 - Swift

I managed to figure it out without dropping down in to C:

My solution is as follows:

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)!
}

More recent versions of Xcode need an explicit typecast (works in Xcode 6.4):

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}

Solution 3 - Swift

It's actually not that easy, you have to interact with the C API. There is no alternative to scanf. I've build a little example:

main.swift

import Foundation

var output: CInt = 0
getInput(&output)

println(output)


UserInput.c

#include <stdio.h>

void getInput(int *output) {
    scanf("%i", output);
}


cliinput-Bridging-Header.h

void getInput(int *output);

Solution 4 - Swift

In general readLine() function is used for scanning input from console. But it will not work in normal iOS project until or unless you add "command-line tool".

The best way for testing, you can do :

1. Create an macOS file

enter image description here

2. Use the readLine() func to scan optional String from console

 import Foundation

 print("Please enter some input\n")

 if let response = readLine() {
    print("output :",response)
 } else {
    print("Nothing")
 }

Output :

Please enter some input

Hello, World
output : Hello, World
Program ended with exit code: 0

enter image description here

Solution 5 - Swift

edit As of Swift 2.2 the standard library includes readLine. I'll also note Swift switched to markdown doc comments. Leaving my original answer for historical context.

Just for completeness, here is a Swift implementation of readln I've been using. It has an optional parameter to indicate the maximum number of bytes you want to read (which may or may not be the length of the String).

This also demonstrates the proper use of swiftdoc comments - Swift will generate a <project>.swiftdoc file and Xcode will use it.

///reads a line from standard input
///
///:param: max specifies the number of bytes to read
///
///:returns: the string, or nil if an error was encountered trying to read Stdin
public func readln(max:Int = 8192) -> String? {
    assert(max > 0, "max must be between 1 and Int.max")
    
    var buf:Array<CChar> = []
    var c = getchar()
    while c != EOF && c != 10 && buf.count < max {
        buf.append(CChar(c))
        c = getchar()
    }
    
    //always null terminate
    buf.append(CChar(0))
    
    return buf.withUnsafeBufferPointer { String.fromCString($0.baseAddress) }
}

Solution 6 - Swift

Another alternative is to link [libedit][1] for proper line editing (arrow keys, etc.) and optional history support. I wanted this for a project I'm starting and put together a [basic example for how I set it up][2].

Usage from swift

let prompt: Prompt = Prompt(argv0: C_ARGV[0])

while (true) {
    if let line = prompt.gets() {
        print("You typed \(line)")
    }
}

ObjC wrapper to expose libedit

#import <histedit.h>

char* prompt(EditLine *e) {
    return "> ";
}

@implementation Prompt

EditLine* _el;
History* _hist;
HistEvent _ev;

- (instancetype) initWithArgv0:(const char*)argv0 {
    if (self = [super init]) {
        // Setup the editor
        _el = el_init(argv0, stdin, stdout, stderr);
        el_set(_el, EL_PROMPT, &prompt);
        el_set(_el, EL_EDITOR, "emacs");
        
        // With support for history
        _hist = history_init();
        history(_hist, &_ev, H_SETSIZE, 800);
        el_set(_el, EL_HIST, history, _hist);
    }
    
    return self;
}

- (void) dealloc {
    if (_hist != NULL) {
        history_end(_hist);
        _hist = NULL;
    }
    
    if (_el != NULL) {
        el_end(_el);
        _el = NULL;
    }
}

- (NSString*) gets {
    
    // line includes the trailing newline
    int count;
    const char* line = el_gets(_el, &count);
    
    if (count > 0) {
        history(_hist, &_ev, H_ENTER, line);
        
        return [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
    }

    return nil;
}

@end

[1]: http://thrysoee.dk/editline/ "libedit" [2]: https://github.com/neilpa/swift-libedit "swift-libedit"

Solution 7 - Swift

Here is simple example of taking input from user on console based application: You can use readLine(). Take input from console for first number then press enter. After that take input for second number as shown in the image below:

func solveMefirst(firstNo: Int , secondNo: Int) -> Int {
    return firstNo + secondNo
}

let num1 = readLine()
let num2 = readLine()

var IntNum1 = Int(num1!)
var IntNum2 = Int(num2!)

let sum = solveMefirst(IntNum1!, secondNo: IntNum2!)
print(sum)

Output

Solution 8 - Swift

Lots of outdated answers to this question. As of Swift 2+ the Swift Standard Library contains the readline() function. It will return an Optional but it will only be nil if EOF has been reached, which will not happen when getting input from the keyboard so it can safely be unwrapped by force in those scenarios. If the user does not enter anything its (unwrapped) value will be an empty string. Here's a small utility function that uses recursion to prompt the user until at least one character has been entered:

func prompt(message: String) -> String {
    print(message)
    let input: String = readLine()!
    if input == "" {
        return prompt(message: message)
    } else {
        return input
    }
}

let input = prompt(message: "Enter something!")
print("You entered \(input)")

Note that using optional binding (if let input = readLine()) to check if something was entered as proposed in other answers will not have the desired effect, as it will never be nil and at least "" when accepting keyboard input.

This will not work in a Playground or any other environment where you does not have access to the command prompt. It seems to have issues in the command-line REPL as well.

Solution 9 - Swift

Swift 5 : If you continuously want input from keyboard , without ending the program, like a stream of input, Use below steps:

  1. Create new project of type comnnad line tool Command line project

  2. Add below code in main.swift file:

         var inputArray = [String]()
    
         while let input = readLine() {
    
         guard input != "quit" else {
    
         break
    
         }
    
         inputArray.append(input)
    
         print("You entered: \(input)")
    
         print(inputArray)
    
         print("Enter a word:")
         }   
    
  3. RunThe project and click the executable under Products folder in Xcode and open in finder

  4. Double click the executable to open it.

  5. Now enter your Inputs. Terminal will look something like this: enter image description here

Solution 10 - Swift

I swear to God.. the solution to this utterly basic problem eluded me for YEARS. It's SO simple.. but there is so much vague / bad information out there; hopefully I can save someone from some of the bottomless rabbit holes that I ended up in...

So then, lets's get a "string" from "the user" via "the console", via stdin, shall we?

[NSString.alloc initWithData:
[NSFileHandle.fileHandleWithStandardInput availableData]
                          encoding:NSUTF8StringEncoding];

if you want it WITHOUT the trailing newline, just add...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥᏪℯⅩ

Solution 11 - Swift

Since there were no fancy solutions to this problem, I made a tiny class to read and parse the standard input in Swift. You can find it here.

Example

To parse:

+42 st_ring!
-0.987654321 12345678900
.42

You do:

let stdin = StreamScanner.standardInput

if
    let i: Int = stdin.read(),
    let s: String = stdin.read(),
    let d: Double = stdin.read(),
    let i64: Int64 = stdin.read(),
    let f: Float = stdin.read()
{
    print("\(i) \(s) \(d) \(i64) \(f)")  //prints "42 st_ring! -0.987654321 12345678900 0.42"
}

Solution 12 - Swift

Before

enter image description here

*******************.

Correction

enter image description here

Solution 13 - Swift

This works in xCode v6.2, I think that's Swift v1.2

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}

Solution 14 - Swift

If you want to read space separated string, and immediately split the string into an array, you can do this:

var arr = readLine()!.characters.split(" ").map(String.init)

eg.

print("What is your full name?")

var arr = readLine()!.characters.split(" ").map(String.init)

var firstName = ""
var middleName = ""
var lastName = ""

if arr.count > 0 {
    firstName = arr[0]
}
if arr.count > 2 {
    middleName = arr[1]
    lastName = arr[2]
} else if arr.count > 1 {
    lastName = arr[1]
}

print("First Name: \(firstName)")
print("Middle Name: \(middleName)")
print("Last Name: \(lastName)")

Solution 15 - Swift

When readLine() function is run on Xcode, the debug console waits for input. The rest of the code will be resumed after input is done.

    let inputStr = readLine()
    if let inputStr = inputStr {
        print(inputStr)
    }

Solution 16 - Swift

I just wanted to comment (I have not enough reps) on xenadu's implementation, because CChar in OS X is Int8, and Swift does not like at all when you add to the array when getchar() returns parts of UTF-8, or anything else above 7 bit.

I am using an array of UInt8 instead, and it works great and String.fromCString converts the UInt8 into UTF-8 just fine.

However this is how I done it

func readln() -> (str: String?, hadError: Bool) {
    var cstr: [UInt8] = []
    var c: Int32 = 0
    while c != EOF {
        c = getchar()
        if (c == 10 || c == 13) || c > 255 { break }
        cstr.append(UInt8(c))
    }
    cstr.append(0)
    return String.fromCStringRepairingIllFormedUTF8(UnsafePointer<CChar>(cstr))
}

while true {
    if let mystring = readln().str {
        println(" > \(mystring)")
    }
}

Solution 17 - Swift

I have now been able to get Keyboard input in Swift by using the following:

In my main.swift file I declared a variable i and assigned to it the function GetInt() which I defined in Objective C. Through a so called Bridging Header where I declared the function prototype for GetInt I could link to main.swift. Here are the files:

main.swift:

var i: CInt = GetInt()
println("Your input is \(i) ");

Bridging Header:

#include "obj.m"

int GetInt();

obj.m:

#import <Foundation/Foundation.h>
#import <stdio.h>
#import <stdlib.h>

int GetInt()
{
    int i;
    scanf("%i", &i);
    return i;
}

In obj.m it is possible to include the c standard output and input, stdio.h, as well as the c standard library stdlib.h which enables you to program in C in Objective-C, which means there is no need for including a real swift file like user.c or something like that.

Hope I could help,

Edit: It is not possible to get String input through C because here I am using the CInt -> the integer type of C and not of Swift. There is no equivalent Swift type for the C char*. Therefore String is not convertible to string. But there are fairly enough solutions around here to get String input.

Raul

Solution 18 - Swift

The top ranked answer to this question suggests using the readLine() method to take in user input from the command line. However, I want to note that you need to use the ! operator when calling this method to return a string instead of an optional:

var response = readLine()!

Solution 19 - Swift

var a;
scanf("%s\n", n);

I tested this out in ObjC, and maybe this will be useful.

Solution 20 - Swift

import Foundation
print("Enter a number")
let number : Int = Int(readLine(strippingNewline: true)!) ?? 0
if(number < 5)
{
    print("Small")
}else{
    print("Big")
}
for i in 0...number{
    print(i)
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChalkersView Question on Stackoverflow
Solution 1 - SwiftEzekielView Answer on Stackoverflow
Solution 2 - SwiftChalkersView Answer on Stackoverflow
Solution 3 - SwiftLeandrosView Answer on Stackoverflow
Solution 4 - SwiftAshis LahaView Answer on Stackoverflow
Solution 5 - SwiftrussbishopView Answer on Stackoverflow
Solution 6 - SwiftNeilView Answer on Stackoverflow
Solution 7 - SwiftShehzad AliView Answer on Stackoverflow
Solution 8 - SwiftAndreas BergströmView Answer on Stackoverflow
Solution 9 - SwiftShikha BudhirajaView Answer on Stackoverflow
Solution 10 - SwiftAlex GrayView Answer on Stackoverflow
Solution 11 - SwiftshoumikhinView Answer on Stackoverflow
Solution 12 - SwiftMurphyView Answer on Stackoverflow
Solution 13 - SwiftGiant ElkView Answer on Stackoverflow
Solution 14 - SwiftBrian HoView Answer on Stackoverflow
Solution 15 - SwiftSourabh ShekharView Answer on Stackoverflow
Solution 16 - SwiftarediggView Answer on Stackoverflow
Solution 17 - SwiftjrranalystView Answer on Stackoverflow
Solution 18 - SwifttopherPedersenView Answer on Stackoverflow
Solution 19 - Swiftuser13986003View Answer on Stackoverflow
Solution 20 - SwiftIndrajeet TripathiView Answer on Stackoverflow