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?
swift
macos
cocoa
command-line
Kreda
źródło
źródło
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 }
źródło
var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
W rzeczywistości nie jest to takie proste, musisz współdziałać z interfejsem API języka C. Nie ma alternatywy dla
scanf
. 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);
źródło
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
readln
któ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) } }
źródło
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
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
źródło
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
źródło
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)
źródło
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ń.
źródło
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!
♥ ⱥ ᏪℯⅩźródło
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:
Utwórz nowy projekt typu comnnad line tool
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:") }
źródło
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" }
źródło
Przed
*******************.
Korekta
źródło
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 }
źródło
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)")
źródło
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) }
źródło
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()!
źródło
readLine()
z jakiegoś powodu zwraca opcjonalne, dlatego nie jest bezpieczne wymuszanie rozwijania i tak naprawdę nie dodaje niczego do przykładu.var a; scanf("%s\n", n);
Przetestowałem to w ObjC i może to się przyda.
źródło
Chciałem tylko skomentować (nie mam wystarczającej liczby powtórzeń) na temat implementacji xenadu, bo
CChar
w OS X jestInt8
, 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.UInt8
Zamiast tego używam tablicy i działa świetnie iString.fromCString
konwertujeUInt8
do 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)") } }
źródło
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
źródło