Ввод с клавиатуры в приложении командной строки

Я пытаюсь получить ввод с клавиатуры для приложения командной строки для нового языка программирования Apple Swift.

Я сканировал документы безрезультатно.

import Foundation

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

Есть идеи?

20 ответов

Решение

Мне удалось понять это, не опускаясь до C:

Мое решение заключается в следующем:

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

Более поздние версии Xcode нуждаются в явном приведении типов (работает в Xcode 6.4):

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

Правильный способ сделать это - использовать readLine из стандартной библиотеки Swift.

Пример:

let response = readLine()

Даст вам необязательное значение, содержащее введенный текст.

На самом деле это не так просто, вы должны взаимодействовать с C API. Нет альтернативы scanf, Я построил маленький пример:

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

В общем, функция readLine() используется для сканирования ввода с консоли. Но он не будет работать в обычном проекте iOS до тех пор, пока вы не добавите "инструмент командной строки".

Лучший способ для тестирования вы можете сделать:

1. Создайте файл macOS

2. Используйте функцию readLine() для сканирования необязательной строки из консоли

 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

Начиная с Swift 2.2 стандартная библиотека включает readLine, Также отмечу, что Swift перешел на комментарии к документам уценки. Оставив мой оригинальный ответ для исторического контекста.

Просто для полноты, вот реализация Swift readln Я использую. Он имеет необязательный параметр для указания максимального количества байтов, которые вы хотите прочитать (которая может быть или не быть длиной строки).

Это также демонстрирует правильное использование комментариев swiftdoc - Swift создаст файл .swiftdoc, а Xcode будет его использовать.

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

Другая альтернатива - связать libedit для правильного редактирования строки (клавиши со стрелками и т. Д.) И дополнительной поддержки истории. Я хотел это для проекта, который я начинаю, и собрал базовый пример того, как я его настроил.

Использование от Swift

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

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

Обертка ObjC, чтобы выставить 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

Много устаревших ответов на этот вопрос. Начиная с Swift 2+ стандартная библиотека Swift содержит функцию readline(). Он вернет Optional, но он будет нулевым, если достигнут EOF, что не произойдет при получении ввода с клавиатуры, поэтому его можно безопасно развернуть силой в этих сценариях. Если пользователь ничего не вводит, его (развернутым) значением будет пустая строка. Вот небольшая служебная функция, которая использует рекурсию для запроса пользователя, пока не будет введен хотя бы один символ:

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

Обратите внимание, что использование необязательного связывания (если пусть input = readLine()) для проверки того, что что-то было введено, как предложено в других ответах, не даст желаемого эффекта, поскольку оно никогда не будет равно нулю и, по крайней мере, "" при принятии ввода с клавиатуры.

Это не будет работать на игровой площадке или в любой другой среде, где у вас нет доступа к командной строке. Кажется, что есть проблемы и в командной строке REPL.

Вот простой пример получения ввода от пользователя в консольном приложении: Вы можете использовать readLine(). Возьмите ввод с консоли для первого номера, затем нажмите ввод. После этого введите второй номер, как показано на рисунке ниже:

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)

Выход

Swift 5: если вы постоянно хотите вводить данные с клавиатуры, не завершая программу, например поток ввода, выполните следующие действия:

  1. Создать новый проект типа comnnad line tool

    1. Добавьте ниже код в файл 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. Запустите проект, щелкните исполняемый файл в папке Products в Xcode и откройте его в Finder.
    3. Дважды щелкните исполняемый файл, чтобы открыть его.
    4. Теперь введите свои входы. Терминал будет выглядеть примерно так:

Клянусь Богом... решение этой совершенно основной проблемы ускользнуло от меня за ГОДЫ. Это НАСТОЛЬКО просто... но там так много неопределенной / плохой информации; надеюсь, я смогу спасти кого- нибудь из бездонных кроличьих норов, в которых я оказался...

Итак, давайте получим "строку" от "пользователя" через "консоль", через stdin мы будем?

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

если вы хотите это без завершающей строки, просто добавьте...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ

Ответ на этот вопрос в верхнем рейтинге предполагает использование метода readLine(), чтобы получить пользовательский ввод из командной строки. Тем не менее, я хочу отметить, что вам нужно использовать! Оператор при вызове этого метода возвращает строку вместо необязательного:

var response = readLine()!

Когда функция readLine() запущена в Xcode, консоль отладки ожидает ввода. Остальная часть кода будет возобновлена ​​после завершения ввода.

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

Это работает в xCode v6.2, я думаю, что это Swift v1.2

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

Поскольку не было причудливых решений этой проблемы, я создал крошечный класс для чтения и анализа стандартного ввода в Swift. Вы можете найти это здесь.

пример

Для разбора:

+42 st_ring!
-0.987654321 12345678900
.42

Ты сделаешь:

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

До

введите описание изображения здесь

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

коррекция

введите описание изображения здесь

Если вы хотите прочитать разделенную пробелами строку и сразу разбить строку на массив, вы можете сделать это:

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

например.

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)")
      import Foundation
print("Enter a number")
let number : Int = Int(readLine(strippingNewline: true)!) ?? 0
if(number < 5)
{
    print("Small")
}else{
    print("Big")
}
for i in 0...number{
    print(i)
}

Теперь я смог получить ввод с клавиатуры в Swift с помощью следующего:

В моем файле main.swift я объявил переменную i и назначил ей функцию GetInt(), которую я определил в Objective C. Через так называемый Bridging Header, где я объявил прототип функции для GetInt, я мог связываться с main.swift. Вот файлы:

main.swift:

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

Заголовок моста:

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

В obj.m можно включить стандартный вывод и ввод c, stdio.h, а также стандартную библиотеку c stdlib.h, которая позволяет программировать на C в Objective-C, что означает, что нет необходимости включать настоящий быстрый файл вроде user.c или что-то в этом роде.

Надеюсь, я смогу помочь,

Изменить: Невозможно получить ввод строки через C, потому что здесь я использую CInt -> целочисленный тип C, а не Swift. Не существует эквивалентного типа Swift для C char*. Поэтому строка не может быть преобразована в строку. Но здесь достаточно решений, чтобы получить ввод String.

Raul

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

Я тестировал это в ObjC, и, возможно, это будет полезно.

Я просто хотел прокомментировать (у меня недостаточно повторений) реализацию xenadu, потому что CChar в OS X есть Int8и Swift вообще не нравится когда добавляешь в массив когда getchar() возвращает части UTF-8 или что-либо еще выше 7 бит.

Я использую массив UInt8 вместо этого, и это прекрасно работает и String.fromCString преобразует UInt8 в UTF-8 просто отлично.

Однако, вот как я это сделал

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)")
    }
}
Другие вопросы по тегам