Nhập từ bàn phím trong ứng dụng dòng lệnh


93

Tôi đang cố gắng lấy đầu vào bàn phím cho một ứng dụng dòng lệnh cho ngôn ngữ lập trình Swift mới của Apple.

Tôi đã quét tài liệu nhưng không có kết quả.

import Foundation

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

Bất kỳ ý tưởng?

Câu trả lời:


137

Cách chính xác để làm điều này là sử dụng readLine, từ Thư viện tiêu chuẩn Swift.

Thí dụ:

let response = readLine()

Sẽ cung cấp cho bạn một giá trị Tùy chọn chứa văn bản đã nhập.


2
cảm ơn. có cách nào để lấy loại mật khẩu đầu vào không?
Abhijit Gaikwad

Đối với tôi, nó chỉ giảm xuống dòng này với response = nil. Bất kỳ ý tưởng vấn đề là gì?
Peter Webb

4
@PeterWebb - hoạt động tốt trong thiết bị đầu cuối xcode, rơi vào sân chơi :)
aprofromindia

2
readLine đã được thêm vào sau hầu hết các câu trả lời ban đầu, vì vậy thái độ có chút không chính đáng IMHO
russbishop

2
Điều chỉnh cho Swift 3, SlimJim
Ezekiel Elin

62

Tôi đã cố gắng tìm ra nó mà không bỏ xuống C:

Giải pháp của tôi như sau:

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

Các phiên bản mới hơn của Xcode cần một typecast rõ ràng (hoạt động trong Xcode 6.4):

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

5
Tôi cũng thích điều này, nó có thể được nén xuống một dòng nếu bạn không muốn xác định một hàm, như nếu bạn chỉ cần chấp nhận đầu vào một lần trong một chương trình:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11

3
Có cách nào để sử dụng điều này trong Playground để chấp nhận thông tin nhập của người dùng ngay lập tức không?
Rob Cameron

5
Bạn không thể sử dụng cái này trong sân chơi. Bạn phải sử dụng các biến trong đó.
Chalkers

8
Lưu ý rằng bạn sẽ nhận được dòng mới trong chuỗi với điều này. Dải chúng ra vớistring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
vn-nb

3
Bạn cũng sẽ nhận được các ký tự lạ nếu người dùng xóa một ký tự, sử dụng phím mũi tên, v.v. AAAGGGGGHHHH TẠI SAO, Swift, Tại sao?
ybakos

13

Nó thực sự không dễ dàng như vậy, bạn phải tương tác với C API. Không có thay thế cho scanf. Tôi đã xây dựng một ví dụ nhỏ:

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

Ôi cậu bé - Tôi ước có một cái gì đó tầm thường hơn thế này! Tôi thấy khó điều chỉnh điều này cho một chuỗi ký tự.
Chalkers

1
Sẽ tốt hơn nếu tạo một "UserInput.h" để lưu trữ các định nghĩa hàm và bao gồm tệp này trong "cliinput-Bridging-Header.h".
Alan Zhiliang Feng

7

chỉnh sửa Kể từ Swift 2.2, thư viện tiêu chuẩn bao gồm readLine. Tôi cũng sẽ lưu ý rằng Swift đã chuyển sang nhận xét tài liệu đánh dấu. Để lại câu trả lời ban đầu của tôi cho bối cảnh lịch sử.

Chỉ để hoàn thiện, đây là một triển khai Swift readlnmà tôi đã sử dụng. Nó có một tham số tùy chọn để chỉ ra số byte tối đa mà bạn muốn đọc (có thể có hoặc không phải là độ dài của Chuỗi).

Điều này cũng chứng tỏ việc sử dụng đúng cách các chú thích của swiftdoc - Swift sẽ tạo ra một tệp <project> .swiftdoc và Xcode sẽ sử dụng nó.

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

Tôi thích các nhận xét swiftdoc và công cụ sửa đổi quyền truy cập "công khai", tôi chưa từng thấy điều đó trong Swift trước đây, nhưng CChar và C-String dường như là một sự trở lại của bộ ký tự C và 8-bit và Swift là tất cả về văn bản Unicode ... hay các công cụ dòng lệnh đều là ASCII ??
Kaydell,

2
Tôi tin rằng getchar () trả về ASCII. Thiết bị đầu cuối Unicode là một điều hoàn toàn tôi không muốn để có được vào :)
russbishop

6

Nói chung, hàm readLine () được sử dụng để quét đầu vào từ bảng điều khiển. Nhưng nó sẽ không hoạt động trong dự án iOS bình thường cho đến khi hoặc trừ khi bạn thêm "công cụ dòng lệnh" .

Cách tốt nhất để kiểm tra, bạn có thể làm:

1. Tạo tệp macOS

nhập mô tả hình ảnh ở đây

2. Sử dụng hàm readLine () để quét Chuỗi tùy chọn từ bảng điều khiển

 import Foundation

 print("Please enter some input\n")

 if let response = readLine() {
    print("output :",response)
 } else {
    print("Nothing")
 }

Đầu ra:

Please enter some input

Hello, World
output : Hello, World
Program ended with exit code: 0

nhập mô tả hình ảnh ở đây


5

Một giải pháp thay thế khác là liên kết libedit để chỉnh sửa dòng thích hợp (phím mũi tên, v.v.) và hỗ trợ lịch sử tùy chọn. Tôi muốn điều này cho một dự án mà tôi đang bắt đầu và tập hợp một ví dụ cơ bản cho cách tôi thiết lập nó .

Cách sử dụng từ nhanh

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

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

Trình bao bọc objC để hiển thị 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

Đây là ví dụ đơn giản về việc lấy đầu vào từ người dùng trên ứng dụng dựa trên bảng điều khiển: Bạn có thể sử dụng readLine (). Lấy đầu vào từ bảng điều khiển cho số đầu tiên rồi nhấn enter. Sau đó, lấy đầu vào cho số thứ hai như thể hiện trong hình ảnh bên dưới:

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)

Đầu ra


2

Rất nhiều câu trả lời lỗi thời cho câu hỏi này. Kể từ Swift 2+, Thư viện Tiêu chuẩn Swift chứa hàm readline () . Nó sẽ trả về Tùy chọn nhưng nó sẽ chỉ là con số không nếu đã đạt đến EOF, điều này sẽ không xảy ra khi nhận đầu vào từ bàn phím để nó có thể được mở ra một cách an toàn trong các trường hợp đó. Nếu người dùng không nhập bất kỳ thứ gì thì giá trị (chưa được gói) của nó sẽ là một chuỗi trống. Đây là một chức năng tiện ích nhỏ sử dụng đệ quy để nhắc người dùng cho đến khi nhập ít nhất một ký tự:

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

Lưu ý rằng việc sử dụng liên kết tùy chọn (if let input = readLine ()) để kiểm tra xem nội dung nào đó đã được nhập như được đề xuất trong các câu trả lời khác sẽ không có tác dụng mong muốn, vì nó sẽ không bao giờ bằng 0 và ít nhất là "" khi chấp nhận nhập bằng bàn phím.

Điều này sẽ không hoạt động trong Playground hoặc bất kỳ môi trường nào khác mà bạn không có quyền truy cập vào dấu nhắc lệnh. Có vẻ như nó cũng có vấn đề trong REPL dòng lệnh.


1

Tôi thề có Chúa .. giải pháp cho vấn đề hoàn toàn cơ bản này đã trốn tránh tôi trong nhiều NĂM. Nó rất đơn giản .. nhưng có quá nhiều thông tin mơ hồ / xấu ngoài kia; hy vọng tôi có thể cứu ai đó khỏi một số hố thỏ không đáy mà tôi đã mắc vào ...

Vì vậy, chúng ta hãy lấy một "chuỗi" từ "người dùng" thông qua "bảng điều khiển", thông qua stdin, phải không?

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

nếu bạn muốn nó KHÔNG có dòng mới ở cuối, chỉ cần thêm ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ


4
Swift, OP nói Swift.
HenryRootThai

1
Anh ta nói osx. Tất cả biên dịch xuống cùng một thứ! Ôm lấy khách hàng bên trong của bạn!
Alex Gray

2
Có những người viết bằng Swift và sẽ không bao giờ học Objective C. Đây không phải là về những gì nó biên dịch.
HenryRootHai

0

Vì không có giải pháp ưa thích nào cho vấn đề này, tôi đã tạo một lớp nhỏ để đọc và phân tích cú pháp đầu vào chuẩn trong Swift. Bạn có thể tìm thấy nó ở đây .

Thí dụ

Để phân tích cú pháp:

+42 st_ring!
-0.987654321 12345678900
.42

Bạn làm:

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

Làm cách nào để nhập lớp StreamScanner?
Timothy Swan

@TimothySwan, giống như được giải thích trong phần Cài đặt của Readme ( github.com/shoumikhin/StreamScanner#installation ). Vì vậy, về cơ bản bạn chỉ có thể thêm tệp StreamScanner.swift vào dự án của mình.
shoumikhin


0

Điều này hoạt động trong xCode v6.2, tôi nghĩ đó là Swift v1.2

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

0

Nếu bạn muốn đọc chuỗi được phân tách bằng dấu cách và ngay lập tức chia chuỗi thành một mảng, bạn có thể thực hiện điều này:

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

ví dụ.

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

Khi hàm readLine () được chạy trên Xcode, bảng điều khiển gỡ lỗi sẽ đợi đầu vào. Phần còn lại của mã sẽ được tiếp tục sau khi nhập xong.

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

0

Câu trả lời được xếp hạng cao nhất cho câu hỏi này đề xuất sử dụng phương thức readLine () để nhận đầu vào của người dùng từ dòng lệnh. Tuy nhiên, tôi muốn lưu ý rằng bạn cần phải sử dụng! khi gọi phương thức này để trả về một chuỗi thay vì một tùy chọn:

var response = readLine()!

Tại sao force-unrap, readLine()trả về tùy chọn vì một lý do, do đó, nó không an toàn để buộc mở và không thực sự thêm bất cứ điều gì vào ví dụ.
Camsoft

0

Swift 5: Nếu bạn liên tục muốn nhập liệu từ bàn phím mà không cần kết thúc chương trình, chẳng hạn như một luồng nhập liệu, hãy sử dụng các bước dưới đây:

  1. Tạo dự án mới của loại công cụ dòng comnnad Dự án dòng lệnh

    1. Thêm mã bên dưới vào tệp 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. Chạy Dự án và nhấp vào tệp thực thi trong thư mục Sản phẩm trong Xcode và mở trong công cụ tìm
    3. Nhấp đúp vào tệp thực thi để mở nó.
    4. Bây giờ hãy nhập Đầu vào của bạn. Thiết bị đầu cuối sẽ trông giống như sau: nhập mô tả hình ảnh ở đây

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

Tôi đã thử nghiệm điều này trong ObjC và có thể điều này sẽ hữu ích.


-1

Tôi chỉ muốn nhận xét (tôi không có đủ đại diện) về việc triển khai xenadu, bởi vì CChartrong OS X là như Int8vậy và Swift hoàn toàn không thích khi bạn thêm vào mảng khigetchar() trả về các phần của UTF-8 hoặc bất kỳ thứ gì khác trên 7 bit.

Tôi đang sử dụng một mảng UInt8thay thế, và nó hoạt động tốt và String.fromCStringchuyển đổi UInt8thành UTF-8 tốt.

Tuy nhiên đây là cách tôi làm

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

Bây giờ tôi đã có thể nhập Bàn phím trong Swift bằng cách sử dụng như sau:

Trong tệp main.swift của tôi, tôi đã khai báo một biến i và gán cho nó hàm GetInt () mà tôi đã xác định trong Mục tiêu C. Thông qua một cái gọi là Bridging Header, nơi tôi đã khai báo nguyên mẫu hàm cho GetInt, tôi có thể liên kết với main.swift. Đây là các tệp:

main.swift:

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

Tiêu đề cầu nố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;
}

Trong obj.m, có thể bao gồm đầu ra và đầu vào tiêu chuẩn c, stdio.h, cũng như thư viện tiêu chuẩn c stdlib.h cho phép bạn lập trình bằng C trong Objective-C, có nghĩa là không cần bao gồm một tệp nhanh chóng thực sự như user.c hoặc một cái gì đó tương tự.

Hy vọng tôi có thể giúp,

Chỉnh sửa: Không thể lấy đầu vào String thông qua C vì ở đây tôi đang sử dụng CInt -> kiểu số nguyên của C chứ không phải của Swift. Không có kiểu Swift tương đương cho C char *. Do đó String không thể chuyển đổi thành string. Nhưng có khá đủ các giải pháp xung quanh đây để lấy đầu vào Chuỗi.

Raul

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.