Wejście z klawiatury w aplikacji wiersza poleceń

94

Próbuję uzyskać dane wejściowe z klawiatury dla aplikacji wiersza poleceń dla nowego języka programowania Apple Swift.

Przeskanowałem dokumenty bezskutecznie.

import Foundation

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

Jakieś pomysły?

Kreda
źródło

Odpowiedzi:

138

Prawidłowym sposobem na to jest użycie readLine z biblioteki Swift Standard Library.

Przykład:

let response = readLine()

Podaje opcjonalną wartość zawierającą wprowadzony tekst.

Ezekiel Elin
źródło
2
dzięki. czy istnieje sposób na uzyskanie hasła?
Abhijit Gaikwad,
U mnie po prostu przechodzi przez tę linię z odpowiedzią = nil. Masz pojęcie, na czym polega problem?
Peter Webb,
4
@PeterWebb - działa dobrze w terminalu xcode, wypada na placu zabaw :)
aprofromindia
2
readLine zostało dodane po większości oryginalnych odpowiedzi, więc podejście jest nieco nieuzasadnione. IMHO
russbishop
2
Poprawione dla Swift 3, SlimJim
Ezekiel Elin
62

Udało mi się to rozgryźć bez schodzenia do C:

Moje rozwiązanie jest następujące:

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

Nowsze wersje Xcode wymagają jawnego typecast (działa w Xcode 6.4):

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}
Kreda
źródło
5
Podoba mi się to również, można to skompresować do jednej linii, jeśli nie chcesz definiować funkcji, na przykład, jeśli musisz zaakceptować dane wejściowe tylko raz w programie:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11
3
Czy istnieje sposób użycia tego w Playground do akceptowania danych wejściowych użytkownika w locie?
Rob Cameron,
5
Nie możesz tego używać na placu zabaw. Musisz tam użyć zmiennych.
Chalkers
8
Zauważ, że w tym ciągu otrzymasz znaki nowej linii. Pozbądź się ich zstring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb
3
Otrzymasz również dziwne znaki, jeśli użytkownik usunie znak, użyje klawisza strzałki itp. AAAGGGGGHHHH DLACZEGO, Szybki, dlaczego?
ybakos
14

W rzeczywistości nie jest to takie proste, musisz współdziałać z interfejsem API języka C. Nie ma alternatywy dlascanf . Zbudowałem mały przykład:

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
źródło
O rany - szkoda, że ​​nie było czegoś bardziej trywialnego niż to! Trudno jest dostosować to do ciągu znaków.
Chalkers
1
Byłoby lepiej utworzyć plik „UserInput.h” do przechowywania definicji funkcji i dołączyć ten plik do „cliinput-Bridging-Header.h”.
Alan Zhiliang Feng
7

edytuj Od wersji Swift 2.2 standardowa biblioteka zawiera readLine. Zwrócę również uwagę, że Swift przełączył się na komentarze docenione w docenie. Pozostawiając moją oryginalną odpowiedź na kontekst historyczny.

Dla kompletności, oto szybka implementacja, z readlnktórej korzystałem. Ma opcjonalny parametr wskazujący maksymalną liczbę bajtów, które chcesz odczytać (która może, ale nie musi, być długością ciągu).

Pokazuje to również prawidłowe użycie komentarzy swiftdoc - Swift wygeneruje plik <project> .swiftdoc i Xcode go użyje.

///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
źródło
Lubię komentarze swiftdoc i "publiczny" modyfikator dostępu, nie widziałem wcześniej tego typu rzeczy w Swift, ale CChar i C-String wydają się być powrotem do C i 8-bitowych zestawów znaków oraz Swift dotyczy tylko tekstu Unicode ... czy też narzędzia wiersza poleceń są w całości w ASCII?
Kaydell
2
Uważam, że getchar () zwraca ASCII. Terminale Unicode to rzecz, w którą nie chciałem się
zagłębiać
7

Ogólnie funkcja readLine () służy do skanowania danych wejściowych z konsoli. Ale nie będzie działać w normalnym projekcie iOS, dopóki nie dodasz „narzędzia wiersza poleceń” .

Najlepszym sposobem na testowanie jest:

1. Utwórz plik macOS

wprowadź opis obrazu tutaj

2. Użyj funkcji readLine (), aby przeskanować opcjonalny ciąg z konsoli

 import Foundation

 print("Please enter some input\n")

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

Wynik :

Please enter some input

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

wprowadź opis obrazu tutaj

Ashis Laha
źródło
5

Inną alternatywą jest połączenie libedit w celu poprawnej edycji linii (klawisze strzałek itp.) I opcjonalnej obsługi historii. Chciałem tego dla projektu, który zaczynam, i przygotowałem podstawowy przykład, jak go skonfigurować .

Wykorzystanie od swift

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

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

Opakowanie ObjC do ujawnienia 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
źródło
3

Oto prosty przykład pobierania danych wejściowych od użytkownika w aplikacji opartej na konsoli: Możesz użyć readLine (). Weź dane wejściowe z konsoli dla pierwszej liczby, a następnie naciśnij enter. Następnie wprowadź drugą liczbę, jak pokazano na poniższym obrazku:

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)

Wynik

Shehzad Ali
źródło
2

Wiele nieaktualnych odpowiedzi na to pytanie. Od wersji Swift 2+ biblioteka Swift Standard zawiera funkcję readline () . Zwróci wartość Optional, ale będzie wynosić zero tylko wtedy, gdy osiągnięto EOF, co nie nastąpi podczas pobierania danych z klawiatury, aby można było bezpiecznie rozpakować siłą w tych scenariuszach. Jeśli użytkownik nic nie wprowadzi, jego (nieopakowana) wartość będzie pustym ciągiem. Oto mała funkcja narzędzia, która używa rekurencji, aby monitować użytkownika, dopóki nie zostanie wprowadzony co najmniej jeden znak:

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

Zauważ, że użycie opcjonalnego wiązania (jeśli let input = readLine ()) do sprawdzenia, czy coś zostało wprowadzone zgodnie z propozycją w innych odpowiedziach, nie przyniesie pożądanego efektu, ponieważ nigdy nie będzie zerowe i przynajmniej "" przy akceptacji wprowadzania z klawiatury.

To nie zadziała na placu zabaw ani w żadnym innym środowisku, w którym nie masz dostępu do wiersza polecenia. Wydaje się, że ma również problemy z poleceniem REPL w wierszu poleceń.

Andreas Bergström
źródło
1

Przysięgam na Boga… rozwiązanie tego całkowicie podstawowego problemu umykało mi przez LATA. To jest TAKIE proste ... ale jest tam tak wiele niejasnych / złych informacji; mam nadzieję, że uda mi się uratować kogoś przed niektórymi bezdennymi króliczymi norami, w których znalazłem ...

Więc weźmy „string” od „użytkownika” poprzez „konsolę”, poprzez stdin, dobrze ?

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

jeśli chcesz to BEZ końcowego znaku nowej linii, po prostu dodaj ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ

Alex Gray
źródło
4
Swift, OP mówi Swift.
HenryRootTwo
1
Powiedział osx. Wszystko sprowadza się do tego samego! Przyjmij swój wewnętrzny cel!
Alex Grey,
2
Są ludzie, którzy piszą w Swift i nigdy nie nauczą się Celu C. Nie chodzi o to, do czego się składa.
HenryRootTwo
1

Swift 5: Jeśli ciągle chcesz wprowadzać dane z klawiatury, bez kończenia programu, jak strumień danych wejściowych, wykonaj poniższe czynności:

  1. Utwórz nowy projekt typu comnnad line tool Projekt wiersza poleceń

    1. Dodaj poniższy kod w pliku 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. Uruchom Projekt i kliknij plik wykonywalny w folderze Produkty w Xcode i otwórz w wyszukiwarce
    3. Kliknij dwukrotnie plik wykonywalny, aby go otworzyć.
    4. Teraz wprowadź swoje dane wejściowe. Terminal będzie wyglądał mniej więcej tak: wprowadź opis obrazu tutaj
Shikha Budhiraja
źródło
0

Ponieważ nie było wyszukanych rozwiązań tego problemu, utworzyłem małą klasę do odczytywania i analizowania standardowego wejścia w języku Swift. Znajdziesz go tutaj .

Przykład

Do analizowania:

+42 st_ring!
-0.987654321 12345678900
.42

Ty robisz:

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
źródło
Jak zaimportować klasę StreamScanner?
Timothy Swan
@TimothySwan, tak jak wyjaśniono w sekcji Instalacja pliku Readme ( github.com/shoumikhin/StreamScanner#installation ). Więc w zasadzie możesz po prostu dodać plik StreamScanner.swift do swojego projektu.
shoumikhin
0

Przed

wprowadź opis obrazu tutaj

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

Korekta

wprowadź opis obrazu tutaj

Murphy
źródło
0

Działa to w xCode v6.2, myślę, że to Swift v1.2

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}
Gigantyczny łoś
źródło
0

Jeśli chcesz odczytać ciąg oddzielony spacjami i natychmiast podzielić ciąg na tablicę, możesz to zrobić:

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

na przykład.

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
źródło
0

Gdy funkcja readLine () jest uruchamiana w Xcode, konsola debugowania czeka na dane wejściowe. Pozostała część kodu zostanie wznowiona po zakończeniu wprowadzania danych.

    let inputStr = readLine()
    if let inputStr = inputStr {
        print(inputStr)
    }
Sourabh Shekhar
źródło
0

Najwyżej sklasyfikowana odpowiedź na to pytanie sugeruje użycie metody readLine () do pobierania danych wejściowych użytkownika z wiersza poleceń. Chciałbym jednak zauważyć, że musisz użyć! operator podczas wywoływania tej metody w celu zwrócenia ciągu znaków zamiast opcjonalnego:

var response = readLine()!
topherPedersen
źródło
Dlaczego wymuszone rozpakowanie, readLine()z jakiegoś powodu zwraca opcjonalne, dlatego nie jest bezpieczne wymuszanie rozwijania i tak naprawdę nie dodaje niczego do przykładu.
Camsoft
0
var a;
scanf("%s\n", n);

Przetestowałem to w ObjC i może to się przyda.


źródło
-1

Chciałem tylko skomentować (nie mam wystarczającej liczby powtórzeń) na temat implementacji xenadu, bo CCharw OS X jest Int8, a Swift w ogóle nie lubi gdy dodajesz do tablicy kiedygetchar() zwraca części UTF-8 lub cokolwiek innego powyżej 7 bitów.

UInt8Zamiast tego używam tablicy i działa świetnie i String.fromCStringkonwertuje UInt8do UTF-8 po prostu dobrze.

Jednak tak to zrobiłem

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
źródło
-1

Mogłem teraz uzyskać dane wejściowe z klawiatury w Swift, używając:

W moim pliku main.swift zadeklarowałem zmienną i i przypisałem do niej funkcję GetInt (), którą zdefiniowałem w Objective C. Poprzez tzw. Bridging Header, w którym zadeklarowałem prototyp funkcji dla GetInt, mogę połączyć się z main.swift. Oto pliki:

main.swift:

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

Nagłówek mostkujący:

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

W obj.m możliwe jest dołączenie standardowego wyjścia i wejścia c, stdio.h, a także standardowej biblioteki c stdlib.h, która umożliwia programowanie w C w Objective-C, co oznacza, że ​​nie ma potrzeby dołączania prawdziwy szybki plik, taki jak user.c lub coś w tym rodzaju.

Mam nadzieję, że mógłbym pomóc

Edycja: Nie jest możliwe uzyskanie danych wejściowych typu String przez C, ponieważ tutaj używam CInt -> typu całkowitego C, a nie Swift. Nie ma odpowiednika typu Swift dla znaku C *. Dlatego String nie jest konwertowany na łańcuch. Ale jest tutaj wystarczająco dużo rozwiązań, aby uzyskać dane wejściowe typu String.

Raul

Raul Rao
źródło