Unexpected StrComp result when string is sent from golang app - go

In my code below I've set up a ReadString which reads user input and passes it along in a exec.Command.
This works just fine, but when I try to compare the string with a hardcoded string in vbscript (in this case I'm comparing it to "hello") it always fails even when the user input is "hello" as well.
If I just run the vbscript through the command line like this however...
cscript.exe script.vbs hello
...then the StrComp works as intended so I suspect that it's either a data type issue or there's some extra character that's passed along in the golang app.
Here's the main.go:
package main
import (
"fmt"
"os/exec"
"bufio"
"os"
)
func main() {
buf := bufio.NewReader(os.Stdin)
fmt.Print("Type something: ")
text, err := buf.ReadString('\n')
if err != nil {
fmt.Println(err)
} else {
args := []string{"./script.vbs", string(text)}
exec.Command("cscript.exe", args...).Run()
}
}
And here's the script.vbs
MsgBox(WScript.Arguments(0))
If StrComp(WScript.Arguments(0), "hello") = 0 Then
MsgBox("it's the same")
Else
MsgBox("It's not the same...")
End If

When working with windows, line endings are "\r\n". I don't know whether ReadString() should remove the delimiter, but even then text will contain an invisible \r. Use strings.TrimSpace to be on the save side:
package main
import (
"fmt"
"os/exec"
"bufio"
"os"
"strings"
)
func main() {
buf := bufio.NewReader(os.Stdin)
fmt.Print("Type something: ")
text, err := buf.ReadString('\n')
fmt.Printf("0 got: %T %v %q\r\n", text, text, text)
text = strings.TrimSpace(text)
fmt.Printf("1 got: %T %v %q", text, text, text)
if err != nil {
fmt.Println(err)
} else {
args := []string{"./script.vbs", string(text)}
exec.Command("cscript.exe", args...).Run()
}
}
output (of main; use your imagination for the VBScript MsgBoxes):
main
Type something: hello
0 got: string hello
"hello\r\n"
1 got: string hello "hello"

Related

How to read a string with fmt.Scan after using bufio for reading a line in Go?

I read a line with bufio.NewReader(os.Stdin), then I read a string with fmt.Scanf.
package main
import (
"fmt"
"bufio"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
var str string
inp, _ := reader.ReadString('\n')
fmt.Scanf("%s", &str)
fmt.Println(inp)
fmt.Printf(str)
}
Input:
This is a sentence.
John
I expect the output to be like above, but it isn't.
Output:
This is a sentence.
actually fmt.Scanf("%s", &str) doesn't work.
What is the problem? and How can I fix it?
reader.ReadString(delim) reads everything up to the delim, including the delimiter. So, it adds \n between two inputs. fmt.Printf(str) does not have \n in the end, so the second output sticks to the next thing printed to stdout.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
var str string
inp, _ := reader.ReadString('\n')
fmt.Scanf("%s", &str)
fmt.Println(inp)
fmt.Printf(str)
}
Input:
some line
John
Output:
some line
John
Below is the code that runs as you want it to.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
var str string
inp, _ := reader.ReadString('\n')
fmt.Scanf("%s", &str)
fmt.Print(inp)
fmt.Printf("%s\n", str)
}

Why Go split by newline separator needs to be escaped when received as an argument

package main
import (
"fmt"
"os"
"strings"
)
func main() {
arguments := os.Args
words := strings.Split(arguments[1], "\n")
fmt.Println(words)
fmt.Println(words[0])
}
example:
go run main.go "hello\nthere"
output:
[hello\nthere]
hello\nthere
expected:
[hello there]
hello
why does the separator for the newline "\n" needs to be escaped "\\n" to get the expected result?
Because you don't need to escape the newline if used like this https://play.golang.org/p/UlRISkVa8_t
You're assuming that Go sees your input as:
"hello\nthere"
but it really sees your input as:
`hello\nthere`
So, if you want that input to be recognized as a newline, then you need to unquote it. But that's a problem, because it doesn't have quotes either. So you need to add quotes, then remove them before you can continue with your program:
package main
import (
"fmt"
"strconv"
)
func unquote(s string) (string, error) {
return strconv.Unquote(`"` + s + `"`)
}
func main() {
s, err := unquote(`hello\nthere`)
if err != nil {
panic(err)
}
fmt.Println(s)
}
Result:
hello
there
You could try to pass a ANSI C like string
go run main.go $'hello\nthere'

Passing variable as argument for exec.Command()

I want to read user input and use it as an argument for a command. I got this code:
package main
import (
"bufio"
"fmt"
"log"
"os"
"os/exec"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter img path: ")
imgPath, _ := reader.ReadString('\n')
args := []string{imgPath, "stdout", "-l spa+eng"}
out, err := exec.Command("tesseract", args...).Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
}
But when I execute it it outputs an error saying exit status 1.
If instead of using the variable imgPath as an argument I write some text directly into the array it works like a charm.
The following code returns a line with the delimiter (I work on Windows and its EOL is '\r\n'), something that wasn't shown when I printed it on the console.
reader := bufio.NewReader(os.Stdin)
imgPath, _ := reader.ReadString('\n')
In my case it ended up working after I trimmed '\r\n' from the input:
package main
import (
"bufio"
"fmt"
"log"
"os"
"os/exec"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter img path: ")
imgPath, _ := reader.ReadString('\n')
imgPath= strings.TrimRight(line, "\r\n")
args := []string{imgPath, "stdout", "-l spa+eng"}
out, err := exec.Command("tesseract", args...).Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
}

Checking user input string

I am new in GoLang and I am encountering a problem with this condition:
Even if the input of the user is "1", it doesn't enter in the if statement.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"math"
"strings"
)
func prompt(toprint string) string{
if(toprint == ""){
toprint = "Enter text :";
}
reader := bufio.NewReader(os.Stdin);
fmt.Println(toprint);
text, _ := reader.ReadString('\n');
return text;
}
func main() {
choice := prompt("Please enter '1'");
if(strings.Compare("1",choice)==0||choice=="1"){
// D'ONT ENTER HERE EVEN WHEN choice=="1"
}else{
// Always go here
}
}
Thank you for your help.
This is because reader.ReadString returns all the text including the delimiter, so the string returned will be 1\n not just 1. From the documentation (my emphasis):
func (*Reader) ReadString
func (b *Reader) ReadString(delim byte) (string, error)
ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter. If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF). ReadString returns err != nil if and only if the returned data does not end in delim. For simple uses, a Scanner may be more convenient.
Perhaps you want to do
return strings.TrimSpace(text)
at the end of prompt().
Thank you !
Here's the "prompt()" code which returns the correct input :
func prompt(toprint string) string{
if(toprint == ""){
toprint = "Enter text :";
}
reader := bufio.NewReader(os.Stdin);
fmt.Println(toprint);
text, _ := reader.ReadString('\n');
return text[0:len(text)-2];
}

How check whether a file contains a string or not?

I've tried to search on Google for pattern matching function between file and string but I could not find it. I've also tried to use strings.Contains(), but it gives wrong result in large input file.
Is there any function in Go for searching string in some file?
If no, is there another way to resolve this problem?
Here is my code:
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
// read the whole file at once
b, err := ioutil.ReadFile("input.txt")
if err != nil {
panic(err)
}
s := string(b)
length := len(s)
//check whether s contains substring text
fmt.Println(strings.Contains(s, text))
}
If I read your question correctly you want to read from a file and determine if a string entered at the command line is in that file... And I think the problem that you are seeing has to do with the string delimiter, the reader.ReadString('\n') bit, and not string.Contains().
In my opinion it will be a little bit easier to make what you want work with fmt.Scanln; it will simplify things and will return a result that I'm pretty sure is what you want. Try this variation of your code:
package main
import (
"fmt"
"io/ioutil"
"strings"
)
func main() {
var text string
fmt.Print("Enter text: ")
// get the sub string to search from the user
fmt.Scanln(&text)
// read the whole file at once
b, err := ioutil.ReadFile("input.txt")
if err != nil {
panic(err)
}
s := string(b)
// //check whether s contains substring text
fmt.Println(strings.Contains(s, text))
}
I am just adding a flag to use command line arguments. If nothing is passed it will prompt you :).
package main
import (
"flag"
"fmt"
"io/ioutil"
"strings"
)
//Usage go run filename -text=dataYouAreLookingfor
//if looking for Nissan in file the command will be
// go run filename -text=Nissan
func main() {
var text string
// use it as cmdline argument
textArg := flag.String("text", "", "Text to search for")
flag.Parse()
// if cmdline arg was not passed ask
if fmt.Sprintf("%s", *textArg) == "" {
fmt.Print("Enter text: ")
// get the sub string to search from the user
fmt.Scanln(&text)
} else {
text = fmt.Sprintf("%s", *textArg)
}
// read the whole file at once
b, err := ioutil.ReadFile("input.txt")
if err != nil {
panic(err)
}
s := string(b)
// //check whether s contains substring text
fmt.Println(strings.Contains(s, text))
}

Resources