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


91

我正在尝试获取新的Apple编程语言Swift的命令行应用程序的键盘输入。

我扫描了文档无济于事。

import Foundation

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

有任何想法吗?

Answers:


135

正确的方法是使用readLineSwift标准库中的。

例:

let response = readLine()

将为您提供一个可选值,其中包含输入的文本。


2
谢谢。有没有办法获得密码类型的输入?
Abhijit Gaikwad

对我来说,它只是通过response = nil掉到这一行。知道是什么问题吗?
Peter Webb

4
@PeterWebb-在xcode终端中工作正常,在操场上摔倒了:)
aprofromindia 16-4-8

2
在大多数原始答案后都添加了readLine,因此态度有点不正确,恕我直言
russbishop

2
校正斯威夫特3,SlimJim
结艾琳

62

我设法弄清楚了,而没有陷入C:

我的解决方案如下:

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

较新的Xcode版本需要显式的类型转换(在Xcode 6.4中有效):

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

5
我也喜欢这样,如果您不想定义一个函数,可以将其压缩为一行,就像您只需要在程序中接受一次输入:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11 2014年

3
有没有办法在Playground中使用它来即时接受用户输入?
罗伯·卡梅隆

5
您不能在操场上使用它。您必须在其中使用变量。
Chalkers 2014年

8
注意,您将在字符串中获得换行符。用string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb

3
如果用户删除字符,使用箭头键等,您还会得到奇怪的字符。AAAGGGGGHHHH为什么,Swift,为什么?
ybakos 2015年

13

实际上并不是那么容易,您必须与C API进行交互。别无选择scanf。我建立了一个小例子:

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

哦,男孩-我希望有比这更琐碎的东西!我发现很难对此进行调整以适应字符串。
Chalkers 2014年

1
最好创建一个“ UserInput.h”来存储函数定义,并将该文件包含在“ cliinput-Bridging-Header.h”中。
冯志良

7

编辑夫特2.2的作为标准库包括readLine。我还将注意到Swift切换到markdown文档注释。保留我对历史背景的原始答案。

为了完整起见,这是readln我一直在使用的Swift实现。它具有一个可选参数,用于指示您要读取的最大字节数(它可以是字符串的长度,也可以不是字符串的长度)。

这也说明了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) }
}

我喜欢swiftdoc注释和“ public”访问修饰符,我以前在Swift中从未见过这种东西,但是CChar和C-String似乎是对C和8位字符集以及Swift的回溯是关于Unicode文本的全部...还是所有ASCII的命令行工具?
凯德尔2014年

2
我相信getchar()返回ASCII。Unicode终端是我不想讲的一整件事:)
russbishop 2014年

5

另一个选择是链接libedit以进行适当的行编辑(箭头键等)和可选的历史记录支持。我希望这个项目可以用于我正在启动的项目,并提供一个有关如何设置它基本示例

快速使用

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

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

ObjC包装器公开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

5

通常,readLine()函数用于扫描来自控制台的输入。但是,除非您添加“命令行工具”,否则它将无法在普通的iOS项目中使用。

最好的测试方法是:

1.创建一个macOS文件

在此处输入图片说明

2.使用readLine()函数从控制台扫描可选的字符串

 import Foundation

 print("Please enter some input\n")

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

输出:

Please enter some input

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

在此处输入图片说明


3

这是在基于控制台的应用程序上从用户那里获取输入的简单示例:可以使用readLine()。从控制台输入第一个数字,然后按Enter。之后,输入第二个数字,如下图所示:

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)

输出量


1

我向上帝发誓.. 几年以来,对这个根本问题的解决方案一直困扰着我。太简单了 ..但是那里有太多模糊/不好的信息。希望我能救一个人一些无底的兔子洞,我在结束了...

因此,让我们通过“控制台”,“通过”从“用户”获得“字符串” stdin可以吗?

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

如果您想要它而没有尾随换行符,只需添加...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ⱥᏪℯⅩ


4
Swift,OP称Swift。
HenryRootTwo16年

1
他说osx。一切都编译成同一件事!拥抱你的内心对象!
亚历克斯·格雷

2
有些人用Swift编写东西,从不学习目标C。这与其编译成什么无关。
HenryRootTwo

1

这个问题有很多过时的答案。从Swift 2+开始,Swift标准库包含readline()函数。它会返回一个Optional,但只有在达到EOF时才为零,当从键盘获取输入时不会发生,因此在这些情况下可以安全地用力打开它。如果用户未输入任何内容,则其(未包装)值将为空字符串。这是一个小的实用程序函数,它使用递归来提示用户,直到输入至少一个字符为止:

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

请注意,使用可选绑定(如果让input = readLine())来检查是否按照其他答案中的建议输入了某些内容,将不会达到预期的效果,因为它永远不会为零,并且在接受键盘输入时至少为“”。

在无法访问命令提示符的Playground或任何其他环境中,这将不起作用。在命令行REPL中似乎也有问题。


0

由于没有解决这个问题的理想方法,因此我制作了一个小类来读取和解析Swift中的标准输入。你可以在这里找到

解析:

+42 st_ring!
-0.987654321 12345678900
.42

你做:

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

如何导入StreamScanner类?
蒂莫西·斯旺

@TimothySwan,如自述文件的“安装”部分(github.com/shoumikhin/StreamScanner#installation)中所述。因此,基本上,您可以仅将StreamScanner.swift文件添加到您的项目中。
shoumikhin


0

这在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
}

0

如果要读取以空格分隔的字符串,并立即将字符串拆分为数组,则可以执行以下操作:

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

例如。

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

0

当在Xcode上运行readLine()函数时,调试控制台将等待输入。输入完成后,其余代码将恢复。

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

0

该问题的最高答案建议使用readLine()方法从命令行接收用户输入。但是,我要注意,您需要使用!运算符,当调用此方法以返回字符串而不是可选字符串时:

var response = readLine()!

为什么强制展开会readLine()返回可选参数(由于某种原因),因此强制展开是不安全的,并且实际上并未在示例中添加任何内容。
Camsoft

0

迅捷5:如果您持续希望通过键盘进行输入,而又不像输入流那样结束程序,请执行以下步骤:

  1. 创建comnnad线工具类型的新项目 命令行项目

    1. 在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. 运行项目,然后单击Xco​​de中Products文件夹下的可执行文件,然后在finder中打开
    3. 双击可执行文件以将其打开。
    4. 现在输入您的输入。终端看起来像这样: 在此处输入图片说明

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

我在ObjC中进行了测试,也许这会很有用。


-1

我只是想评论xenadu的实现(我没有足够的代表),因为CChar在OS X中是Int8,而Swift在添加到数组时根本不喜欢getchar()返回部分UTF-8或其他7位以上的值将。

我使用的是一个数组UInt8,它可以很好地工作并String.fromCString转换UInt8为UTF-8很好。

但是,这就是我的方法

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

-1

我现在可以通过使用以下命令在Swift中获取键盘输入:

在我的main.swift文件中,我声明了一个变量i,并为其分配了我在Objective C中定义的函数GetInt()。通过所谓的桥接头,在其中声明了GetInt的函数原型,我可以链接到main.swift。这些是文件:

main.swift:

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

桥接头:

#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;
}

在obj.m中,可以包含c标准输出和输入stdio.h,以及c标准库stdlib.h,该库使您可以在Objective-C中使用C进行编程,这意味着不需要包含C。一个真正的快捷文件,例如user.c或类似的文件。

希望我能帮上忙,

编辑:不可能通过C获得String输入,因为在这里我使用的是CInt-> C而不是Swift的整数类型。C char *没有等效的Swift类型。因此,字符串不能转换为字符串。但是,这里有足够的解决方案来获取String输入。

劳尔

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.