Entrada do teclado no aplicativo de linha de comando

91

Estou tentando obter a entrada do teclado para um aplicativo de linha de comando para a nova linguagem de programação Swift da Apple.

Eu examinei os documentos sem sucesso.

import Foundation

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

Alguma ideia?

Chalkers
fonte

Respostas:

135

A maneira correta de fazer isso é usar readLinea Biblioteca padrão do Swift.

Exemplo:

let response = readLine()

Fornecerá um valor opcional contendo o texto inserido.

Ezekiel Elin
fonte
2
obrigado. existe uma maneira de obter o tipo de entrada de senha?
Abhijit Gaikwad de
Para mim, ele simplesmente percorre essa linha com resposta = nil. Alguma ideia sobre qual é o problema?
Peter Webb
4
@PeterWebb - funciona bem no terminal xcode, falha no playground :)
aprofromindia
2
readLine foi adicionado após a maioria das respostas originais, então a atitude é um pouco injustificada IMHO
russbishop
2
Corrigido para Swift 3, SlimJim
Ezekiel Elin
62

Eu consegui descobrir sem cair no C:

Minha solução é a seguinte:

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

Versões mais recentes do Xcode precisam de um typecast explícito (funciona no Xcode 6.4):

func input() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)! as String
}
Chalkers
fonte
5
Eu gosto disso, também, pode ser compactado em uma linha se você não quiser definir uma função, como se você só precisa aceitar entradas uma vez em um programa:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11
3
Existe uma maneira de usar isso no Playground para aceitar a entrada do usuário em tempo real?
Rob Cameron
5
Você não pode usar isso no playground. Você tem que usar variáveis ​​lá.
Chalkers
8
Observe que você obterá novas linhas na string com isso. Retire-os comstring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb
3
Você também obterá caracteres estranhos se o usuário excluir um caractere, usar uma tecla de seta, etc. AAAGGGGGHHHH POR QUE, Swift, Por quê?
ybakos de
13

Na verdade não é tão fácil, você tem que interagir com a API C. Não há alternativa para scanf. Eu construí um pequeno exemplo:

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
fonte
Oh cara - eu gostaria que houvesse algo mais trivial do que isso! Estou achando difícil adaptar isso para uma sequência de caracteres.
Chalkers
1
Seria melhor criar um "UserInput.h" para armazenar as definições de função e incluir esse arquivo em "cliinput-Bridging-Header.h".
Alan Zhiliang Feng
7

editar A partir do Swift 2.2, a biblioteca padrão inclui readLine. Também observarei que o Swift mudou para comentários de documentos em markdown. Deixando minha resposta original para o contexto histórico.

Apenas para completar, aqui está uma implementação do Swift readlnque venho usando. Possui um parâmetro opcional para indicar o número máximo de bytes que você deseja ler (que pode ou não ser o comprimento da String).

Isso também demonstra o uso adequado dos comentários do swiftdoc - o Swift irá gerar um arquivo <project> .swiftdoc e o Xcode irá usá-lo.

///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) }
}
Russbishop
fonte
Eu gosto dos comentários do swiftdoc e do modificador de acesso "público", eu não tinha visto esse tipo de coisa no Swift antes, mas o CChar e o C-String parecem ser um retrocesso ao C e conjuntos de caracteres de 8 bits e Swift tem tudo a ver com texto Unicode ... ou as ferramentas de linha de comando são todas ASCII ??
Kaydell
2
Acredito que getchar () retorna ASCII. Terminais Unicode é uma coisa que eu não queria
abordar
5

Outra alternativa é vincular libedit para edição de linha apropriada (teclas de seta, etc.) e suporte de histórico opcional. Eu queria isso para um projeto que estou começando e montei um exemplo básico de como configurá-lo .

Uso do Swift

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

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

Wrapper ObjC para expor 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
fonte
5

Em geral, a função readLine () é usada para verificar a entrada do console. Mas não funcionará em projetos iOS normais até ou a menos que você adicione "ferramenta de linha de comando" .

A melhor maneira de testar, você pode fazer:

1. Crie um arquivo macOS

insira a descrição da imagem aqui

2. Use a função readLine () para verificar String opcional do console

 import Foundation

 print("Please enter some input\n")

 if let response = readLine() {
    print("output :",response)
 } else {
    print("Nothing")
 }

Resultado :

Please enter some input

Hello, World
output : Hello, World
Program ended with exit code: 0

insira a descrição da imagem aqui

Ashis Laha
fonte
3

Aqui está um exemplo simples de obtenção de entrada do usuário em um aplicativo baseado em console: Você pode usar readLine (). Faça a entrada do console para o primeiro número e pressione enter. Depois disso, insira o segundo número, conforme mostrado na imagem abaixo:

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)

Resultado

Shehzad Ali
fonte
1

Juro por Deus ... a solução para este problema absolutamente básico me escapou por ANOS. É TÃO simples ... mas há tantas informações vagas / ruins por aí; espero poder salvar alguém de algumas das tocas de coelho sem fundo em que acabei ...

Então, vamos obter uma "string" de "o usuário" por meio de "o console", por meio de stdin, vamos ?

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

se você quiser SEM a nova linha final, basta adicionar ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ

Alex Gray
fonte
4
Swift, o OP diz Swift.
HenryRootTwo
1
Ele disse osx. Tudo se resume na mesma coisa! Abrace seu ObjC interno!
Alex Gray
2
Há pessoas que escrevem em Swift e nunca aprenderão o Objetivo C. Não se trata do que ele compila.
HenryRootTwo
1

Muitas respostas desatualizadas para esta pergunta. A partir do Swift 2+, a biblioteca padrão do Swift contém a função readline () . Ele retornará um opcional, mas só será nulo se EOF for atingido, o que não acontecerá ao obter a entrada do teclado para que possa ser desembrulhado à força com segurança nesses cenários. Se o usuário não inserir nada, seu valor (desembrulhado) será uma string vazia. Aqui está uma pequena função de utilidade que usa recursão para alertar o usuário até que pelo menos um caractere tenha sido inserido:

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

Observe que o uso de ligação opcional (if let input = readLine ()) para verificar se algo foi inserido como proposto em outras respostas não terá o efeito desejado, pois nunca será nulo e pelo menos "" ao aceitar a entrada do teclado.

Isso não funcionará em um Playground ou em qualquer outro ambiente onde você não tenha acesso ao prompt de comando. Parece haver problemas no REPL da linha de comando também.

Andreas Bergström
fonte
0

Como não havia soluções sofisticadas para esse problema, fiz uma pequena classe para ler e analisar a entrada padrão em Swift. Você pode encontrar aqui .

Exemplo

Para analisar:

+42 st_ring!
-0.987654321 12345678900
.42

Você faz:

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
fonte
Como importar a classe StreamScanner?
Timothy Swan de
@TimothySwan, conforme explicado na seção Instalação do Leiame ( github.com/shoumikhin/StreamScanner#installation ). Então, basicamente, você pode apenas adicionar o arquivo StreamScanner.swift ao seu projeto.
shoumikhin
0

Antes

insira a descrição da imagem aqui

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

Correção

insira a descrição da imagem aqui

Murphy
fonte
0

Isso funciona no xCode v6.2, acho que é o Swift v1.2

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

Se quiser ler uma string separada por espaço e dividir imediatamente a string em uma matriz, você pode fazer o seguinte:

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

por exemplo.

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
fonte
0

Quando a função readLine () é executada no Xcode, o console de depuração espera pela entrada. O resto do código será retomado após a entrada ser feita.

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

A resposta mais bem classificada para essa pergunta sugere o uso do método readLine () para obter a entrada do usuário a partir da linha de comando. No entanto, quero observar que você precisa usar o! operador ao chamar este método para retornar uma string em vez de um opcional:

var response = readLine()!
topherPedersen
fonte
Por que o desempacotamento forçado readLine()retorna opcional por uma razão, portanto, não é seguro forçar o desempacotamento e realmente não adiciona nada ao exemplo.
Camsoft,
0

Swift 5: se você deseja inserir continuamente a partir do teclado, sem encerrar o programa, como um fluxo de entrada, use as etapas abaixo:

  1. Criar novo projeto do tipo ferramenta de linha comnnad Projeto de linha de comando

    1. Adicione o código abaixo no arquivo 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. Execute o projeto e clique no executável na pasta Produtos no Xcode e abra no localizador
    3. Clique duas vezes no executável para abri-lo.
    4. Agora insira suas entradas. O terminal será semelhante a este: insira a descrição da imagem aqui
Shikha Budhiraja
fonte
0
var a;
scanf("%s\n", n);

Eu testei isso no ObjC, e talvez isso seja útil.

StackOverflow
fonte
-1

Eu só queria comentar (não tenho representantes suficientes) sobre a implementação do xenadu, porque CCharno OS X é Int8, e o Swift não gosta nada quando você adiciona ao array quando getchar()retorna partes de UTF-8, ou qualquer outra coisa acima de 7 bits.

Estou usando um array de UInt8, em vez disso, funciona muito bem e String.fromCStringconverte UInt8em UTF-8 muito bem.

Porém foi assim que eu fiz

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
fonte
-1

Agora consigo obter entrada de teclado em Swift usando o seguinte:

Em meu arquivo main.swift, declarei uma variável i e atribuí a ela a função GetInt () que defini no Objetivo C. Por meio de um cabeçalho chamado Bridging, onde declarei o protótipo de função para GetInt, pude vincular a main.swift. Aqui estão os arquivos:

main.swift:

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

Cabeçalho de ponte:

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

Em obj.m é possível incluir a saída e entrada padrão c, stdio.h, bem como a biblioteca padrão c stdlib.h que permite programar em C em Objective-C, o que significa que não há necessidade de incluir um arquivo rápido real como user.c ou algo parecido.

Espero poder ajudar,

Edit: Não é possível obter a entrada String por meio de C porque aqui estou usando o CInt -> o tipo inteiro de C e não de Swift. Não existe um tipo Swift equivalente para o C char *. Portanto, String não é conversível em string. Mas existem soluções bastante por aqui para obter entrada String.

Raul

Raul Rao
fonte