ป้อนข้อมูลจากแป้นพิมพ์ในแอปพลิเคชันบรรทัดคำสั่ง


94

ฉันพยายามรับข้อมูลแป้นพิมพ์สำหรับแอปบรรทัดคำสั่งสำหรับภาษาโปรแกรม Apple ใหม่ Swift

ฉันสแกนเอกสารแล้วก็ไม่มีประโยชน์

import Foundation

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

ความคิดใด ๆ ?

คำตอบ:


138

วิธีที่ถูกต้องคือการใช้readLineจาก Swift Standard Library

ตัวอย่าง:

let response = readLine()

จะให้ค่าทางเลือกที่มีข้อความที่ป้อน


2
ขอบคุณ. มีวิธีรับประเภทรหัสผ่านหรือไม่?
Abhijit Gaikwad

สำหรับฉันมันแค่เลื่อนผ่านบรรทัดนี้ด้วย response = nil มีความคิดว่าปัญหาคืออะไร?
Peter Webb

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
มีวิธีใช้สิ่งนี้ใน Playground เพื่อยอมรับข้อมูลของผู้ใช้ทันทีหรือไม่?
Rob Cameron

5
คุณไม่สามารถใช้สิ่งนี้ในสนามเด็กเล่น คุณต้องใช้ตัวแปรในนั้น
Chalkers

8
โปรดทราบว่าคุณจะได้ขึ้นบรรทัดใหม่ในสตริงด้วย ถอดออกด้วยstring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb

3
นอกจากนี้คุณยังจะได้รับอักขระแปลก ๆ หากผู้ใช้ลบอักขระใช้ปุ่มลูกศร ฯลฯ AAAGGGGGHHHH ทำไม Swift ทำไม?
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"
Alan Zhiliang Feng

7

แก้ไขในฐานะของสวิฟท์ 2.2 readLineห้องสมุดมาตรฐานรวมถึง ฉันจะสังเกตด้วยว่า Swift เปลี่ยนไปใช้ความคิดเห็นเอกสาร markdown ทิ้งคำตอบเดิมของฉันไว้สำหรับบริบททางประวัติศาสตร์

เพื่อความสมบูรณ์นี่คือการใช้งาน Swift ที่readlnฉันเคยใช้ มีพารามิเตอร์ที่เป็นทางเลือกเพื่อระบุจำนวนไบต์สูงสุดที่คุณต้องการอ่าน (ซึ่งอาจเป็นความยาวของ String หรือไม่ก็ได้)

นอกจากนี้ยังแสดงให้เห็นถึงการใช้ความคิดเห็น 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 text ... หรือเป็นเครื่องมือบรรทัดคำสั่ง ASCII ทั้งหมด ??
Kaydell

2
ฉันเชื่อว่า getchar () ส่งคืน ASCII เทอร์มินัล Unicode เป็นสิ่งที่ฉันไม่ต้องการเข้าไป :)
russbishop

7

โดยทั่วไปแล้วฟังก์ชั่นreadLine ()ใช้สำหรับการสแกนอินพุตจากคอนโซล แต่มันจะไม่ทำงานใน iOS ปกติโครงการจนกว่าหรือจนกว่าคุณจะเพิ่ม"เครื่องมือบรรทัดคำสั่ง"

วิธีที่ดีที่สุดสำหรับการทดสอบคุณสามารถทำได้:

1. สร้างไฟล์ macOS

ป้อนคำอธิบายภาพที่นี่

2. ใช้ readLine () func เพื่อสแกนสตริงเสริมจากคอนโซล

 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 wrapper เพื่อแสดง 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 ()) เพื่อตรวจสอบว่ามีการป้อนข้อมูลตามที่เสนอในคำตอบอื่น ๆ หรือไม่จะไม่มีผลตามที่ต้องการเนื่องจากจะไม่เป็นศูนย์และเป็นอย่างน้อย "" เมื่อยอมรับการป้อนข้อมูลด้วยแป้นพิมพ์

สิ่งนี้จะไม่ทำงานใน Playground หรือสภาพแวดล้อมอื่น ๆ ที่คุณไม่มีสิทธิ์เข้าถึงพรอมต์คำสั่ง ดูเหมือนว่าจะมีปัญหาใน REPL ของบรรทัดคำสั่งเช่นกัน


1

ฉันขอสาบานต่อพระเจ้า .. การแก้ปัญหาพื้นฐานนี้ทำให้ฉันหายไปนานหลายปี มันง่ายมาก .. แต่มีข้อมูลที่คลุมเครือ / ไม่ดีอยู่มากมาย หวังว่าฉันสามารถบันทึกคนจากบางส่วนของก้นหลุมกระต่ายที่ผมสิ้นสุดลงใน ...

ดังนั้นแล้วช่วยให้ของได้รับ "สตริง" จาก "ผู้ใช้" ผ่าน "คอนโซล" ผ่านstdin,เราจะ ?

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

หากคุณต้องการโดยไม่ต้องขึ้นบรรทัดใหม่เพียงเพิ่ม ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ⱥᏪℯⅩ


4
Swift OP กล่าวว่า Swift
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. RunThe project และคลิกไฟล์ปฏิบัติการภายใต้โฟลเดอร์ Products ใน Xcode และเปิดใน finder
    3. ดับเบิลคลิกที่ไฟล์ปฏิบัติการเพื่อเปิด
    4. ตอนนี้ป้อนอินพุตของคุณ Terminal จะมีลักษณะดังนี้: ป้อนคำอธิบายภาพที่นี่

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
Timothy Swan

@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 หรืออะไรทำนองนั้น

หวังว่าฉันจะช่วยได้

แก้ไข: ไม่สามารถรับอินพุตสตริงผ่าน C ได้เนื่องจากที่นี่ฉันใช้ CInt -> ประเภทจำนวนเต็มของ C ไม่ใช่ Swift ไม่มีประเภท Swift ที่เทียบเท่าสำหรับถ่าน C * ดังนั้น String จึงไม่สามารถแปลงเป็นสตริงได้ แต่มีวิธีแก้ปัญหาที่เพียงพอในการรับอินพุตสตริง

ราอูล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.