Entrada desde el teclado en la aplicación de línea de comando

Estoy tratando de obtener la entrada del teclado para una aplicación de línea de comandos para el nuevo lenguaje de progtwigción de Apple Swift.

He escaneado los documentos en vano.

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

¿Algunas ideas?

La forma correcta de hacerlo es usar readLine , de la Biblioteca estándar de Swift.

Ejemplo:

 let response = readLine() 

Le dará un valor opcional que contiene el texto ingresado.

Me las arreglé para resolverlo sin caer en C:

Mi solución es la siguiente:

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

Las versiones más recientes de Xcode necesitan una conversión explícita (funciona en Xcode 6.4):

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

De hecho, no es tan fácil, tienes que interactuar con la API C. No hay alternativa a scanf . He construido un pequeño ejemplo:

main.swift

 import Foundation var output: CInt = 0 getInput(&output) println(output) 

UserInput.c

 #include  void getInput(int *output) { scanf("%i", output); } 

cliinput-Bridging-Header.h

 void getInput(int *output); 

editar A partir de Swift 2.2, la biblioteca estándar incluye readLine . También notaré que Swift cambió a comentarios de doc. Dejando mi respuesta original para el contexto histórico.

Solo para completar, aquí hay una implementación de Swift de readln que he estado usando. Tiene un parámetro opcional para indicar el número máximo de bytes que desea leer (que puede ser o no la longitud de la Cadena).

Esto también demuestra el uso correcto de los comentarios de swiftdoc: Swift generará un archivo .swiftdoc y Xcode lo usará.

 ///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 = [] 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) } } 

Otra alternativa es vincular libedit para una correcta edición de línea (teclas de flecha, etc.) y soporte de historial opcional. Quería esto para un proyecto que estoy comenzando y armé un ejemplo básico de cómo lo configuré .

Uso de veloz

 let prompt: Prompt = Prompt(argv0: C_ARGV[0]) while (true) { if let line = prompt.gets() { print("You typed \(line)") } } 

ObjC wrapper para exponer libedit

 #import  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 

En general, la función readLine () se usa para escanear entradas desde la consola. Pero no funcionará en el proyecto normal de iOS hasta que, a menos que agregue una “herramienta de línea de comandos” .

La mejor forma de probar, puedes hacer:

1. Crea un archivo macOS

enter image description here

2. Utilice el func readLine () para escanear String opcional de la consola

  import Foundation print("Please enter some input\n") if let response = readLine() { print("output :",response) } else { print("Nothing") } 

Salida:

 Please enter some input Hello, World output : Hello, World Program ended with exit code: 0 

enter image description here

Aquí hay un ejemplo simple de cómo tomar entrada del usuario en una aplicación basada en consola: Puede usar readLine (). Toma la entrada de la consola para el primer número y luego presiona enter. Después de eso, tome la entrada para el segundo número como se muestra en la imagen a continuación:

 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) 

Salida

La respuesta mejor clasificada a esta pregunta sugiere usar el método readLine () para tomar la entrada del usuario desde la línea de comando. Sin embargo, quiero señalar que necesitas usar el! operador cuando llama a este método para devolver una cadena en lugar de una opción:

 var response = readLine()! 

Como no había soluciones sofisticadas para este problema, hice una pequeña clase para leer y analizar la entrada estándar en Swift. Puedes encontrarlo aquí .

Ejemplo

Analizar:

 +42 st_ring! -0.987654321 12345678900 .42 

Tú lo haces:

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

Lo juro por Dios … la solución a este problema completamente básico se me escapó por AÑOS. Es TAN simple … pero hay mucha información vaga / mala por ahí; con suerte puedo salvar a alguien de algunos de los agujeros de conejo sin fondo en los que terminé …

Entonces, vamos a obtener una “cadena” de “el usuario” a través de “la consola”, vía stdin , ¿de acuerdo ?

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

si lo quiere SIN la nueva línea final, solo agregue …

 [ ... stringByTrimmingCharactersInSet: NSCharacterSet.newlineCharacterSet]; 

Ta Da! ♥ ⱥ ᏪℯⅩ

antes de

enter image description here

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

Corrección

enter image description here

Esto funciona en xCode v6.2, creo que es Swift v1.2

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

Si desea leer cadenas separadas por espacios e inmediatamente dividir la cadena en una matriz, puede hacer esto:

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

p.ej.

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

Cuando la función readLine () se ejecuta en Xcode, la consola de depuración espera la entrada. El rest del código se reanudará después de hacer la entrada.

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

Solo quería comentar (no tengo suficientes representantes) sobre la implementación de xenadu, porque CChar en OS X es Int8 , y Swift no le gusta en absoluto cuando se agrega a la matriz cuando getchar() devuelve partes de UTF-8, o cualquier cosa más de 7 bits

Estoy utilizando una matriz de UInt8 en UInt8 lugar, y funciona muy bien y String.fromCString convierte el UInt8 en UTF-8 muy bien.

Sin embargo, así es como lo hice

 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(cstr)) } while true { if let mystring = readln().str { println(" > \(mystring)") } } 

Ahora he podido obtener la entrada del teclado en Swift usando lo siguiente:

En mi archivo main.swift, declare una variable iy le asigné la función GetInt () que definí en Objective C. A través de un llamado Bridging Header, donde declare el prototipo de función para GetInt, podría enlazar con main.swift. Aquí están los archivos:

main.swift:

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

Encabezado de puente:

 #include "obj.m" int GetInt(); 

obj.m:

 #import  #import  #import  int GetInt() { int i; scanf("%i", &i); return i; } 

En obj.m es posible incluir la entrada y la salida estándar c, stdio.h, así como la biblioteca estándar c stdlib.h que le permite progtwigr en C en Objective-C, lo que significa que no hay necesidad de incluir un archivo rápido como user.c o algo así.

Espero poder ayudar,

Editar: No es posible obtener la entrada de cadena a través de C porque aquí estoy usando el CInt -> el tipo entero de C y no de Swift. No hay un tipo de Swift equivalente para el C char *. Por lo tanto, String no es convertible a cadena. Pero hay suficientes soluciones por aquí para obtener la entrada de String.

Raúl