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?

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.


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
}

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

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

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

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


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


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! ♥ ⱥ ᏪℯⅩ


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.


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

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

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
}

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

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

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()!

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

0
var a;
scanf("%s\n", n);

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


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

-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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.