简体   繁体   中英

Input from the keyboard in command line application

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?

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.

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
}

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);

In general function is used for scanning input from console.函数用于扫描来自控制台的输入。 But it will not work in normal iOS project until or unless you add .否则它不会在正常的 iOS 项目中工作。

The best way for testing, you can do :

1. Create an macOS file

在此处输入图片说明

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

在此处输入图片说明

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) }
}

Another alternative is to link libedit 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 .

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

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)

输出

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.

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命令行项目

    1. 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:") }
    2. RunThe project and click the executable under Products folder in Xcode and open in finder
    3. Double click the executable to open it.
    4. Now enter your Inputs. Terminal will look something like this: 在此处输入图片说明

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! ♥ ⱥᏪℯⅩ

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"
}

Before

在此处输入图片说明

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

Correction

在此处输入图片说明

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
}

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)")

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)
    }

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()!
var a;
scanf("%s\n", n);

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

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)
}

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)")
    }
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM