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?
Odpowiedzi:
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.
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
}
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 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);
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) }
}
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:
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
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
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)
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ń.
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!
♥ ⱥ ᏪℯⅩ
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:")
}
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"
}
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
}
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)")
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)
}
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()!
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.
Chciałem tylko skomentować (nie mam wystarczającej liczby powtórzeń) na temat implementacji xenadu, bo CChar
w 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.
UInt8
Zamiast tego używam tablicy i działa świetnie i String.fromCString
konwertuje UInt8
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)")
}
}
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