วิธีการอ่านจากอินพุตมาตรฐานในคอนโซล?


270

ฉันต้องการอ่านอินพุตมาตรฐานจากบรรทัดคำสั่ง แต่ความพยายามของฉันสิ้นสุดลงด้วยการออกจากโปรแกรมก่อนที่ฉันจะได้รับแจ้งให้ป้อนข้อมูล ฉันกำลังมองหาเทียบเท่าของConsole.ReadLine ()ใน C #

นี่คือสิ่งที่ฉันมีในปัจจุบัน:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("Enter text: ")
    text, _ := reader.ReadString('\n')
    fmt.Println(text)

    fmt.Println("Enter text: ")
    text2 := ""
    fmt.Scanln(text2)
    fmt.Println(text2)

    ln := ""
    fmt.Sscanln("%v", ln)
    fmt.Println(ln)
}

รหัสนี้ดูถูกต้อง จากความอยากรู้คุณทำสิ่งนี้บนสนามเด็กเล่นหรือไม่? The Playground ไม่อนุญาตให้อินพุต stdin เนื่องจากเหตุผลด้านเครือข่าย
LinearZoetrope

ไม่เป็นไรมันเป็นเรื่องละเอียดที่คุณต้องการตัวชี้ (ดูคำตอบของฉัน) แม้ว่าฉันจะไม่แน่ใจว่าปัญหาของวิธี bufio.NewReader คืออะไรเพราะมันใช้ได้กับฉัน
LinearZoetrope

เป็นไปได้ที่ซ้ำกันของอ่านจาก stdin เริ่มต้นใน GO?
Anatoly techtonik

8
อย่าผสมการbufioบัฟเฟอร์ของตัวอ่าน (เช่นbufio.NewReader(os.Stdin)) กับการอ่านโดยตรงจากตัวอ่านที่ขีดเส้นใต้ (เช่นfmt.Scanln(x)อ่านโดยตรงจากos.Stdin) การบัฟเฟอร์อาจอ่านโดยพลการล่วงหน้า (ในกรณีเฉพาะนี้ควรfmt.Fscanln(reader,x)อ่านในภายหลังจากบัฟเฟอร์เดียวกัน)
Dave C

ฉันไม่ได้fmt.Sscanlnงานมันจะกลายเป็น "% v" หลังจากทำงาน
Beeno Tung

คำตอบ:


294

ฉันไม่แน่ใจว่ามีอะไรผิดปกติกับบล็อก

reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
fmt.Println(text)

มันทำงานบนเครื่องของฉัน อย่างไรก็ตามสำหรับบล็อกถัดไปคุณต้องมีตัวชี้ไปยังตัวแปรที่คุณกำลังกำหนดอินพุตให้ ลองเปลี่ยนด้วยfmt.Scanln(text2) fmt.Scanln(&text2)อย่าใช้Sscanlnเพราะมันจะวิเคราะห์สตริงที่มีอยู่แล้วในหน่วยความจำแทนจาก stdin หากคุณต้องการทำบางสิ่งเช่นสิ่งที่คุณพยายามทำให้แทนที่ด้วยfmt.Scanf("%s", &ln)

หากสิ่งนี้ยังใช้งานไม่ได้ผู้กระทำผิดของคุณอาจเป็นการตั้งค่าระบบแปลก ๆ หรือ IDE แบบบั๊กกี้


2
สิ่งที่ควรจะเป็นคำพูดเดียว? ReadString('\n')หรือReadString("\n")?
425nesp

8
@ 425 ไม่ต้องใช่นั่นคือ delimeter ซึ่งเป็นไบต์เดียว golang.org/pkg/bufio/#Reader.ReadString
LinearZoetrope

3
คำตอบที่ดี แต่ล้มเหลวเมื่อฉันลองใช้ backspace คีย์ ฯลฯ
kumarharsh

4
มากสำหรับ Golang ที่จะอ่านบรรทัดจากไฟล์ผ่านโปรแกรมอ่านrdถึงตัวแปรsเช่นif s,_ = rd.ReadString('\n'); true { s = strings.Trim(s, " \n") }
Nam G VU

2
เพียงแบ่งปันสิ่งที่น่าสนใจ (ฉันเป็นผู้เริ่มต้น Golang): \ n ต้องอยู่ในเครื่องหมายคำพูดเดี่ยว (อย่าพยายามใช้เครื่องหมายคำพูดคู่) มิฉะนั้นมันจะทำซ้ำสิ่งนี้:cannot use "\n" (type string) as type byte in argument to reader.ReadString
ivanleoncz

124

คุณสามารถลอง:

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}

if scanner.Err() != nil {
    // handle error.
}

6
คุณสามารถลบ "สำหรับ {}" หากคุณต้องการเพียงหนึ่งอินพุตบรรทัดเดียว
user2707671

3
หากมีห่วงสำหรับ {} วิธีการออกมาจากวงในขณะที่คุณป้อน? มีอักขระพิเศษที่จะทำให้ลูปหยุดหรือไม่ - ขอบคุณ
Madhan Ganesh

2
@Madhan scanner.Scan () ส่งคืนค่าบูลเพื่อระบุว่ากำลังออกจากลูปหรือไม่
Helin Wang

5
คุณจะได้รับข้อผิดพลาดนี้bufio.Scanner: โทเค็นยาวเกินไปหากอินพุตของคุณมีขนาดใหญ่กว่า 64 * 1024 ไบต์ นอกจากนี้อย่าลืมเพิ่มfmt.Println(scanner.Err())ด้านล่างสำหรับห่วง
Yuvaraj Loganathan

ถ้าฉันป้อน "abc \ n ^ D" สตริงที่คาดหวังคือ "abc \ n" แต่จะส่งกลับ "abc"
Shivendra Mishra

96

ฉันคิดว่าวิธีที่เป็นมาตรฐานมากกว่านี้คือ:

package main

import "fmt"

func main() {
    fmt.Print("Enter text: ")
    var input string
    fmt.Scanln(&input)
    fmt.Print(input)
}

ดูscangodoc: http://godoc.org/fmt#Scan

สแกนสแกนข้อความที่อ่านจากอินพุตมาตรฐานจัดเก็บค่าที่คั่นด้วยช่องว่างต่อเนื่องกันเป็นอาร์กิวเมนต์ที่ต่อเนื่องกัน ขึ้นบรรทัดใหม่นับเป็นช่องว่าง

Scanln นั้นคล้ายกับการสแกน แต่หยุดการสแกนที่บรรทัดใหม่และหลังจากรายการสุดท้ายจะต้องมีการขึ้นบรรทัดใหม่หรือ EOF


10
ดูเหมือนจะไม่ชอบช่องว่างในสตริงป้อนข้อมูล
Hairy Chris

3
@ FairyChris ใช่นี่มันแปลก ใน doc มันบอกว่าstops scanning at a newline and after the final item there must be a newline or EOFไม่แน่ใจว่าทำไมช่องว่าง "หยุด" มัน ... ฉันคิดว่ามันเป็นข้อผิดพลาด
karantan

6
มีข้อผิดพลาดที่เปิดขึ้นสำหรับสิ่งนี้: github.com/golang/go/issues/5703มันถูกปิดเป็น WorkingAsIntended ดูเพิ่มเติมที่: stackoverflow.com/questions/24005899/…และgroups.google.com/forum/#!topic/golang-nuts/r6Jl4D9Juw0ดูเหมือนว่าผู้คนจำนวนมากมีปัญหากับเรื่องนี้ จำเป็นต้องเปลี่ยนเอกสารหรือไม่ นอกจากนี้จากลิงก์สุดท้าย: "การสแกนและ Scanln ใช้สำหรับการแยกวิเคราะห์และสิ่งต่างๆเช่นนั้นดังนั้นการได้รับข้อความบรรทัดเดียวจาก stdin จะเอาชนะวัตถุประสงค์"
user2707671

สำหรับฉันมันทำให้สับสนจริงๆที่ fmt.Scan ในฟังก์ชั่นที่คล้ายกันของมันไม่สามารถเล่นได้ดีกับช่องว่างเช่น bufio.NewReader
FilBot3

3
ปัญหาเดียวกันของช่องว่างยังคงอยู่ในขณะที่ใช้งานfmt.Scanlnและfmt.Scanกับรุ่น 2016 go ปัจจุบัน (go version go1.6.2 linux / amd64)
Chiheb Nexus

30

พยายามใช้bufio.NewScanner เสมอในการรวบรวมข้อมูลจากคอนโซล ตามที่คนอื่น ๆ กล่าวถึงมีหลายวิธีในการทำงาน แต่สแกนเนอร์มีวัตถุประสงค์เพื่อเริ่มต้นงาน Dave Cheney อธิบายว่าทำไมคุณควรใช้เครื่องสแกนแทน buLio.Reader's ReadLine

https://twitter.com/davecheney/status/604837853344989184?lang=en

นี่คือคำตอบโค้ดสำหรับคำถามของคุณ

package main

import (
    "bufio"
    "fmt"
    "os"
)

/*
 Three ways of taking input
   1. fmt.Scanln(&input)
   2. reader.ReadString()
   3. scanner.Scan()

   Here we recommend using bufio.NewScanner
*/

func main() {
    // To create dynamic array
    arr := make([]string, 0)
    scanner := bufio.NewScanner(os.Stdin)
    for {
        fmt.Print("Enter Text: ")
        // Scans a line from Stdin(Console)
        scanner.Scan()
        // Holds the string that scanned
        text := scanner.Text()
        if len(text) != 0 {
            fmt.Println(text)
            arr = append(arr, text)
        } else {
            break
        }

    }
    // Use collected inputs
    fmt.Println(arr)
}

หากคุณไม่ต้องการรวบรวมอินพุตโดยทางโปรแกรมเพียงเพิ่มบรรทัดเหล่านี้

   scanner := bufio.NewScanner(os.Stdin)
   scanner.Scan()
   text := scanner.Text()
   fmt.Println(text)

ผลลัพธ์ของโปรแกรมข้างต้นจะเป็น:

Enter Text: Bob
Bob
Enter Text: Alice
Alice
Enter Text:
[Bob Alice]

โปรแกรมด้านบนจะรวบรวมอินพุตของผู้ใช้และบันทึกลงในอาร์เรย์ นอกจากนี้เรายังสามารถทำลายการไหลนั้นด้วยตัวละครพิเศษ สแกนเนอร์ให้ API สำหรับการใช้งานขั้นสูงเช่นการแยกโดยใช้ฟังก์ชั่นที่กำหนดเอง ฯลฯ การสแกนสตรีม I / O ประเภทต่างๆ (Stdin, String) เป็นต้น


นี่ควรเป็นคำตอบที่ยอมรับได้ ไม่เพียง แต่เป็นคำตอบที่แม่นยำยิ่งขึ้น แต่คุณภาพที่ดีขึ้น
Daniel Farrell

11

อีกวิธีในการอ่านหลายอินพุตภายในลูปซึ่งสามารถจัดการอินพุตด้วยช่องว่าง:

package main
import (
    "fmt"
    "bufio"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    var text string
    for text != "q" {  // break the loop if text == "q"
        fmt.Print("Enter your text: ")
        scanner.Scan()
        text = scanner.Text()
        if text != "q" {
            fmt.Println("Your text was: ", text)
        }
    }
}

เอาท์พุท:

Enter your text: Hello world!
Your text was:  Hello world!
Enter your text: Go is awesome!
Your text was:  Go is awesome!
Enter your text: q

2
คุณอาจจะแค่ใช้ตัวแบ่งในการตรวจสอบ "q" ภายในและห่อมันทั้งหมดในวงวนไม่สิ้นสุด คำตอบที่ยอดเยี่ยมโดยวิธี!
tebanep

2
ดูเหมือนว่าคุณยังสามารถกำจัดเงื่อนไขในลูป for ได้เช่นกัน
irbanana

6

ฉันไปงานปาร์ตี้สาย แต่ซับหนึ่ง:

data, err := ioutil.ReadAll(os.Stdin)

และกด ctrl + d (EOT) เมื่อป้อนอินพุตในบรรทัดคำสั่ง


เพราะos.Stdinไม่ได้ 'จบ' มันเป็นไปไม่ได้ที่จะอ่านทุกอย่าง คุณอาจจะรอสักครู่ ...
gypsydave5

2
กด ctrl + d เช่น eot
Shivendra Mishra

3
ใช่ว่าจะทำมัน - mailทำให้ผมนึกถึงเขียนอีเมลด้วย
gypsydave5

5

ลองรหัสนี้: -

var input string
func main() {
      fmt.Print("Enter Your Name=")
      fmt.Scanf("%s",&input)
      fmt.Println("Hello "+input)
      }

3
ดูเหมือนว่าScanf()จะไม่ยอมรับช่องว่างสีขาวในสตริง
Eslam

4

สามารถทำได้เช่นนี้: -

package main
import "fmt"     

func main(){
    var myname string
fmt.Scanf("%s", &myname)           
fmt.Println("Hello", myname)       
}

3

อ่านอย่างชัดเจนในค่าที่แจ้งสองสามอัน

// Create a single reader which can be called multiple times
reader := bufio.NewReader(os.Stdin)
// Prompt and read
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
fmt.Print("Enter More text: ")
text2, _ := reader.ReadString('\n')
// Trim whitespace and print
fmt.Printf("Text1: \"%s\", Text2: \"%s\"\n",
    strings.TrimSpace(text), strings.TrimSpace(text2))

นี่คือการวิ่ง:

Enter text: Jim
Enter More text: Susie
Text1: "Jim", Text2: "Susie"

2
ยังเป็นวิธีที่ดีตั้งแต่ strings.TrimSpace ลบ '\ n' และฉันเชื่อว่าผู้อ่าน ReadString ('\ n') ก็เป็นแพลตฟอร์มข้ามเช่นกัน
user2707671

ฉันจะเดาว่าส่วนใหญ่เวลาที่คุณต้องการลบ \ n โดยค่าเริ่มต้นนั่นคือเหตุผลที่ bufio.NewScanner ดีกว่าเป็น @Naren Yellavula คำตอบ
John Balvin Arias


0

ในกรณีของฉันโปรแกรมไม่รอเพราะฉันใช้watcherคำสั่งเพื่อรันโปรแกรมโดยอัตโนมัติ การรันโปรแกรมด้วยตนเองgo run main.goส่งผลให้ "ป้อนข้อความ" และพิมพ์ไปยังคอนโซลในที่สุด

fmt.Print("Enter text: ")
var input string
fmt.Scanln(&input)
fmt.Print(input)

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