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


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.