Eingabe über die Tastatur in der Befehlszeilenanwendung

91

Ich versuche, die Tastatureingabe für eine Befehlszeilen-App für die neue Apple-Programmiersprache Swift zu erhalten.

Ich habe die Dokumente ohne Erfolg gescannt.

import Foundation

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

Irgendwelche Ideen?

Chalkers
quelle

Antworten:

135

Der richtige Weg, dies zu tun, ist die Verwendung readLineaus der Swift Standard Library.

Beispiel:

let response = readLine()

Gibt Ihnen einen optionalen Wert, der den eingegebenen Text enthält.

Hesekiel Elin
quelle
2
Vielen Dank. Gibt es eine Möglichkeit, eine Passwort-Eingabe zu erhalten?
Abhijit Gaikwad
Für mich fällt es einfach durch diese Zeile mit response = nil. Irgendeine Idee, wo das Problem liegt?
Peter Webb
4
@ PeterWebb - funktioniert gut im Xcode-Terminal, fällt auf dem Spielplatz durch :)
Aprofromindia
2
readLine wurde nach den meisten ursprünglichen Antworten hinzugefügt, daher ist die Einstellung meiner Meinung nach etwas ungerechtfertigt
russbishop
2
Korrigiert für Swift 3, SlimJim
Ezekiel Elin
62

Ich habe es geschafft, es herauszufinden, ohne zu C zu kommen:

Meine Lösung lautet wie folgt:

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

Neuere Versionen von Xcode benötigen eine explizite Typumwandlung (funktioniert in Xcode 6.4):

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}
Chalkers
quelle
5
Ich mag das auch, es kann auf eine Zeile komprimiert werden, wenn Sie keine Funktion definieren möchten, wie wenn Sie Eingaben nur einmal in einem Programm akzeptieren müssen:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11
3
Gibt es eine Möglichkeit, dies auf dem Spielplatz zu verwenden, um Benutzereingaben im laufenden Betrieb zu akzeptieren?
Rob Cameron
5
Sie können dies nicht auf dem Spielplatz verwenden. Sie müssen dort Variablen verwenden.
Chalkers
8
Beachten Sie, dass Sie damit Zeilenumbrüche in der Zeichenfolge erhalten. string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
Zieh
3
Sie erhalten auch seltsame Zeichen, wenn der Benutzer ein Zeichen löscht, eine Pfeiltaste verwendet usw. AAAGGGGGHHHH WARUM, Swift, Warum?
Ybakos
13

Es ist eigentlich nicht so einfach, Sie müssen mit der C-API interagieren. Es gibt keine Alternative zu scanf. Ich habe ein kleines Beispiel gebaut:

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);
Leandros
quelle
Oh Mann - ich wünschte, es gäbe etwas Trivialeres als das! Ich finde es schwierig, dies für eine Reihe von Zeichen anzupassen.
Chalkers
1
Es ist besser, eine "UserInput.h" zum Speichern der Funktionsdefinitionen zu erstellen und diese Datei in "cliinput-Bridging-Header.h" aufzunehmen.
Alan Zhiliang Feng
7

Bearbeiten Ab Swift 2.2 enthält die Standardbibliothek readLine. Ich werde auch feststellen, dass Swift auf Markdown-Dokumentkommentare umgestellt hat. Ich lasse meine ursprüngliche Antwort für den historischen Kontext.

Der Vollständigkeit halber hier eine Swift-Implementierung, die readlnich verwendet habe. Es verfügt über einen optionalen Parameter, der die maximale Anzahl von Bytes angibt, die Sie lesen möchten (dies kann die Länge des Strings sein oder nicht).

Dies zeigt auch die ordnungsgemäße Verwendung von Swiftdoc-Kommentaren. Swift generiert eine <project> .swiftdoc-Datei und Xcode verwendet sie.

///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) }
}
russbishop
quelle
Ich mag die Swiftdoc-Kommentare und den "öffentlichen" Zugriffsmodifikator. Ich hatte so etwas noch nie in Swift gesehen, aber der CChar und der C-String scheinen ein Rückschritt zu C- und 8-Bit-Zeichensätzen und Swift zu sein dreht sich alles um Unicode-Text ... oder sind Kommandozeilen-Tools alle ASCII?
Kaydell
2
Ich glaube, getchar () gibt ASCII zurück. Unicode-Terminals ist eine ganze Sache, auf die ich nicht
eingehen
5

Eine andere Alternative besteht darin, libedit für eine ordnungsgemäße Zeilenbearbeitung (Pfeiltasten usw.) und optionale Verlaufsunterstützung zu verknüpfen . Ich wollte dies für ein Projekt, das ich gerade starte, und habe ein grundlegendes Beispiel für die Einrichtung zusammengestellt .

Verwendung von schnell

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

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

ObjC-Wrapper zum Offenlegen von 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
Neil
quelle
5

Im Allgemeinen wird die Funktion readLine () zum Scannen von Eingaben von der Konsole verwendet. In normalen iOS-Projekten funktioniert dies jedoch erst, wenn Sie das Befehlszeilentool hinzufügen .

Der beste Weg zum Testen ist:

1. Erstellen Sie eine macOS-Datei

Geben Sie hier die Bildbeschreibung ein

2. Verwenden Sie die Funktion readLine (), um optionale Zeichenfolgen von der Konsole aus zu scannen

 import Foundation

 print("Please enter some input\n")

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

Ausgabe :

Please enter some input

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

Geben Sie hier die Bildbeschreibung ein

Ashis Laha
quelle
3

Hier ist ein einfaches Beispiel für die Eingabe von Benutzern in einer konsolenbasierten Anwendung: Sie können readLine () verwenden. Nehmen Sie die Eingabe von der Konsole für die erste Nummer und drücken Sie die Eingabetaste. Geben Sie danach die zweite Zahl ein, wie im Bild unten gezeigt:

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)

Ausgabe

Shehzad Ali
quelle
1

Ich schwöre bei Gott. Die Lösung dieses äußerst grundlegenden Problems entging mir JAHRE lang. Es ist so einfach ... aber es gibt so viele vage / schlechte Informationen da draußen; hoffentlich kann ich jemanden vor einigen der bodenlosen Kaninchenlöcher retten , in denen ich gelandet bin ...

Lassen Sie uns also einen "String" von "dem Benutzer" über "die Konsole" erhalten, über stdin, sollen wir ?

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

Wenn Sie es OHNE den nachfolgenden Zeilenumbruch möchten, fügen Sie einfach ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ

Alex Gray
quelle
4
Swift, sagt der OP Swift.
HenryRootTwo
1
Er sagte osx. Alles läuft auf dasselbe hinaus! Umfassen Sie Ihr inneres Ziel!
Alex Gray
2
Es gibt Leute, die in Swift schreiben und niemals Ziel C lernen werden. Hier geht es nicht darum, worauf es ankommt.
HenryRootTwo
1

Viele veraltete Antworten auf diese Frage. Ab Swift 2+ enthält die Swift Standard Library die Funktion readline () . Es wird ein Optional zurückgegeben, aber es wird nur Null sein, wenn EOF erreicht wurde. Dies tritt nicht auf, wenn Eingaben von der Tastatur abgerufen werden, sodass es in diesen Szenarien sicher gewaltsam ausgepackt werden kann. Wenn der Benutzer nichts eingibt, ist sein (nicht verpackter) Wert eine leere Zeichenfolge. Hier ist eine kleine Dienstprogrammfunktion, die den Benutzer mithilfe der Rekursion auffordert, bis mindestens ein Zeichen eingegeben wurde:

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

Beachten Sie, dass die Verwendung der optionalen Bindung (wenn let input = readLine ()) zur Überprüfung, ob etwas wie in anderen Antworten vorgeschlagen eingegeben wurde, nicht den gewünschten Effekt hat, da es beim Akzeptieren von Tastatureingaben niemals Null und mindestens "" ist.

Dies funktioniert nicht auf einem Spielplatz oder in einer anderen Umgebung, in der Sie keinen Zugriff auf die Eingabeaufforderung haben. Es scheint auch Probleme in der Befehlszeile REPL zu geben.

Andreas Bergström
quelle
0

Da es für dieses Problem keine ausgefallenen Lösungen gab, habe ich eine winzige Klasse erstellt, um die Standardeingabe in Swift zu lesen und zu analysieren. Sie finden es hier .

Beispiel

Zum Parsen:

+42 st_ring!
-0.987654321 12345678900
.42

Sie machen:

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"
}
Shoumikhin
quelle
Wie importiere ich die StreamScanner-Klasse?
Timothy Swan
@TimothySwan, genau wie im Abschnitt Installation von Readme ( github.com/shoumikhin/StreamScanner#installation ) erläutert . Grundsätzlich können Sie Ihrem Projekt einfach die Datei StreamScanner.swift hinzufügen.
Shoumikhin
0

Vor

Geben Sie hier die Bildbeschreibung ein

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

Korrektur

Geben Sie hier die Bildbeschreibung ein

Murphy
quelle
0

Dies funktioniert in xCode v6.2, ich denke das ist Swift v1.2

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

Wenn Sie eine durch Leerzeichen getrennte Zeichenfolge lesen und die Zeichenfolge sofort in ein Array aufteilen möchten, haben Sie folgende Möglichkeiten:

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

z.B.

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)")
Brian Ho
quelle
0

Wenn die Funktion readLine () unter Xcode ausgeführt wird, wartet die Debug-Konsole auf die Eingabe. Der Rest des Codes wird nach erfolgter Eingabe fortgesetzt.

    let inputStr = readLine()
    if let inputStr = inputStr {
        print(inputStr)
    }
Sourabh Shekhar
quelle
0

Die am besten bewertete Antwort auf diese Frage schlägt vor, die readLine () -Methode zu verwenden, um Benutzereingaben über die Befehlszeile zu erfassen. Ich möchte jedoch darauf hinweisen, dass Sie die! Operator beim Aufrufen dieser Methode, um eine Zeichenfolge anstelle einer optionalen zurückzugeben:

var response = readLine()!
topherPedersen
quelle
Warum das Force-Unwrap readLine()aus einem bestimmten Grund optional zurückgegeben wird, ist daher das Force-Unwrap unsicher und fügt dem Beispiel nichts hinzu.
Camsoft
0

Swift 5: Wenn Sie kontinuierlich Eingaben über die Tastatur wünschen, ohne das Programm wie einen Eingabestream zu beenden, führen Sie die folgenden Schritte aus:

  1. Erstellen Sie ein neues Projekt vom Typ comnnad line tool Befehlszeilenprojekt

    1. Fügen Sie den folgenden Code in die Datei main.swift ein:

      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. Führen Sie das Projekt aus, klicken Sie in Xcode auf die ausführbare Datei im Ordner "Produkte" und öffnen Sie sie im Finder
    3. Doppelklicken Sie auf die ausführbare Datei, um sie zu öffnen.
    4. Geben Sie nun Ihre Eingaben ein. Das Terminal sieht ungefähr so ​​aus: Geben Sie hier die Bildbeschreibung ein
Shikha Budhiraja
quelle
0
var a;
scanf("%s\n", n);

Ich habe dies in ObjC getestet, und vielleicht ist dies nützlich.

Paketüberfluss
quelle
-1

Ich wollte nur die Implementierung von xenadu kommentieren (ich habe nicht genug Wiederholungen), weil dies CCharin OS X der Fall ist Int8und Swift es überhaupt nicht mag, wenn Sie dem Array wann hinzufügengetchar() Teile von UTF-8 oder etwas anderes über 7 Bit zurückgegeben werden.

Ich verwende UInt8stattdessen ein Array von , und es funktioniert großartig und String.fromCStringkonvertiert das UInt8in UTF-8 ganz gut.

Aber so habe ich es gemacht

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)")
    }
}
Aredigg
quelle
-1

Ich konnte jetzt Tastatureingaben in Swift mithilfe der folgenden Funktionen abrufen:

In meiner Datei main.swift habe ich eine Variable i deklariert und ihr die Funktion GetInt () zugewiesen, die ich in Ziel C definiert habe. Über einen sogenannten Bridging Header, in dem ich den Funktionsprototyp für GetInt deklariert habe, konnte ich eine Verknüpfung zu main.swift herstellen. Hier sind die Dateien:

main.swift:

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

Überbrückungskopfzeile:

#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 ist es möglich, die c-Standardausgabe und -eingabe stdio.h sowie die c-Standardbibliothek stdlib.h einzuschließen, mit der Sie in C in Objective-C programmieren können, sodass keine Einbeziehung erforderlich ist eine wirklich schnelle Datei wie user.c oder so ähnlich.

Hoffe ich konnte helfen,

Bearbeiten: Es ist nicht möglich, String-Eingaben über C zu erhalten, da ich hier das CInt -> den Integer-Typ von C und nicht von Swift verwende. Es gibt keinen äquivalenten Swift-Typ für das C-Zeichen *. Daher kann String nicht in String konvertiert werden. Aber es gibt hier ziemlich genug Lösungen, um String-Eingaben zu erhalten.

Raul

Raul Rao
quelle