Komut satırı uygulamasında klavyeden giriş


94

Yeni Apple programlama dili Swift için bir komut satırı uygulaması için klavye girişi almaya çalışıyorum.

Belgeleri boşuna taradım.

import Foundation

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

Herhangi bir fikir?

Yanıtlar:


139

Bunu yapmanın doğru yolu readLine, Swift Standard Kitaplığından kullanmaktır.

Misal:

let response = readLine()

Girilen metni içeren bir İsteğe bağlı değer verecektir.


2
Teşekkürler. şifre girişi almanın bir yolu var mı?
Abhijit Gaikwad

Benim için, yanıt = nil ile bu çizgiden düşüyor. Problemin ne olduğu ile alakalı fikrin var mı?
Peter Webb

4
@PeterWebb - xcode terminalinde iyi çalışıyor, oyun alanında düşüyor :)
aprofromindia

2
Orijinal yanıtların çoğunun arkasına readLine eklendi, bu nedenle tutum biraz garantisiz IMHO
russbishop

2
Swift 3, SlimJim
Ezekiel Elin

62

C'ye düşmeden çözmeyi başardım:

Benim çözümüm aşağıdaki gibidir:

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

Xcode'un daha yeni sürümleri, açık bir typecast'e ihtiyaç duyar (Xcode 6.4'te çalışır):

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

5
Bunu beğendim, ayrıca, bir fonksiyon tanımlamak istemiyorsanız, bir programda yalnızca bir kez girişi kabul etmeniz gerekiyorsa, bir satıra kadar sıkıştırılabilir:var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
jcmiller11

3
Bunu Oyun Alanı'nda kullanıcı girişini anında kabul etmek için kullanmanın bir yolu var mı?
Rob Cameron

5
Bunu oyun alanında kullanamazsınız. Orada değişkenler kullanmalısın.
Chalkers

8
Bununla dizede yeni satırlar alacağınızı unutmayın. Onları soyunstring.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
pk-nb

3
Kullanıcı bir karakteri siler, ok tuşu vb. Kullanırsa da garip karakterler elde edersiniz. AAAGGGGGHHHH NEDEN, Swift, Neden?
ybakos

14

Aslında o kadar kolay değil, C API ile etkileşime girmeniz gerekiyor. Bunun alternatifi yok scanf. Küçük bir örnek oluşturdum:

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

Oh oğlum - Keşke bundan daha önemsiz bir şey olsaydı! Bunu bir dizi karaktere uyarlamakta zorlanıyorum.
Chalkers

1
İşlev tanımlarını saklamak için bir "UserInput.h" oluşturmak ve bu dosyayı "cliinput-Bridging-Header.h" içine dahil etmek daha iyi olacaktır.
Alan Zhiliang Feng

7

düzenleme Swift 2.2'den itibaren standart kitaplık içerir readLine. Swift'in belge yorumlarını işaretlemek için değiştirildiğini de not edeceğim. Orijinal cevabımı tarihsel bağlam için bırakıyorum.

Tamlık adına, burada kullandığım bir Swift uygulaması var readln. Okumak istediğiniz maksimum bayt sayısını (String'in uzunluğu olabilir veya olmayabilir) belirtmek için isteğe bağlı bir parametreye sahiptir.

Bu aynı zamanda swiftdoc yorumlarının doğru kullanımını gösterir - Swift bir <project> .swiftdoc dosyası oluşturur ve Xcode onu kullanır.

///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 yorumlarını ve "genel" erişim değiştiriciyi seviyorum, daha önce Swift'de böyle bir şey görmemiştim, ancak CChar ve C-String, C ve 8 bitlik karakter kümeleri ve Swift için bir gerileme gibi görünüyor tamamen Unicode metinle ilgili ... veya komut satırı araçları tamamen ASCII mi?
Kaydell

2
Getchar () işlevinin ASCII döndürdüğüne inanıyorum. Unicode terminalleri, girmek istemediğim tüm bir şey :)
russbishop

7

Genel olarak readLine () işlevi konsoldan giriş taramak için kullanılır. Ancak, siz "komut satırı aracı" eklemediğiniz sürece normal iOS projesinde çalışmayacaktır .

Test etmenin en iyi yolu şunları yapabilirsiniz:

1. Bir macOS dosyası oluşturun

görüntü açıklamasını buraya girin

2. Konsoldan isteğe bağlı String taramak için readLine () işlevini kullanın

 import Foundation

 print("Please enter some input\n")

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

Çıktı :

Please enter some input

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

görüntü açıklamasını buraya girin


5

Diğer bir alternatif, düzgün satır düzenleme (ok tuşları, vb.) Ve isteğe bağlı geçmiş desteği için libedit'i bağlamaktır . Bunu, başladığım bir proje için istedim ve onu nasıl kurduğuma dair temel bir örnek oluşturdum .

Swift'den kullanım

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

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

Libedit'i ortaya çıkarmak için ObjC sarıcı

#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

Konsol tabanlı uygulamada kullanıcıdan girdi almanın basit bir örneği: readLine () kullanabilirsiniz. İlk numara için konsoldan giriş alın ve ardından enter tuşuna basın. Bundan sonra, aşağıdaki resimde gösterildiği gibi ikinci numarayı girin:

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)

Çıktı


2

Bu soruya birçok modası geçmiş cevap. Swift 2+ itibariyle Swift Standart Kitaplığı, readline () işlevini içerir. Bir İsteğe Bağlı döndürür, ancak yalnızca EOF'ye ulaşıldığında sıfır olacaktır, bu, klavyeden girdi alınırken gerçekleşmez, böylece bu senaryolarda zorla açılabilir. Kullanıcı herhangi bir şey girmezse (sarılmamış) değeri boş bir dizge olacaktır. İşte, en az bir karakter girilene kadar kullanıcıyı uyarmak için özyinelemeyi kullanan küçük bir yardımcı program işlevi:

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

Bir şeyin diğer cevaplarda önerildiği gibi girilip girilmediğini kontrol etmek için isteğe bağlı bağlama (let input = readLine ()) kullanmanın istenen etkiyi yaratmayacağına dikkat edin, çünkü bu hiçbir zaman sıfır olmayacak ve en azından klavye girişi kabul edilirken "" olacaktır.

Bu, Oyun Alanında veya komut istemine erişiminizin olmadığı başka herhangi bir ortamda çalışmayacaktır. REPL komut satırında da sorunlar var gibi görünüyor.


1

Yemin ederim .. Bu son derece basit sorunun çözümü yıllarca benden kaçtı. Çok basit .. ama orada çok fazla belirsiz / kötü bilgi var; umarım ben kaydedebilirsiniz birisini gelen bazı dipsiz bir tavşan delikleri ben bitti o ...

Öyleyse, "konsol" aracılığıyla "kullanıcıdan" bir "dizgi" alalımstdin , olur mu?

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

son satırda OLMADAN istiyorsanız , sadece ekleyin ...

[ ... stringByTrimmingCharactersInSet:
                       NSCharacterSet.newlineCharacterSet];

Ta Da! ♥ ⱥ ᏪℯⅩ


4
Swift, OP Swift diyor.
HenryRoot 2

1
Osx dedi. Hepsi aynı şekilde derlenir! İçinizdeki ObjC'yi kucaklayın!
Alex Grey

2
Swift ile yazan ve Hedef C'yi asla öğrenemeyecek insanlar var. Bu, onun neye göre derlendiğiyle ilgili değil.
HenryRootTwo

1

Swift 5: Programı sonlandırmadan, sürekli olarak klavyeden giriş yapmak istiyorsanız, bir giriş akışı gibi, aşağıdaki adımları kullanın:

  1. Comnnad çizgi aracı türünde yeni proje oluştur Komut satırı projesi

    1. Main.swift dosyasına aşağıdaki kodu ekleyin:

      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. Projeyi çalıştırın ve Xcode'daki Ürünler klasörünün altındaki yürütülebilir dosyayı tıklayın ve bulucuda açın
    3. Açmak için yürütülebilir dosyayı çift tıklayın.
    4. Şimdi Girişlerinizi girin. Terminal şunun gibi görünecek: görüntü açıklamasını buraya girin

0

Bu soruna süslü çözümler olmadığından, Swift'deki standart girişi okuyup ayrıştırmak için küçük bir sınıf yaptım. Burada bulabilirsiniz .

Misal

Ayrıştırmak için:

+42 st_ring!
-0.987654321 12345678900
.42

Yapmalısın:

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 sınıfı nasıl içe aktarılır?
Timothy Swan

@TimothySwan, Readme'nin Kurulum bölümünde açıklandığı gibi ( github.com/shoumikhin/StreamScanner#installation ). Yani temelde StreamScanner.swift dosyasını projenize ekleyebilirsiniz.
shoumikhin


0

Bu xCode v6.2'de çalışıyor, bence bu Swift v1.2

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

0

Boşlukla ayrılmış dizeyi okumak ve dizeyi hemen bir diziye bölmek istiyorsanız, bunu yapabilirsiniz:

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

Örneğin.

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 () işlevi Xcode üzerinde çalıştırıldığında, hata ayıklama konsolu giriş için bekler. Kodun geri kalanı, giriş yapıldıktan sonra devam edecektir.

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

0

Bu sorunun en üst sıradaki yanıtı, komut satırından kullanıcı girişi almak için readLine () yönteminin kullanılmasını önerir. Ancak, kullanmanız gerektiğini belirtmek isterim! isteğe bağlı yerine bir dize döndürmek için bu yöntemi çağırırken operatör:

var response = readLine()!

Neden açmayı zorla, readLine()isteğe bağlı olarak dönüyor, bu nedenle zorla sarmayı açmak güvensizdir ve örneğe gerçekten hiçbir şey eklemez.
Camsoft

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

Bunu ObjC'de test ettim ve belki bu faydalı olabilir.


-1

Sadece xenadu uygulaması hakkında yorum yapmak istedim (yeterli tekrarım yok), çünkü CCharOS X'de Int8ve Swift, getchar()UTF-8'in parçalarını veya 7 bitin üzerindeki herhangi bir şeyi döndürdüğünde diziye eklediğinizde hiç hoşlanmıyor .

Bunun UInt8yerine bir dizi kullanıyorum ve harika çalışıyor ve UTF-8'e String.fromCStringdönüştürüyor UInt8.

Ancak ben böyle yaptı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

Artık aşağıdakileri kullanarak Swift'de Klavye girişi yapabildim:

Main.swift dosyamda bir i değişkenini tanımladım ve ona Objective C'de tanımladığım GetInt () fonksiyonunu atadım. GetInt için fonksiyon prototipini ilan ettiğim sözde Bridging Header aracılığıyla main.swift'e bağlanabilirdim. İşte dosyalar:

main.swift:

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

Köprüleme Başlığı:

#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'de c standart çıktı ve girdisi stdio.h ile birlikte Objective-C'de C programlamanızı sağlayan stdlib.h c standart kitaplığını dahil etmek mümkündür, bu da dahil etmeye gerek olmadığı anlamına gelir user.c gibi gerçek bir hızlı dosya veya bunun gibi bir şey.

Umarım yardım edebilirim

Düzenleme: C aracılığıyla String girdisi almak mümkün değil çünkü burada Swift'in değil, CInt -> C'nin tamsayı türünü kullanıyorum. C karakter * için eşdeğer bir Swift türü yoktur. Bu nedenle String, dizeye dönüştürülemez. Ancak burada String girdisi almak için oldukça yeterli çözüm var.

Raul

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.