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:
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.
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
}
var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
string.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
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);
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 readln
mà 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) }
}
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:
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
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
Đâ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)
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.
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!
♥ ⱥ ᏪℯⅩ
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"
}
Đ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
}
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)")
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)
}
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()!
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ụ.
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:
Tạo dự án mới của loại công cụ dòng comnnad
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:")
}
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.
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ì CChar
trong OS X là như Int8
vậ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 UInt8
thay thế, và nó hoạt động tốt và String.fromCString
chuyển đổi UInt8
thà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)")
}
}
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