Прослушивание stdin в Swift

В настоящее время я пытаюсь прослушать ввод пользователя из командной строки в моем быстром приложении.

Я знаю о readLine() метод, но он не совсем соответствует моим потребностям. Я хочу слушать данные, вставляемые в командную строку. Например, когда пользователь нажимает клавишу "вверх" внутри терминала.

Что-то вроде того, что можно сделать в Node.js:

stdin.on( 'data', function( key ){ 
    if (key === '\u0003' ) {
        process.exit();   
    }   // write the key to stdout all normal like                 

    process.stdout.write( key ); 
});

Я попытался найти, но я не смог найти аналога в Swift. Я подумал, что-то с Inputstream, но не нашел подходящего решения.

Если бы кто-нибудь мог дать мне несколько советов о том, как сделать что-то подобное в Swift, я был бы очень признателен.

2 ответа

Решение

Обычно стандартный ввод буферизует все до ввода новой строки, поэтому типичный стандартный ввод читается по строкам:

while let line = readLine() {
   print(line)
}

(нажмите CTRL+D, чтобы отправить EOF, то есть завершить ввод)

Чтобы действительно прочитать каждый символ отдельно, вам нужно войти в режим raw, а это значит использовать функции терминала низкого уровня:

// see https://stackru.com/a/24335355/669586
func initStruct<S>() -> S {
    let struct_pointer = UnsafeMutablePointer<S>.allocate(capacity: 1)
    let struct_memory = struct_pointer.pointee
    struct_pointer.deallocate() 
    return struct_memory
}

func enableRawMode(fileHandle: FileHandle) -> termios {
    var raw: termios = initStruct()
    tcgetattr(fileHandle.fileDescriptor, &raw)

    let original = raw

    raw.c_lflag &= ~(UInt(ECHO | ICANON))
    tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &raw);

    return original
}

func restoreRawMode(fileHandle: FileHandle, originalTerm: termios) {
    var term = originalTerm
    tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &term);
}

let stdIn = FileHandle.standardInput
let originalTerm = enableRawMode(fileHandle: stdIn)

var char: UInt8 = 0
while read(stdIn.fileDescriptor, &char, 1) == 1 {
    if char == 0x04 { // detect EOF (Ctrl+D)
        break
    }
    print(char)
}

// It would be also nice to disable raw input when exiting the app.
restoreRawMode(fileHandle: stdIn, originalTerm: originalTerm)

Ссылка https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html

Вы, вероятно, хотите FileHandle.standardInput,

Что-то вроде:

let file = FileHandle.standardInput

while true {
    let data = file.availableData
    print("\(String(bytes: data, encoding: .utf8))")
}

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

Я не совсем уверен, как бы вы подходили к определенным клавишам управления и стрелкам, но это только начало.

Другие вопросы по тегам