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

93

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

He escaneado los documentos en vano.

import Foundation

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

¿Algunas ideas?

Chalkers
fuente

Respuestas:

137

La forma correcta de hacer esto es usar readLine, de la biblioteca estándar de Swift.

Ejemplo:

let response = readLine()

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

Ezequiel Elin
fuente
2
Gracias. ¿Hay alguna manera de obtener el tipo de entrada de contraseña?
Abhijit Gaikwad
Para mí, simplemente pasa por esta línea con response = nil. ¿Alguna idea de cuál es el problema?
Peter Webb
4
@PeterWebb - funciona bien en la terminal xcode, falla en el patio de recreo :)
aprofromindia
2
readLine se agregó después de la mayoría de las respuestas originales, por lo que la actitud es un poco injustificada en mi humilde opinión
russbishop
2
Corregido para Swift 3, SlimJim
Ezekiel Elin
62

Me las arreglé para resolverlo sin bajar a 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 un encasillado explícito (funciona en Xcode 6.4):

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}
Chalkers
fuente
5
Me gusta esto, también, se puede comprimir a una línea si no desea definir una función, como si solo necesita aceptar la entrada una vez en un programa:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11
3
¿Hay alguna forma de usar esto en el patio de juegos para aceptar la entrada del usuario sobre la marcha?
Rob Cameron
5
No puedes usar esto en el patio de recreo. Tienes que usar variables allí.
Chalkers
8
Tenga en cuenta que obtendrá nuevas líneas en la cadena con esto. Quítelos constring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb
3
También obtendrás caracteres extraños si el usuario elimina un personaje, usa una tecla de flecha, etc. AAAGGGGGHHHH ¿POR QUÉ, Swift, Por qué?
ybakos
13

En realidad, no es tan fácil, tienes que interactuar con la API de 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 <stdio.h>

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


cliinput-Bridging-Header.h

void getInput(int *output);
Leandros
fuente
Dios mío, ¡desearía que hubiera algo más trivial que esto! Me resulta difícil adaptar esto para una serie de personajes.
Chalkers
1
Sería mejor crear un "UserInput.h" para almacenar las definiciones de funciones, e incluir este archivo en "cliinput-Bridging-Header.h".
Alan Zhiliang Feng
7

editar A partir de Swift 2.2, la biblioteca estándar incluyereadLine . También señalaré que Swift cambió a comentarios de documentos de rebajas. Dejando mi respuesta original para el contexto histórico.

Solo para completar, aquí hay una implementación de Swift readlnque 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 adecuado de los comentarios de swiftdoc: Swift generará un archivo <project> .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<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) }
}
obispo ruso
fuente
Me gustan los comentarios de swiftdoc y el modificador de acceso "público", no había visto ese tipo de cosas en Swift antes, pero CChar y C-String parecen ser un retroceso a los conjuntos de caracteres C y 8 ​​bits y Swift se trata de texto Unicode ... ¿o las herramientas de línea de comandos son todas ASCII?
Kaydell
2
Creo que getchar () devuelve ASCII. Las terminales Unicode son algo en lo que no quería entrar :)
russbishop
6

En general, la función readLine () se usa para escanear la entrada desde la consola. Pero no funcionará en un proyecto iOS normal hasta que agregue una "herramienta de línea de comandos" .

La mejor forma de realizar la prueba es:

1. Crea un archivo macOS

ingrese la descripción de la imagen aquí

2. Utilice la función readLine () para escanear una cadena opcional desde 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

ingrese la descripción de la imagen aquí

Ashis Laha
fuente
5

Otra alternativa es vincular libedit para la edición de línea adecuada (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 Swift

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

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

Contenedor de ObjC para exponer 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
fuente
3

A continuación, se muestra un ejemplo simple de cómo recibir información del usuario en una aplicación basada en consola: Puede usar readLine (). Tome la entrada de la consola para el primer número y luego presione 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

Shehzad Ali
fuente
2

Muchas respuestas obsoletas a esta pregunta. A partir de Swift 2+, la biblioteca estándar de Swift contiene la función readline () . Devolverá un Opcional, pero solo será nulo si se ha alcanzado EOF, lo que no sucederá cuando se obtenga una entrada desde el teclado, por lo que se puede desenvolver con seguridad por la fuerza en esos escenarios. Si el usuario no ingresa nada, su valor (sin empaquetar) será una cadena vacía. Aquí hay una pequeña función de utilidad que usa la recursividad para avisar al usuario hasta que se haya ingresado al menos un carácter:

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

Tenga en cuenta que el uso de enlace opcional (si deja input = readLine ()) para verificar si se ingresó algo como se propuso en otras respuestas no tendrá el efecto deseado, ya que nunca será nulo y al menos "" al aceptar la entrada del teclado.

Esto no funcionará en un patio de juegos ni en ningún otro entorno donde no tenga acceso al símbolo del sistema. También parece tener problemas en la línea de comandos REPL.

Andreas Bergström
fuente
1

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

Entonces, obtengamos una "cadena" del "usuario" a través de "la consola", a través stdin, ¿de acuerdo ?

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

si lo desea SIN la nueva línea final, simplemente agregue ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ

Alex Grey
fuente
4
Swift, el OP dice Swift.
HenryRootTwo
1
Dijo osx. ¡Todo se resume en lo mismo! ¡Abraza tu ObjC interior!
Alex Gray
2
Hay personas que escriben en Swift y nunca aprenderán el Objetivo C. No se trata de lo que compila.
HenryRootTwo
0

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"
}
shoumikhin
fuente
¿Cómo importar la clase StreamScanner?
Timothy Swan
@TimothySwan, tal como se explica en la sección Instalación del archivo Léame ( github.com/shoumikhin/StreamScanner#installation ). Básicamente, puede agregar el archivo StreamScanner.swift a su proyecto.
shoumikhin
0

antes de

ingrese la descripción de la imagen aquí

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

Corrección

ingrese la descripción de la imagen aquí

Murphy
fuente
0

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
}
Alce gigante
fuente
0

Si desea leer una cadena separada 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)")
Brian Ho
fuente
0

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

    let inputStr = readLine()
    if let inputStr = inputStr {
        print(inputStr)
    }
Sourabh Shekhar
fuente
0

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 debes usar el! operador al llamar a este método para devolver una cadena en lugar de un opcional:

var response = readLine()!
topherPedersen
fuente
Por qué el desenvolver forzado, readLine()devuelve opcional por una razón, por lo tanto, no es seguro desenvolver a la fuerza y ​​realmente no agrega nada al ejemplo.
Camsoft
0

Swift 5: si continuamente desea ingresar desde el teclado, sin finalizar el programa, como un flujo de entrada, siga los pasos a continuación:

  1. Crear nuevo proyecto de tipo comnnad line tool Proyecto de línea de comando

    1. Agregue el siguiente código en el archivo 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. Ejecute el proyecto y haga clic en el ejecutable en la carpeta Productos en Xcode y ábralo en el buscador
    3. Haga doble clic en el ejecutable para abrirlo.
    4. Ahora ingrese sus Entradas. La terminal se verá así: ingrese la descripción de la imagen aquí
Shikha Budhiraja
fuente
0
var a;
scanf("%s\n", n);

Probé esto en ObjC, y tal vez sea útil.


fuente
-1

Solo quería comentar (no tengo suficientes repeticiones) sobre la implementación de xenadu, porque CCharen OS X lo es Int8, y a Swift no le gusta en absoluto cuando agregas a la matriz cuandogetchar() devuelve partes de UTF-8, o cualquier otra cosa por encima de 7 bits.

Estoy usando una matriz de en su UInt8lugar, y funciona muy bien y String.fromCStringconvierte el UInt8en 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<CChar>(cstr))
}

while true {
    if let mystring = readln().str {
        println(" > \(mystring)")
    }
}
aredigg
fuente
-1

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

En mi archivo main.swift, declaré una variable i y le asigné la función GetInt () que definí en el Objetivo C. A través de un encabezado puente donde declaré el prototipo de función para GetInt, pude vincularlo a main.swift. Aquí están los archivos:

main.swift:

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

Encabezado puente:

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

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

Espero poder ayudar

Editar: No es posible obtener una entrada de cadena a través de C porque aquí estoy usando CInt -> el tipo entero de C y no de Swift. No existe un tipo Swift equivalente para el carácter C *. Por lo tanto, String no se puede convertir en string. Pero hay suficientes soluciones por aquí para obtener la entrada de String.

Raul

Raul Rao
fuente