简体   繁体   English

在命令行应用程序中从键盘输入

[英]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.我正在尝试为新的 Apple 编程语言 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.正确的方法是使用 Swift 标准库中的readLine

Example:例子:

let response = readLine()

Will give you an Optional value containing the entered text.将为您提供一个包含输入文本的 Optional 值。

I managed to figure it out without dropping down in to C:我设法弄明白了,而没有掉到 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):较新版本的 Xcode 需要显式类型转换(适用于 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.这实际上并不那么容易,您必须与 C API 进行交互。 There is no alternative to scanf . scanf没有其他选择。 I've build a little example:我已经建立了一个小例子:

main.swift 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 cliinput-Bridging-Header.h

void getInput(int *output);

In general readLine() function is used for scanning input from console.通常readLine()函数用于扫描来自控制台的输入。 But it will not work in normal iOS project until or unless you add "command-line tool" .但是除非您添加“命令行工具”,否则它不会在正常的 iOS 项目中工作。

The best way for testing, you can do :测试的最佳方式,你可以这样做:

1. Create an macOS file 1.创建一个macOS文件

在此处输入图片说明

2. Use the readLine() func to scan optional String from console 2. 使用 readLine() 函数从控制台扫描可选字符串

 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 .编辑从 Swift 2.2 开始,标准库包括readLine I'll also note Swift switched to markdown doc comments.我还会注意到 Swift 切换到了 Markdown 文档注释。 Leaving my original answer for historical context.留下我对历史背景的原始答案。

Just for completeness, here is a Swift implementation of readln I've been using.为了完整readln ,这里是我一直在使用的readln的 Swift 实现。 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.这也演示了 swiftdoc 注释的正确使用 - Swift 将生成一个 <project>.swiftdoc 文件,Xcode 将使用它。

///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.另一种选择是链接libedit以进行正确的行编辑(箭头键等)和可选的历史支持。 I wanted this for a project I'm starting and put together a basic example for how I set it up .我想要这个用于我正在启动的项目,并 为我如何设置它整理了一个 基本示例

Usage from swift来自 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用于公开 libedit 的 ObjC 包装器

#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().这是在基于控制台的应用程序上从用户那里获取输入的简单示例:您可以使用 readLine()。 Take input from console for first number then press enter.从控制台输入第一个数字,然后按 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.从 Swift 2+ 开始,Swift 标准库包含readline()函数。 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.它将返回一个 Optional 但只有在达到 EOF 时它才会为零,从键盘获取输入时不会发生这种情况,因此在这些情况下可以安全地强制解包。 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.请注意,使用可选绑定(如果让 input = readLine())检查是否按照其他答案中的建议输入了某些内容将不会产生预期的效果,因为在接受键盘输入时它永远不会为零并且至少为 ""。

This will not work in a Playground or any other environment where you does not have access to the command prompt.这在 Playground 或您无权访问命令提示符的任何其他环境中不起作用。 It seems to have issues in the command-line REPL as well.命令行 REPL 中似乎也有问题。

Swift 5 : If you continuously want input from keyboard , without ending the program, like a stream of input, Use below steps: Swift 5 :如果您一直希望从键盘输入,而不像输入流那样结束程序,请使用以下步骤:

  1. Create new project of type comnnad line tool创建 comnnad line tool 类型的新项目命令行项目

    1. Add below code in main.swift file:在 main.swift 文件中添加以下代码:

       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运行工程,点击Xcode中Products文件夹下的可执行文件,在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 ?那么,让我们开始从“用户”通过“控制台”一“串”,通过stdin ,好吗

[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.由于这个问题没有奇特的解决方案,我创建了一个小类来读取和解析 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这适用于 xCode v6.2,我认为这是 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.当在 Xcode 上运行 readLine() 函数时,调试控制台等待输入。 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.这个问题排名靠前的答案建议使用 readLine() 方法从命令行接收用户输入。 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.我在 ObjC 中对此进行了测试,也许这会很有用。

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.我只是想评论(我没有足够的代表)在xenadu的实现,因为CChar在OS X是Int8 ,当你添加到阵列时,斯威夫特不喜欢在所有getchar()返回UTF-8,或任何部分否则超过 7 位。

I am using an array of UInt8 instead, and it works great and String.fromCString converts the UInt8 into UTF-8 just fine.我使用的是UInt8数组,它工作得很好, String.fromCStringUInt8转换为 UTF-8 就好了。

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:我现在可以使用以下方法在 Swift 中获得键盘输入:

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.在我的 main.swift 文件中,我声明了一个变量 i 并将我在 Objective C 中定义的函数 GetInt() 分配给它。通过一个所谓的桥接头,我在其中声明了 GetInt 的函数原型,我可以链接到 main.swift。 Here are the files:以下是文件:

main.swift: main.swift:

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

Bridging Header:桥接标题:

#include "obj.m"

int GetInt();

obj.m:对象.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.在 obj.m 中,可以包含 c 标准输出和输入 stdio.h,以及 c 标准库 stdlib.h,它使您能够在 Objective-C 中使用 C 进行编程,这意味着不需要包括一个真正的 swift 文件,比如 user.c 或类似的东西。

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.编辑:不可能通过 C 获得字符串输入,因为在这里我使用的是 CInt -> C 的整数类型而不是 Swift。 There is no equivalent Swift type for the C char*. C char* 没有等效的 Swift 类型。 Therefore String is not convertible to string.因此字符串不能转换为字符串。 But there are fairly enough solutions around here to get String input.但是这里有足够多的解决方案来获取字符串输入。

Raul劳尔

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从命令行读取输入时允许进行行编辑 - Allow line editing when reading input from the command line 如何在 Swift 的命令行工具应用程序中获取键盘输入(非字符串)? - How to get keyboard inputs (non string) in Command Line Tool application in Swift? 如何从命令行运行XCTest以获得快速应用程序? - How can I run XCTest for a swift application from the command line? 如何使用 AudioKit 编写从键盘打印所有 MIDI 音符的 Mac 命令行应用程序? - How to use AudioKit to write a Mac command line app that print all the midi notes from keyboard? 在Swift中从键盘读取输入 - Reading input from keyboard in Swift Mac 上的 CoreBluetooth 命令行应用程序 - CoreBluetooth on Mac Command line application 在Cocoa命令行应用程序的子进程中从stdin获取输入 - Getting input from stdin in a Cocoa command-line app's sub process 在 Swift 命令行应用程序中捕获信号 - Trapping signals in a Swift command line application 为什么我在Xcode命令行应用程序中没有收到来自URL请求的响应 - Why do I get no response from a URL Request in an Xcode Command-Line Application 如何在命令行应用程序中创建安全密码输入字段? - How to make a secure password input field in a command line app?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM