How can I scan a rune? - go

So far, I haven't been able to print a rune by scanning it with fmt.Scan and printing it with fmt.Print. This is the vary basic code I'm working on:
package main
import "fmt"
func main() {
var c rune
fmt.Scan(&c)
fmt.Printf("%c", c)
}
But it doesn't work, in fact, Printf doesn't produce any output. Instead, by manually assigning a char to my variable c (like var c rune = 'a', without using fmt.Scan), I'm able to print the wanted rune. How can I scan a rune?

As we know Scan return n and err so please check for error under Scan statement as follows
n, err := fmt.Scan(&c)
if err != nil {
fmt.Println(err)
}
It will clearly show you the error and why it was ignored.
Other than the above, please try it locally on your own laptop instead of the playground because on the playground it most of the time gives an EOF error as most of them do not support reading from the terminal.
I hope the above helps you in debugging the issue.
Other Reference:
Scanf ignores if not provided \n

Related

In Go, how to verify that the data type of an input from the user matches the data type of the code?

I am new to Go.
Currently, I am creating a menu in Go and I want to verify that the data type of the input from the user matches the data type of the variable defined in the code. Part of my code looks like this so far:
package main
import (
"fmt"
"reflect"
)
var option int // The variable is declared outside of the main().
func general_menu() {
fmt.Println(".......................General Menu..................................")
fmt.Println()
fmt.Println("Calculator..........................................................1")
fmt.Println("Linear algebra package..............................................2")
fmt.Println("Language change.....................................................9")
fmt.Println("Exit...............................................................10")
fmt.Println()
fmt.Println("Choose an option from the menu.")
fmt.Println()
fmt.Scan(&option)
fmt.Println()
if (option != 1 && option != 2 && option != 9 && option != 10)||reflect.TypeOf(option)!=int{
fmt.Println("Wrong option input. Please, try again.")
fmt.Println()
general_menu()
}
}
I know that this doens't work this way, and I know that "int" can not be used as part of an "if" condirion.
I would kindly appreciate any suggestions on the proper way to solve this problem.
Thanks.
Edit: I have added more of my code as kindly suggested by the contributors.
Edit: Based on the answer provided, I have tried to implement a function, but the syntax is still not correct:
func check_integers_are_not_string(x int) bool {
change := strconv.Itoa(x)
if change != nil {
return true
} else {
return false
}
} // This function returns a true boolean value if conversion from int to string was possible, meaning that the entered value is a string.
Just read the documentation of Scan - https://pkg.go.dev/fmt#Scan
It returns the number of successfully read arguments and an error. The input is mapped in your case to a variable of type int, so if a user inputs a string it will return 0 and an error. Otherwise it will return 1 and the error should be nil. You can check for that.
n, err := fmt.Scan(&option)
if n != 1 || err != nil {
// print error and go back
}
One common way to do it is to try to make the conversion and see if it succeeds.
optionInt, err := strconv.Atoi(option) // Assuming option is of type string
if err != nil {
log.Printf("String '%s' cannot be converted to type int: %v", option, err)
os.Exit(1)
}
log.Printf(`optionInt is %d.`, optionInt)
This is a good approach if you are only interested in conversion to one type. Otherwise things can quickly get more involved, utilizing constructs such as lexers and parsers, but that would warrant more information on what you are trying to accomplish.

Strange performing of map data type

I am trying to add a bunch of values in a map data type and after that trying to print it out. But it is performing strangely. When I am directly calling the map with the key it is giving me the correct output but not giving me any output when I am storing the key in a variable and then calling it. I am not been able to figure it out what is happening and why am I getting this kind of output. Can Somebody help me with the same.
package main
import (
"bufio"
"fmt"
"os"
)
func main(){
type Authentication struct {
password string
}
var authentication = map[string]Authentication{}
var user1 Authentication
user1.password = "abc"
authentication["def"] = user1
reader := bufio.NewReader(os.Stdin)
usid := readString(reader)
fmt.Println(authentication)
fmt.Println(authentication[usid])
fmt.Println(authentication["def"])
}
// Reading input functions
func readString(reader *bufio.Reader) string {
s, _ := reader.ReadString('\n')
for i := 0; i < len(s); i++ {
if s[i] == '\n' {
return s[:i]
}
}
return s
}
Input:
def
Output:
map[def:{abc}]
{abc}
You're trying to do the same thing twice in readString. But all you have to do is to cut it by one byte.
func readString(reader *bufio.Reader) string {
s, _ := reader.ReadString('\n')
return s[:len(s)-1]
}
The program in the question does not work when \r\n is used as the line terminator in stdin. The program removes the trailing \n from the line, but not the \r.
Fix by using bufio.Scanner instead of bufio.Reader to read lines from the input. The bufio.Scanner type removes line terminators.
func main() {
type Authentication struct {
password string
}
var authentication = map[string]Authentication{}
var user1 Authentication
user1.password = "abc"
authentication["def"] = user1
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
log.Fatal(scanner.Err())
}
usid := scanner.Text()
fmt.Println(authentication)
fmt.Println(authentication[usid])
fmt.Println(authentication["def"])
}
There can always be a better way of reading string, but I see your code works too. I ran it in my local and it gives the expected output:
From your description, I presume you are using go playground or any such platform. If that is so, the thing is, go playground doesn't take standard input, and your code has reader on os.Stdin. When I copy your code to playground and add the following line to check,
fmt.Printf("Length of usid: %d\nusid: %q\n", len(usid), usid)
I see the following output:
Length of usid: 0
usid: ""
Conclusion: There is no issue with variables, map or code, but just the stdin.

How to convert the string representation of a Terraform set of strings to a slice of strings

I've a terratest where I get an output from terraform like so s := "[a b]". The terraform output's value = toset([resource.name]), it's a set of strings.
Apparently fmt.Printf("%T", s) returns string. I need to iterate to perform further validation.
I tried the below approach but errors!
var v interface{}
if err := json.Unmarshal([]byte(s), &v); err != nil {
fmt.Println(err)
}
My current implementation to convert to a slice is:
s := "[a b]"
s1 := strings.Fields(strings.Trim(s, "[]"))
for _, v:= range s1 {
fmt.Println("v -> " + v)
}
Looking for suggestions to current approach or alternative ways to convert to arr/slice that I should be considering. Appreciate any inputs. Thanks.
Actually your current implementation seems just fine.
You can't use JSON unmarshaling because JSON strings must be enclosed in double quotes ".
Instead strings.Fields does just that, it splits a string on one or more characters that match unicode.IsSpace, which is \t, \n, \v. \f, \r and .
Moeover this works also if terraform sends an empty set as [], as stated in the documentation:
returning [...] an empty slice if s contains only white space.
...which includes the case of s being empty "" altogether.
In case you need additional control over this, you can use strings.FieldsFunc, which accepts a function of type func(rune) bool so you can determine yourself what constitutes a "space". But since your input string comes from terraform, I guess it's going to be well-behaved enough.
There may be third-party packages that already implement this functionality, but unless your program already imports them, I think the native solution based on the standard lib is always preferrable.
unicode.IsSpace actually includes also the higher runes 0x85 and 0xA0, in which case strings.Fields calls FieldsFunc(s, unicode.IsSpace)
package main
import (
"fmt"
"strings"
)
func main() {
src := "[a b]"
dst := strings.Split(src[1:len(src)-1], " ")
fmt.Println(dst)
}
https://play.golang.org/p/KVY4r_8RWv6

"Declared but not used" variable in for's condition expression [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
My intuitive approach:
https://play.golang.org/p/6xzg7TK1IH
and it doesn't work.
Can you share some alternative ways?
You are declaring a variable (moreline) which you don't use it. You have two options here: either to replace the moreline with underscore, which means you can omit the return value.
for moreline {
line, _, err := bio.ReadLine()
if err != nil {
log.Fatal(err)
}
fmt.Println(line)
}
But a better option will be to use the ReadScanner, ReadBytes('\n') or ReadString('\n').
Checking the bufio.go file this is what you get as description for the ReadLine method:
ReadLine is a low-level line-reading primitive. Most callers should
use ReadBytes('\n') or ReadString('\n') instead or use a Scanner.
Calling UnreadByte after ReadLine will always unread the last byte read (possibly a character belonging to the line end) even if that byte is not part of the line returned by ReadLine. ReadLine either returns a non-nil line or it returns an error, never both.
So this would be a better option:
scanner := bufio.NewScanner(bio)
for scanner.Scan() {
line := scanner.Text()
fmt.Printf("%v\n", line)
}
You use := which discards the previous contents of the variables. You don't use morelines in that scope, thus the error message.
If you declare your variables beforehand and don't use :=, it works fine. The function ReadLine() might not do what you think it should do.
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
bio := bufio.NewReader(os.Stdin)
var line []byte
var err error
moreline := true
for moreline {
line, moreline, err = bio.ReadLine()
if err != nil {
log.Fatal(err)
}
fmt.Println(line)
}
}

Read multi word string from console

I realized that the following only reads a single word string -
fmt.Scan(&sentence)
How do I read multi word string - as in, the string sentence should store a string that contains multiple words.
One can use the InputReader also to scan and print multiple words from the console.
The solution code is as follows:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
inputReader := bufio.NewReader(os.Stdin)
input, _ := inputReader.ReadString('\n')
fmt.Println(input)
}
Console Input:
Let's Go!!!
Console Output:
Let's Go!!!
Note:
To run a GOLANG program, open the command prompt or powershell, navigate to the directory where the program file is present and type in the following command:
go run file_name.go
Your question refers to scanning space separated input. The definition of fmt.Scan https://golang.org/pkg/fmt/#Scan states:
Scan scans text read from standard input, storing successive space-
separated values into successive arguments. Newlines count as space.
It returns the number of items successfully scanned. If that is less
than the number of arguments, err will report why.
So, by definition, input is scanned up until the first space is found. To scan, let's say until you hit a \n on the command line you can use the code from the comment scanning spaces from stdin in Go:
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
return scanner.Text()
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
Also this thread might be useful: https://groups.google.com/forum/#!topic/golang-nuts/r6Jl4D9Juw0

Resources