Як читати зі стандартного вводу в консолі?


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

Цей код виглядає правильно. З цікавості ви це запускаєте на дитячому майданчику? Ігровий майданчик Go не дозволяє вводити stdin через мережеві причини.
— LinearZoetrope

Не забувайте, це виглядає як тонке питання, де вам потрібен вказівник (див. Мою відповідь). Хоча я не впевнений, у чому проблема з методом bufio.NewReader, оскільки він працює для мене.
— LinearZoetrope


8
Не змішуйте bufioбуферизацію будь-якого зчитувача (наприклад bufio.NewReader(os.Stdin)) з прямим зчитуванням з підкреслювального читача (наприклад, fmt.Scanln(x)безпосередньо зчитується з os.Stdin). Буферизація може читатися довільно далеко вперед. (У цьому конкретному випадку пізніше слід fmt.Fscanln(reader,x)читати з того самого буфера).
— Дейв С

У мене не виходить 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
@ 425nesp так, це деліметр, який є одним байтом. golang.org/pkg/bufio/#Reader.ReadString
— LinearZoetrope

3
Хороша відповідь, але це не вдається, коли я намагаюся використовувати клавіші
— зворотного простору

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
якщо є цикл для {}, як вийти з циклу під час введення? Чи є спеціальний символ, який змусить петлю зупинитися? - Спасибі
— Мадхан Ганеш

2
@Madhan scanner.Scan () повертає значення bool, щоб вказати, що виходить з циклу for чи ні.
— Хелін Ван

5
Ви отримаєте цю помилку bufio.Scanner: маркер занадто довгий Якщо ваш вхід більше 64 * 1024 байт. Також не забудьте додати fmt.Println(scanner.Err())нижче циклу for.
— 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 схожий на Scan, але зупиняє сканування на новому рядку і після остаточного елемента повинен бути новий рядок або EOF.


10
Здається, це не подобається пробілів у вхідному рядку.
— Волосатий Кріс

3
@HairyChris так це дивно. У документі сказано, що stops scanning at a newline and after the final item there must be a newline or EOFтак не впевнений, чому простір "ламає" це ... Я думаю, що це помилка
— Карантан

6
Для цього відкрилася помилка: github.com/golang/go/isissue/5703 Закрився як WorkingAsIntended. Дивіться також: stackoverflow.com/questions/24005899/… та groups.google.com/forum/#!topic/golang-nuts/r6Jl4D9Juw0 Здається, у багатьох людей із цим проблеми. Потрібна зміна документації? Крім того, з останнього посилання: "Scan і Scanln призначені для розбору і подібних матеріалів, тож отримання лише одного рядка тексту з stdin перешкоджає меті".
— user2707671

Для мене це дійсно заплутало цей fmt.Scan в будь-якій з його подібних функцій, не грає добре з просторами, як у bufio.NewReader.
— FilBot3

3
Та ж проблема з пробілами залишається під час використання fmt.Scanlnта fmt.Scanз поточною версією 2016 go (go версія go1.6.2 linux / amd64).
— Chiheb Nexus

30

Завжди намагайтеся використовувати bufio.NewScanner для збору вводу з консолі. Як згадували інші, існує кілька способів виконати роботу, але сканер спочатку призначений для виконання цієї роботи. Дейв Чейні пояснює, чому слід використовувати Scanner замість bufio.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 для розширеного використання, наприклад розбиття за допомогою спеціальної функції тощо, сканування різних типів потоків вводу / виводу (Stdin, String) тощо.


Це має бути прийнятою відповіддю. Це не тільки більш точна відповідь, але й краща якість.
— Даніель Фаррелл

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 for також зараз.
— ірбанана

6

Я спізнююсь на вечірку. А як щодо одного лайнера:

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

і натисніть ctrl + d (EOT), як тільки введений вхід в командному рядку.


Тому os.Stdinщо не закінчується, неможливо все прочитати. Можливо, ви будете чекати деякий час
— gypsydave5

2
натисніть ctrl + d, тобто eot.
— Шивендра Мішра

3
Так, це зробило б це - нагадує мені писати електронні листи mail.
— gypsydave5

5

Спробуйте цей код: -

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

3
Здається Scanf(), не приймає пробілів у рядку
— Еслам

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
— Джон Балвін Аріас

2

Вам потрібно надати вказівник на вар, який ви хочете сканувати, наприклад:

fmt.scan(&text2)

0

У моєму випадку програма не чекала, тому що я використовував watcherкоманду для автоматичного запуску програми. Запуск програми вручну go run main.goпризвів до "Введення тексту" та, врешті-решт, друку до консолі.

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

2
Обмеженням Scan*сім'ї є те, що вони читають до роздільника пробілів (наприклад, пробілу).
— Джордж Церес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.