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


94

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

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

import Foundation

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

Любые идеи?

Ответы:


138

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

Пример:

let response = readLine()

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


2
Спасибо. есть ли способ получить ввод пароля?
Абхиджит Гайквад,

Для меня это просто пропадает через эту строку с response = nil. Есть идеи, в чем проблема?
Питер Уэбб

4
@PeterWebb - отлично работает в терминале xcode, проваливается на детской площадке :)
aprofromindia 08

2
readLine был добавлен после большинства исходных ответов, так что отношение немного необоснованное, ИМХО
russbishop

2
Исправлено для Swift 3, SlimJim
Иезекииль Элин

62

Мне удалось понять это, не переходя на 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
}

5
Мне также нравится, что его можно сжать до одной строки, если вы не хотите определять функцию, например, если вам нужно принять ввод только один раз в программе:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11

3
Есть ли способ использовать это на игровой площадке, чтобы принимать вводимые пользователем данные на лету?
Роб Кэмерон

5
Вы не можете использовать это на детской площадке. Вы должны использовать там переменные.
Chalkers

8
Обратите внимание, что с этим вы получите символы новой строки в строке. Разделите их с помощьюstring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb

3
Вы также получите странные символы, если пользователь удалит символ, использует клавишу со стрелкой и т. Д. AAAGGGGGHHHH WHY, Swift, Why?
ybakos

14

На самом деле это не так просто, вам нужно взаимодействовать с 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);

О, мальчик, если бы не было чего-то более тривиального, чем это! Мне сложно адаптировать это для строки символов.
Chalkers

1
Было бы лучше создать «UserInput.h» для хранения определений функций и включить этот файл в «cliinput-Bridging-Header.h».
Алан Чжилян Фэн

7

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

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

Это также демонстрирует правильное использование комментариев swiftdoc - Swift сгенерирует файл <project> .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) }
}

Мне нравятся комментарии swiftdoc и модификатор доступа "public", я раньше не видел ничего подобного в Swift, но CChar и C-String кажутся возвратом к C и 8-битным наборам символов и Swift. все о тексте Unicode ... или все инструменты командной строки ASCII ??
Kaydell

2
Я считаю, что getchar () возвращает ASCII. Терминалы Unicode - это то, во что я не хотел
вдаваться

7

Обычно функция 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

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


5

Другой альтернативой является привязка 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

3

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

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)

Выход


2

Множество устаревших ответов на этот вопрос. Начиная с 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)")

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

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


1

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

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

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

если вы хотите его БЕЗ завершающего символа новой строки, просто добавьте ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ


4
Свифт, ОП говорит Свифт.
HenryRootTwo

1
Он сказал osx. Все сводится к одному и тому же! Примите свой внутренний ObjC!
Alex Gray

2
Есть люди, которые пишут на Swift и никогда не выучат Objective C. Дело не в том, до чего он компилируется.
HenryRootTwo

1

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. Теперь введите свои входы. Терминал будет выглядеть примерно так: введите описание изображения здесь

0

Поскольку у этой проблемы не было необычных решений, я создал крошечный класс для чтения и анализа стандартного ввода в 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"
}

Как импортировать класс StreamScanner?
Тимоти Свон,

@TimothySwan, как описано в разделе установки Readme ( github.com/shoumikhin/StreamScanner#installation ). Таким образом, вы можете просто добавить файл StreamScanner.swift в свой проект.
shoumikhin


0

Это работает в 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
}

0

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

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

0

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

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

0

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

var response = readLine()!

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

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

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


-1

Я просто хотел прокомментировать (у меня недостаточно представителей) реализацию 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)")
    }
}

-1

Теперь я смог получить ввод с клавиатуры в 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 или что-то в этом роде.

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

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

Рауль

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.