J'essaie d'obtenir l'entrée clavier pour une application de ligne de commande pour le nouveau langage de programmation Apple Swift.
J'ai scanné les documents en vain.
import Foundation
println("What is your name?")
???
Des idées?
Réponses:
La bonne façon de faire est d'utiliser readLine
, à partir de la bibliothèque standard Swift.
Exemple:
let response = readLine()
Vous donnera une valeur facultative contenant le texte saisi.
J'ai réussi à le comprendre sans tomber en C:
Ma solution est la suivante:
func input() -> String {
var keyboard = NSFileHandle.fileHandleWithStandardInput()
var inputData = keyboard.availableData
return NSString(data: inputData, encoding:NSUTF8StringEncoding)!
}
Les versions plus récentes de Xcode nécessitent un typage explicite (fonctionne dans 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())
Ce n'est en fait pas si simple, vous devez interagir avec l'API C. Il n'y a pas d'alternative à scanf
. J'ai construit un petit exemple:
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);
edit Depuis Swift 2.2, la bibliothèque standard comprend readLine
. Je noterai également que Swift est passé aux commentaires de document de démarque. Laissant ma réponse originale pour le contexte historique.
Juste pour être complet, voici une implémentation Swift que readln
j'utilise. Il a un paramètre facultatif pour indiquer le nombre maximal d'octets que vous souhaitez lire (qui peut ou non être la longueur de la chaîne).
Cela démontre également l'utilisation correcte des commentaires swiftdoc - Swift générera un fichier <project> .swiftdoc et Xcode l'utilisera.
///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) }
}
En général, la fonction readLine () est utilisée pour analyser les entrées de la console. Mais cela ne fonctionnera pas dans un projet iOS normal tant que vous n'aurez pas ajouté "outil de ligne de commande" .
La meilleure façon de tester, vous pouvez faire:
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
Une autre alternative est de lier libedit pour une édition correcte des lignes (touches fléchées, etc.) et une prise en charge facultative de l'historique. Je voulais cela pour un projet que je démarre et j'ai rassemblé un exemple de base pour la façon dont je l'ai mis en place .
Utilisation de swift
let prompt: Prompt = Prompt(argv0: C_ARGV[0])
while (true) {
if let line = prompt.gets() {
print("You typed \(line)")
}
}
Wrapper ObjC pour exposer 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
Voici un exemple simple de prise d'entrée de l'utilisateur sur une application basée sur la console: Vous pouvez utiliser readLine (). Prenez l'entrée de la console pour le premier numéro, puis appuyez sur Entrée. Après cela, saisissez le deuxième numéro comme indiqué dans l'image ci-dessous:
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)
Beaucoup de réponses dépassées à cette question. Depuis Swift 2+, la bibliothèque standard Swift contient la fonction readline () . Il renverra un Option mais il ne sera nul que si EOF a été atteint, ce qui ne se produira pas lors de l'obtention d'une entrée du clavier afin qu'il puisse être déballé en toute sécurité par la force dans ces scénarios. Si l'utilisateur n'entre rien, sa valeur (non emballée) sera une chaîne vide. Voici une petite fonction utilitaire qui utilise la récursivité pour inviter l'utilisateur jusqu'à ce qu'au moins un caractère ait été saisi:
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)")
Notez que l'utilisation de la liaison optionnelle (si let input = readLine ()) pour vérifier si quelque chose a été saisi comme proposé dans d'autres réponses n'aura pas l'effet souhaité, car il ne sera jamais nul et au moins "" lors de l'acceptation d'une saisie au clavier.
Cela ne fonctionnera pas dans un Playground ou dans tout autre environnement où vous n'avez pas accès à l'invite de commande. Il semble également avoir des problèmes dans la ligne de commande REPL.
Je jure devant Dieu ... la solution à ce problème tout à fait fondamental m'a échappé pendant des ANNÉES. C'est tellement simple ... mais il y a tellement d'informations vagues / mauvaises là-bas; j'espère pouvoir sauver quelqu'un de certains des terriers sans fond dans lesquels je me suis retrouvé ...
Alors, obtenons une "chaîne" de "l'utilisateur" via "la console", via stdin
, d' accord ?
[NSString.alloc initWithData:
[NSFileHandle.fileHandleWithStandardInput availableData]
encoding:NSUTF8StringEncoding];
si vous le voulez SANS la nouvelle ligne de fin, ajoutez simplement ...
[ ... stringByTrimmingCharactersInSet:
NSCharacterSet.newlineCharacterSet];
Ta Da!
♥ ⱥ ᏪℯⅩ
Comme il n'y avait pas de solution sophistiquée à ce problème, j'ai créé une petite classe pour lire et analyser l'entrée standard dans Swift. Vous pouvez le trouver ici .
Exemple
Pour analyser:
+42 st_ring!
-0.987654321 12345678900
.42
Tu fais:
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"
}
Cela fonctionne dans xCode v6.2, je pense que c'est Swift v1.2
func input() -> String {
var keyboard = NSFileHandle.fileHandleWithStandardInput()
var inputData = keyboard.availableData
return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}
Si vous souhaitez lire une chaîne séparée par des espaces et diviser immédiatement la chaîne en un tableau, vous pouvez le faire:
var arr = readLine()!.characters.split(" ").map(String.init)
par exemple.
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)")
Lorsque la fonction readLine () est exécutée sur Xcode, la console de débogage attend l'entrée. Le reste du code reprendra une fois la saisie terminée.
let inputStr = readLine()
if let inputStr = inputStr {
print(inputStr)
}
La réponse la mieux classée à cette question suggère d'utiliser la méthode readLine () pour prendre en compte les entrées de l'utilisateur à partir de la ligne de commande. Cependant, je tiens à noter que vous devez utiliser le! opérateur lors de l'appel de cette méthode pour renvoyer une chaîne au lieu d'un optionnel:
var response = readLine()!
readLine()
retourne optionnel pour une raison, il est donc dangereux de forcer le déballage et n'ajoute vraiment rien à l'exemple.
Swift 5: Si vous voulez en permanence une entrée depuis le clavier, sans terminer le programme, comme un flux d'entrée, utilisez les étapes ci-dessous:
Créer un nouveau projet de type outil de ligne comnnad
Ajoutez le code ci-dessous dans le fichier 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:")
}
var a;
scanf("%s\n", n);
J'ai testé cela dans ObjC, et peut-être que ce sera utile.
Je voulais juste commenter (je n'ai pas assez de représentants) sur l'implémentation de xenadu, car CChar
sous OS X est Int8
, et Swift n'aime pas du tout lorsque vous ajoutez au tableau lorsque vous getchar()
retournez des parties de UTF-8, ou toute autre chose au-dessus de 7 bits.
J'utilise un tableau de à la UInt8
place, et cela fonctionne très bien et String.fromCString
convertit très bien le UInt8
en UTF-8.
Cependant c'est comme ça que je l'ai fait
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)")
}
}
J'ai maintenant pu obtenir une entrée clavier dans Swift en utilisant ce qui suit:
Dans mon fichier main.swift, j'ai déclaré une variable i et lui ai assigné la fonction GetInt () que j'ai définie dans l'Objectif C. Grâce à ce qu'on appelle un en-tête de pont où j'ai déclaré le prototype de fonction pour GetInt, je pouvais créer un lien vers main.swift. Voici les fichiers:
main.swift:
var i: CInt = GetInt()
println("Your input is \(i) ");
En-tête de pontage:
#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;
}
Dans obj.m, il est possible d'inclure la sortie et l'entrée standard c, stdio.h, ainsi que la bibliothèque standard c stdlib.h qui vous permet de programmer en C dans Objective-C, ce qui signifie qu'il n'est pas nécessaire d'inclure un vrai fichier rapide comme user.c ou quelque chose comme ça.
J'espère que je pourrais aider
Edit: Il n'est pas possible d'obtenir une entrée String via C car ici j'utilise le CInt -> le type entier de C et non de Swift. Il n'y a pas de type Swift équivalent pour le caractère C *. Par conséquent, String n'est pas convertible en chaîne. Mais il y a assez de solutions ici pour obtenir une entrée String.
Raul