Input dalla tastiera nell'applicazione della riga di comando


91

Sto tentando di ottenere l'input da tastiera per un'app a riga di comando per il nuovo linguaggio di programmazione Apple Swift.

Ho scansionato i documenti senza alcun risultato.

import Foundation

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

Qualche idea?

Risposte:


135

Il modo corretto per farlo è usare readLinedalla Swift Standard Library.

Esempio:

let response = readLine()

Ti darà un valore opzionale contenente il testo inserito.


2
Grazie. c'è un modo per ottenere il tipo di password di input?
Abhijit Gaikwad

Per me, passa solo attraverso questa linea con response = nil. Qualche idea su quale sia il problema?
Peter Webb

4
@PeterWebb - funziona bene nel terminale xcode, cade nel playground :)
aprofromindia

2
readLine è stata aggiunta dopo la maggior parte delle risposte originali, quindi l'atteggiamento è un po 'ingiustificato IMHO
russbishop

2
Corretto per Swift 3, SlimJim
Ezekiel Elin

62

Sono riuscito a capirlo senza scendere in C:

La mia soluzione è la seguente:

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

Le versioni più recenti di Xcode richiedono un typecast esplicito (funziona in Xcode 6.4):

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

5
Mi piace anche questo, può essere compresso fino a una riga se non vuoi definire una funzione, come se devi accettare l'input solo una volta in un programma:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11

3
C'è un modo per usarlo nel Playground per accettare l'input dell'utente al volo?
Rob Cameron

5
Non puoi usarlo nel parco giochi. Devi usare le variabili lì dentro.
Chalkers

8
Nota che otterrai delle nuove righe nella stringa con this. Spogliarli constring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb

3
Otterrai anche caratteri strani se l'utente elimina un carattere, usa un tasto freccia, ecc. AAAGGGGGHHHH WHY, Swift, Why?
ybakos

13

In realtà non è così facile, devi interagire con l'API C. Non c'è alternativa a scanf. Ho costruito un piccolo esempio:

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

Oh ragazzo, vorrei che ci fosse qualcosa di più banale di questo! Trovo difficile adattarlo a una serie di caratteri.
Chalkers

1
Sarebbe meglio creare un "UserInput.h" per memorizzare le definizioni delle funzioni e includere questo file in "cliinput-Bridging-Header.h".
Alan Zhiliang Feng

7

modifica A partire da Swift 2.2 la libreria standard includereadLine . Noterò anche che Swift è passato ai commenti ai documenti markdown. Lasciando la mia risposta originale per il contesto storico.

Solo per completezza, ecco un'implementazione rapida di readln che ho utilizzato. Ha un parametro opzionale per indicare il numero massimo di byte che si desidera leggere (che può essere o meno la lunghezza della stringa).

Questo dimostra anche l'uso corretto dei commenti swiftdoc: Swift genererà un file <progetto> .swiftdoc e Xcode lo userà.

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

Mi piacciono i commenti swiftdoc e il modificatore di accesso "pubblico", non avevo mai visto quel genere di cose in Swift prima, ma CChar e C-String sembrano essere un ritorno ai set di caratteri C e 8 bit e Swift riguarda solo il testo Unicode ... o gli strumenti della riga di comando sono tutti ASCII ??
Kaydell

2
Credo che getchar () restituisca ASCII. I terminali Unicode sono una cosa a cui non volevo entrare :)
russbishop

5

Un'altra alternativa è collegare libedit per la corretta modifica della riga (tasti freccia, ecc.) E il supporto della cronologia opzionale. Lo volevo per un progetto che sto iniziando e ho messo insieme un esempio di base per come l'ho impostato .

Utilizzo da swift

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

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

Wrapper ObjC per esporre 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

In generale, la funzione readLine () viene utilizzata per la scansione dell'input dalla console. Ma non funzionerà nel normale progetto iOS fino a quando oa meno che non si aggiunga "strumento da riga di comando" .

Il modo migliore per i test, puoi fare:

1. Crea un file macOS

inserisci qui la descrizione dell'immagine

2. Utilizzare la funzione readLine () per eseguire la scansione della stringa opzionale dalla console

 import Foundation

 print("Please enter some input\n")

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

Produzione :

Please enter some input

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

inserisci qui la descrizione dell'immagine


3

Ecco un semplice esempio di come ricevere input dall'utente su un'applicazione basata su console: È possibile utilizzare readLine (). Prendi l'input dalla console per il primo numero, quindi premi Invio. Dopodiché, prendi l'input per il secondo numero come mostrato nell'immagine seguente:

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)

Produzione


1

Lo giuro su Dio .. la soluzione a questo problema di base mi è sfuggita per ANNI. È COSÌ semplice .. ma ci sono così tante informazioni vaghe / cattive là fuori; spero di poter salvare qualcuno da alcune delle tane di coniglio senza fondo in cui sono finito ...

Allora, consente di ottenere una "stringa" da "l'utente" tramite "console", via stdin, per così ?

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

se lo vuoi SENZA la nuova riga finale, aggiungi semplicemente ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ


4
Swift, l'OP dice Swift.
HenryRootTwo

1
Ha detto osx. Tutto si compila alla stessa cosa! Abbraccia il tuo ObjC interiore!
Alex Grey,

2
Ci sono persone che scrivono in Swift e non impareranno mai l'obiettivo C. Non si tratta di ciò che compila.
HenryRootTwo

1

Molte risposte obsolete a questa domanda. A partire da Swift 2+, la libreria Swift Standard contiene la funzione readline () . Restituirà un Opzionale ma sarà nullo solo se è stato raggiunto EOF, il che non accadrà quando si riceve l'input dalla tastiera, quindi può essere tranquillamente scartato con la forza in quegli scenari. Se l'utente non inserisce nulla, il suo valore (non confezionato) sarà una stringa vuota. Ecco una piccola funzione di utilità che utilizza la ricorsione per richiedere all'utente fino a quando non è stato inserito almeno un carattere:

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

Si noti che l'uso dell'associazione opzionale (se let input = readLine ()) per verificare se è stato inserito qualcosa come proposto in altre risposte non avrà l'effetto desiderato, poiché non sarà mai nullo e almeno "" quando si accetta l'input da tastiera.

Non funzionerà in un parco giochi o in qualsiasi altro ambiente in cui non si ha accesso al prompt dei comandi. Sembra avere problemi anche nella REPL della riga di comando.


0

Poiché non c'erano soluzioni fantasiose a questo problema, ho creato una piccola classe per leggere e analizzare l'input standard in Swift. Puoi trovarlo qui .

Esempio

Analizzare:

+42 st_ring!
-0.987654321 12345678900
.42

Tu fai:

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

Come importare la classe StreamScanner?
Timothy Swan

@TimothySwan, proprio come spiegato nella sezione Installazione di Readme ( github.com/shoumikhin/StreamScanner#installation ). Quindi in pratica puoi semplicemente aggiungere il file StreamScanner.swift al tuo progetto.
shoumikhin


0

Funziona in xCode v6.2, penso che sia Swift v1.2

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

0

Se vuoi leggere una stringa separata da spazi e dividere immediatamente la stringa in un array, puoi farlo:

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

per esempio.

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

Quando la funzione readLine () viene eseguita su Xcode, la console di debug attende l'input. Il resto del codice verrà ripreso al termine dell'input.

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

0

La prima risposta a questa domanda suggerisce di utilizzare il metodo readLine () per ricevere l'input dell'utente dalla riga di comando. Tuttavia, voglio sottolineare che è necessario utilizzare il! quando si chiama questo metodo per restituire una stringa invece di un opzionale:

var response = readLine()!

Perché lo scartamento forzato readLine()restituisce facoltativo per una ragione, non è quindi sicuro scartare forzatamente e in realtà non aggiunge nulla all'esempio.
Camsoft

0

Swift 5: se desideri continuamente input dalla tastiera, senza terminare il programma, come un flusso di input, utilizza i passaggi seguenti:

  1. Crea un nuovo progetto di tipo comnnad line tool Progetto da riga di comando

    1. Aggiungi sotto il codice nel 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. Esegui il progetto e fai clic sull'eseguibile nella cartella Prodotti in Xcode e aprilo nel Finder
    3. Fare doppio clic sull'eseguibile per aprirlo.
    4. Ora inserisci i tuoi input. Il terminale avrà un aspetto simile a questo: inserisci qui la descrizione dell'immagine


-1

Volevo solo commentare (non ho abbastanza ripetizioni) sull'implementazione di xenadu, perché CCharin OS X lo è Int8, ea Swift non piace affatto quando aggiungi all'array quandogetchar() restituisce parti di UTF-8 o qualsiasi altra cosa sopra i 7 bit.

Sto usando invece un array di UInt8, e funziona benissimo e String.fromCStringconverte il fileUInt8 perfettamente in UTF-8.

Comunque è così che l'ho fatto

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

Ora sono stato in grado di ottenere l'input da tastiera in Swift utilizzando quanto segue:

Nel mio file main.swift ho dichiarato una variabile ie le ho assegnato la funzione GetInt () che ho definito in Objective C. Tramite un cosiddetto Bridging Header dove ho dichiarato il prototipo della funzione per GetInt potevo collegarmi a main.swift. Ecco i file:

main.swift:

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

Intestazione ponte:

#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 è possibile includere l'output e l'input standard c, stdio.h, nonché la libreria standard c stdlib.h che consente di programmare in C in Objective-C, il che significa che non è necessario includere un vero file veloce come user.c o qualcosa del genere.

Spero di poterti aiutare

Modifica: non è possibile ottenere l'input di stringa tramite C perché qui sto usando CInt -> il tipo intero di C e non di Swift. Non esiste un tipo Swift equivalente per il carattere C *. Pertanto String non è convertibile in stringa. Ma ci sono abbastanza soluzioni qui intorno per ottenere l'input di String.

Raul

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.