Введення з клавіатури в програмі командного рядка


92

Я намагаюся отримати введення з клавіатури для програми командного рядка для нової мови програмування Apple Swift.

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

import Foundation

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

Будь-які ідеї?

Відповіді:


136

Правильний спосіб зробити це - використовувати readLineзі стандартної бібліотеки Swift.

Приклад:

let response = readLine()

Дасть вам необов’язкове значення, що містить введений текст.


2
Дякую. чи є спосіб отримати тип введення пароля?
Абхіджіт Гайквад

Для мене це просто падає через цей рядок з response = nil. Будь-яка ідея, в чому проблема?
Пітер Вебб,

4
@PeterWebb - чудово працює в терміналі xcode, провалюється на дитячому майданчику :)
aprofromindia

2
readLine було додано після більшості оригінальних відповідей, тому ставлення до них трохи невиправдане IMHO
russbishop

2
Виправлено для Swift 3, SlimJim
Ezekiel Elin

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 ЧОМУ, Свіфт, Чому?
ybakos

13

Насправді це не так просто, вам доведеться взаємодіяти з API C. Альтернативи для цього немає 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".
Alan Zhiliang Feng

7

редагувати Станом на Swift 2.2 стандартна бібліотека включає readLine. Я також зазначу, що 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 та "публічний" модифікатор доступу, я раніше не бачив такого у Swift, але CChar та C-String здаються поверненням до C та 8-бітових наборів символів та Swift це все про текст Unicode ... або інструменти командного рядка - це всі ASCII ??
Kaydell

2
Я вважаю, що getchar () повертає ASCII. Термінали Unicode - це ціла річ, до якої я не хотів потрапляти :)
russbishop

6

Загалом, функція 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 для належного редагування рядків (клавіші зі стрілками тощо) та додаткової підтримки історії. Я хотів це для проекту, який я починаю, і склав базовий приклад того, як я його створив .

Використання від швидкого

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 () . Він поверне необов’язковий, але це буде нуль, лише якщо буде досягнуто 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.


1

Я клянусь Богом .. рішення цієї цілком основної проблеми уникнуло мене РОКІВ. Це настільки просто .. але там стільки розмитої / поганої інформації; сподіваюся, я можу врятувати когось із якоїсь бездонної кролячої нори, в якій я опинився ...

Тож давайте отримаємо "рядок" від "користувача" через "консоль", через stdin, чи не так ?

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

якщо ви хочете це БЕЗ кінцевого нового рядка, просто додайте ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ


4
Свіфт, ОП каже Свіфт.
HenryRoot, два

1
Він сказав osx. Це все складається до одного і того ж! Обійміть свій внутрішній ObjC!
Алекс Грей

2
Є люди, які пишуть Swift і ніколи не вивчать ціль C. Це не стосується того, до чого вона складається.
HenryRoot,

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

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

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 (), яку я визначив у Цілі 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 *. Тому рядок не можна конвертувати в рядок. Але тут досить багато рішень, щоб отримати String input.

Рауль

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.