if statement always being skipped - if-statement

I'm trying to make a basic little banking program to get my bearing with Go. I run the program and when I type in my answer for either of the if statements, the program just moves on. Any solutions?
here's my code:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter your name: ")
name, _ := reader.ReadString('\n')
fmt.Print("Hello ", name)
balance := 0
fmt.Print("Do you want to deposite? (y/n) ")
doDeposite, _ := reader.ReadString('\n')
if strings.TrimRight(doDeposite, "\n") == "y" {
fmt.Print("How much would you like to deposite? ")
depositeAmount, _ := reader.ReadString('\n')
da, _ := strconv.Atoi(depositeAmount)
balance += balance + da
fmt.Print("Your balance is ", balance)
} else {
fmt.Print("Would you like to withdraw?(y/n) ")
doWithdraw, _ := reader.ReadString('\n')
if strings.TrimRight(doWithdraw, "\n") == "y" {
fmt.Print("How much would you like to withdraw? ")
withdrawAmount, _ := reader.ReadString('\n')
wa, _ := strconv.Atoi(withdrawAmount)
balance += balance + wa
fmt.Print("Your balance is ", balance)
}
}
}

try to use ReadLine() method instead of ReadString()
the doc say
ReadLine tries to return a single line, not including the end-of-line bytes.
ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter
Here is the updated deposit code for reference:
[...]
fmt.Print("How much would you like to deposit? ")
depositAmount, _, err := reader.ReadLine()
if err != nil {
fmt.Printf("ReadLine() error: '%s'", err)
}
da, err := strconv.Atoi(string(depositAmount))
if err != nil {
fmt.Printf("strconv error: '%s'", err)
}
balance += balance + da
fmt.Print("Your balance is ", balance)
[...]
Alternatively, you could trim based on which OS are you executing your code.
if runtime.GOOS == "windows" {
input = strings.TrimRight(input, "\r\n")
} else {
input = strings.TrimRight(input, "\n")
}

Related

unsupported Perl syntax: `(?<`

I want to parse the result of the cmd 'gpg --list-keys' to display it on the browser.
The cmd ouput is like this:
pub rsa3072 2021-08-03 [SC] [expires: 2023-08-03]
07C47E284765D5593171C18F00B11D51A071CB55
uid [ultimate] user1 <user1#example.com>
sub rsa3072 2021-08-03 [E] [expires: 2023-08-03]
pub rsa3072 2021-08-04 [SC]
37709ABD4D96324AB8CBFC3B441812AFBCE7A013
uid [ultimate] user2 <user2#example.com>
sub rsa3072 2021-08-04 [E]
I expect something like this :
{
{uid : user1#example.com},
{uid : user2#example.com},
}
Here is the code:
type GPGList struct{
uid string
}
//find list keys
func Findlistkeys(){
pathexec, _ := exec.LookPath("gpg")
cmd := exec.Command(pathexec, "--list-keys")
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
printCommand(cmd)
err := cmd.Run()
printError(err)
output := cmdOutput.Bytes()
printOutput(output)
GPG := GPGList{}
parseOutput(output, &GPG)
fmt.Println(GPG)
}
func printCommand(cmd *exec.Cmd) {
fmt.Printf("==> Executing: %s\n", strings.Join(cmd.Args, " "))
}
func printError(err error) {
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("==> Error: %s\n", err.Error()))
}
}
func printOutput(outs []byte) {
if len(outs) > 0 {
fmt.Printf("==> Output: %s\n", string(outs))
}
}
func parseOutput(outs []byte, GPG *GPGList) {
var uid = regexp.MustCompile(`(?<=\<)(.*?)(?=\>)`)
fmt.Println(uid)
}
It ends with the following message :
panic: regexp: Compile(`(?<=\<)(.*?)(?=\>)`): error parsing regexp: invalid or unsupported Perl syntax: `(?<
So far I'm stack with the regex.
It don't understand why it don't want to compile...
What is wrong with it?
I've tested the regex on online simulator and it looks OK, yet there is something wrong with it.
Any suggestion please?
The regexp package uses the syntax accepted by RE2. From https://github.com/google/re2/wiki/Syntax
(?<=re) after text matching re (NOT SUPPORTED)
Hence the error message:
error parsing regexp: invalid or unsupported Perl syntax: (?<
The online simulator is likely testing a different regular expression syntax. You will need to find an alternative regular expression encoding or a different regular expression package.
An alternative encoding you can try is \<([^\>]*)\> (playground). This is quite simple and may not match your original intent.
Here is another solution based on gpg --list-keys --with-colons machine readable output.
It is still a slow solution, but easy to write, easy to update, does not use regular expressions.
A smart folk can come with an even faster solution without adding a crazy wall of complexity. (just loop over the string until < then capture the string until >)
this is based on a simple csv reader, so you can plug it onto the output stream of a command.Exec instance, or whatever else.
The big advantage is that it does not need to buffer the whole data in memory, it can stream decode.
package main
import (
"encoding/csv"
"fmt"
"io"
"regexp"
"strings"
)
func main() {
fmt.Printf("%#v\n", extractEmailsCSV(csvInput))
}
var uid = regexp.MustCompile(`\<(.*?)\>`)
func extractEmailsRegexp(input string) (out []string) {
submatchall := uid.FindAllString(input, -1)
for _, element := range submatchall {
element = strings.Trim(element, "<")
element = strings.Trim(element, ">")
out = append(out, element)
}
return
}
func extractEmailsCSV(input string) (out []string) {
r := strings.NewReader(input)
csv := csv.NewReader(r)
csv.Comma = ':'
csv.ReuseRecord = true
csv.FieldsPerRecord = -1
for {
records, err := csv.Read()
if err == io.EOF {
break
} else if err != nil {
panic(err)
}
if len(records) < 10 {
continue
}
r := records[9]
if strings.Contains(r, "#") {
begin := strings.Index(r, "<")
end := strings.Index(r, ">")
if begin+end > 0 {
out = append(out, r[begin+1:end])
}
}
}
return
}
var regexpInput = `
pub rsa3072 2021-08-03 [SC] [expires: 2023-08-03]
07C47E284765D5593171C18F00B11D51A071CB55
uid [ultimate] user1 <user1#example.com>
sub rsa3072 2021-08-03 [E] [expires: 2023-08-03]
pub rsa3072 2021-08-04 [SC]
37709ABD4D96324AB8CBFC3B441812AFBCE7A013
uid [ultimate] user2 <user2#example.com>
sub rsa3072 2021-08-04 [E]
`
var csvInput = `pub:u:1024:17:51FF9A17136C5B87:1999-04-24::59:-:Tony Nelson <tnelson#techie.com>:
uid:u::::::::Tony Nelson <tnelson#conceptech.com>:
`
We dont exactly have the same benchmark setup, but anyways. If you think it bloats the comparison feel free to provide better bench setup.
Here is the benchmark setup
package main
import (
"strings"
"testing"
)
func BenchmarkCSV_1(b *testing.B) {
input := strings.Repeat(csvInput, 1)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = extractEmailsCSV(input)
}
}
func BenchmarkRegExp_1(b *testing.B) {
input := strings.Repeat(regexpInput, 1)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = extractEmailsRegexp(input)
}
}
func BenchmarkCSV_10(b *testing.B) {
input := strings.Repeat(csvInput, 10)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = extractEmailsCSV(input)
}
}
func BenchmarkRegExp_10(b *testing.B) {
input := strings.Repeat(regexpInput, 10)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = extractEmailsRegexp(input)
}
}
func BenchmarkCSV_100(b *testing.B) {
input := strings.Repeat(csvInput, 100)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = extractEmailsCSV(input)
}
}
func BenchmarkRegExp_100(b *testing.B) {
input := strings.Repeat(regexpInput, 100)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = extractEmailsRegexp(input)
}
}
And here is the result
BenchmarkCSV_1
BenchmarkCSV_1-4 242736 4200 ns/op 5072 B/op 18 allocs/op
BenchmarkRegExp_1
BenchmarkRegExp_1-4 252232 4466 ns/op 400 B/op 9 allocs/op
BenchmarkCSV_10
BenchmarkCSV_10-4 68257 17335 ns/op 7184 B/op 40 allocs/op
BenchmarkRegExp_10
BenchmarkRegExp_10-4 29871 39947 ns/op 3414 B/op 68 allocs/op
BenchmarkCSV_100
BenchmarkCSV_100-4 7538 141609 ns/op 25872 B/op 223 allocs/op
BenchmarkRegExp_100
BenchmarkRegExp_100-4 1726 674718 ns/op 37858 B/op 615 allocs/op
In terms of raw speed and allocations regular expression is better on small dataset, though as soon there is a little bit of data regular expressions are slower and allocates mores by a significant factor.
read also https://pkg.go.dev/testing
My conclusion is, don't use regular expressions ... also, optimizing regexp are hard if not impossible, where as optimizing an algorithm to parse some text input is doable, if not easy.
to summarize, even the fastest and best runtime is nothing without a well thought programmer to drive it.
So I updated the regex...but since (?<=\<)(.*?)(?=\>) was working on online simulator, I really got surprised.
Why can't regex work the same with all languages...
func parseOutput(outs []byte, GPG *GPGList) {
var uid = regexp.MustCompile(`\<(.*?)\>`)
submatchall := uid.FindAllString(string(outs), -1)
for _, element := range submatchall {
element = strings.Trim(element, "<")
element = strings.Trim(element, ">")
fmt.Println(element)
}
}

How remove email-address from string?

So, I have a string and I want to remove the e-mail adress from it if there is one.
As example:
This is some text and it continues like this
until sometimes an email
adress shows up asd#asd.com
also some more text here and here.
I want this as a result.
This is some text and it continues like this
until sometimes an email
adress shows up [email_removed]
also some more text here and here.
cleanFromEmail(string)
{
newWordString =
space := a_space
Needle = #
wordArray := StrSplit(string, [" ", "`n"])
Loop % wordArray.MaxIndex()
{
thisWord := wordArray[A_Index]
IfInString, thisWord, %Needle%
{
newWordString = %newWordString%%space%(email_removed)%space%
}
else
{
newWordString = %newWordString%%space%%thisWord%%space%
;msgbox asd
}
}
return newWordString
}
The problem with this is that I end up loosing all the line-breaks and only get spaces. How can I rebuild the string to look just like it did before removing the email-adress?
That looks rather complicated, why not use RegExReplace instead?
string =
(
This is some text and it continues like this
until sometimes an email adress shows up asd#asd.com
also some more text here and here.
)
newWordString := RegExReplace(string, "\S+#\S+(?:\.\S+)+", "[email_removed]")
MsgBox, % newWordString
Feel free to make the pattern as simple or as complicated as you want, depending on your needs, but RegExReplace should do it.
If for some reason RegExReplace doesn't always work for you, you can try this:
text =
(
This is some text and it continues like this
until sometimes an email adress shows up asd#asd.com.
also some more text here and here.
)
MsgBox, % cleanFromEmail(text)
cleanFromEmail(string){
lineArray := StrSplit(string, "`n")
Loop % lineArray.MaxIndex()
{
newLine := ""
newWord := ""
thisLine := lineArray[A_Index]
If InStr(thisLine, "#")
{
wordArray := StrSplit(thisLine, " ")
Loop % wordArray.MaxIndex()
{
thisWord := wordArray[A_Index]
{
If InStr(thisWord, "#")
{
end := SubStr(thisWord, 0)
If end in ,,,.,;,?,!
newWord := "[email_removed]" end ""
else
newWord := "[email_removed]"
}
else
newWord := thisWord
}
newLine .= newWord . " " ; concatenate the outputs by adding a space to each one
}
newLine := trim(newLine) ; remove the last space from this variable
}
else
newLine := thisLine
newString .= newLine . "`n"
}
newString := trim(newString)
return newString
}

Remove all articles and other strings from a string using Go?

Is there any method in Go or having regular expression that it will remove only the articles used in the string?
I have tried below code that will do it but it will also remove other words from the string I'm showing the code below:
removalString := "This is a string"
stringToRemove := []string{"a", "an", "the", "is"}
for _, wordToRemove := range stringToRemove {
removalString = strings.Replace(removalString, wordToRemove, "", -1)
}
space := regexp.MustCompile(`\s+`)
trimedExtraSpaces := space.ReplaceAllString(removalString, " ")
spacesCovertedtoDashes := strings.Replace(trimedExtraSpaces, " ", "-", -1)
slug := strings.ToLower(spacesCovertedtoDashes)
fmt.Println(slug)
Edited
Play link
In this It will remove the is which is used in the this.
The Expected output is this-string
You can use strings.Split and strings.Join plus a loop for filtering and then building it together again:
removalString := "This is a string"
stringToRemove := []string{"a", "an", "the", "is"}
filteredStrings := make([]string, 0)
for _, w := range strings.Split(removalString, " ") {
shouldAppend := true
lowered := strings.ToLower(w)
for _, w2 := range stringToRemove {
if lowered == w2 {
shouldAppend = false
break
}
}
if shouldAppend {
filteredStrings = append(filteredStrings, lowered)
}
}
resultString := strings.Join(filteredStrings, "-")
fmt.Printf(resultString)
Outpus:
this-string
Program exited.
Here you have the live example
My version just using regexp
Construct a regexp of the form '\ba\b|\ban\b|\bthe\b|\bis\b|' which will find
the words in the list that have "word boundaries" on both sides - so "This" is not matched
Second regexp reduces any spaces to dashes and makes multiple spaces a single dash
package main
import (
"bytes"
"fmt"
"regexp"
)
func main() {
removalString := "This is a strange string"
stringToRemove := []string{"a", "an", "the", "is"}
var reg bytes.Buffer
for _, x := range stringToRemove {
reg.WriteString(`\b`) // word boundary
reg.WriteString(x)
reg.WriteString(`\b`)
reg.WriteString(`|`) // alternation operator
}
regx := regexp.MustCompile(reg.String())
slug := regx.ReplaceAllString(removalString, "")
regx2 := regexp.MustCompile(` +`)
slug = regx2.ReplaceAllString(slug, "-")
fmt.Println(slug)
}

Golang regular expression for parsing key value pair into a string map

I'm looking to parse the following string into a map[string]string using a regular expression:
time="2017-05-30T19:02:08-05:00" level=info msg="some log message" app=sample size=10
I'm trying to create a map that would have
m["time"] = "2017-05-30T19:02:08-05:00"
m["level"] = "info"
etc
I have tried using regex.FindAllStringIndex but can't quite come up with an appropriate regex? Is this the correct way to go?
This is not using regex but is just an example of how to achieve the same by using strings.FieldsFunc.
https://play.golang.org/p/rr6U8xTJZT
package main
import (
"fmt"
"strings"
"unicode"
)
const foo = `time="2017-05-30T19:02:08-05:00" level=info msg="some log message" app=sample size=10`
func main() {
lastQuote := rune(0)
f := func(c rune) bool {
switch {
case c == lastQuote:
lastQuote = rune(0)
return false
case lastQuote != rune(0):
return false
case unicode.In(c, unicode.Quotation_Mark):
lastQuote = c
return false
default:
return unicode.IsSpace(c)
}
}
// splitting string by space but considering quoted section
items := strings.FieldsFunc(foo, f)
// create and fill the map
m := make(map[string]string)
for _, item := range items {
x := strings.Split(item, "=")
m[x[0]] = x[1]
}
// print the map
for k, v := range m {
fmt.Printf("%s: %s\n", k, v)
}
}
Instead of writing regex of your own, you could simply use the github.com/kr/logfmt package.
Package implements the decoding of logfmt key-value pairs.
Example logfmt message:
foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf
Example result in JSON:
{
"foo": "bar",
"a": 14,
"baz": "hello kitty",
"cool%story": "bro",
"f": true,
"%^asdf": true
}
Use named capturing groups in your regular expression and the FindStringSubmatch and SubexpNames functions. E.g.:
s := `time="2017-05-30T19:02:08-05:00" level=info msg="some log message" app=sample size=10`
re := regexp.MustCompile(`time="(?P<time>.*?)"\slevel=(?P<level>.*?)\s`)
values := re.FindStringSubmatch(s)
keys := re.SubexpNames()
// create map
d := make(map[string]string)
for i := 1; i < len(keys); i++ {
d[keys[i]] = values[i]
}
fmt.Println(d)
// OUTPUT: map[time:2017-05-30T19:02:08-05:00 level:info]
values is a list containing all submatches. The first submatch is the whole expression that matches the regexp, followed by a submatch for each capturing group.
You can wrap the code into a function if you need this more frequently (i.e. if you need something like pythons match.groupdict):
package main
import (
"fmt"
"regexp"
)
func groupmap(s string, r *regexp.Regexp) map[string]string {
values := r.FindStringSubmatch(s)
keys := r.SubexpNames()
// create map
d := make(map[string]string)
for i := 1; i < len(keys); i++ {
d[keys[i]] = values[i]
}
return d
}
func main() {
s := `time="2017-05-30T19:02:08-05:00" level=info msg="some log message" app=sample size=10`
re := regexp.MustCompile(`time="(?P<time>.*?)"\slevel=(?P<level>.*?)\s`)
fmt.Println(groupmap(s, re))
// OUTPUT: map[time:2017-05-30T19:02:08-05:00 level:info]
}

What is wrong with this StringReplace code?

I have this string XXX:ABC. I want to remove XXX: so that the string becomes ABC .
The variable Symbol contains the string XXX:ABC .
The code as follows:
MsgBox, Symbol %Symbol%
SearchText := "XXX:"
ReplaceText := ""
StringReplace, newSymbol, Symbol, SearchText, ReplaceText, ALL
MsgBox, newSymbol %newSymbol%
From the message box output, newSymbol content is the same as Symbol. Can someone tell me what is wrong with my code?
I am using Autohotkey v1.1.14.03.
For command parameters, you have to distinguish between variable parameters and value parameters.
StringReplace for instance has the following argument list:
StringReplace, OutputVar, InputVar, SearchText [, ReplaceText,
ReplaceAll?]
The docs say furthermore:
OutputVar: The name of the variable in which to store the result
of the replacement process.
InputVar: The name of the variable whose contents will be read
from.
SearchText: The string to search for.
As you can see, some parameters are expected to be variable names, whereas others are expected to be values like strings or numbers. You can use variable contents as value parameters by either enclosing them in percent signs or using them within an expression:
StringReplace, newSymbol, Symbol, %SearchText%, %ReplaceText%, ALL
; or as an expression
StringReplace, newSymbol, Symbol, % SearchText, % ReplaceText, ALL
With the newer StrReplace() function I noticed I could not use it with variables for whatever reason. And the documentation here: https://autohotkey.com/docs/commands/StringReplace.htm
is lacking an example. After a lot of tests, couldn't figure it out.
So I wrote a "polyfill" for StrReplace, complete with test code.
; Author: John Mark Isaac Madison
; EMAIL : J4M4I5M7#hotmail.com
; I_SRC : input source text
; I_OLD : old token to find
; I_NEW : new token to replace old with
FN_POLYFILL_STR_REPLACE(I_SRC, I_OLD, I_NEW)
{
;Check length of input parameters:
;--------------------------------------------;
L1 := StrLen(I_SRC)
L2 := StrLen(I_OLD)
L3 := StrLen(I_NEW)
if( !(L1 > 0) )
{
msgbox BAD_PARAM_#1:STR_REP
}
if( !(L2 > 0) )
{
msgbox BAD_PARAM_#2:STR_REP
}
if( !(L3 > 0) )
{
msgbox BAD_PARAM_#3:STR_REP
}
;--------------------------------------------;
OP := "" ;output string
f_ptr := 0 ;fill pointer
max_i := StrLen(I_SRC)
dx := 0 ;<--Loop counter / index
LOOP ;;[LOOP_START];;
{
dx++
if(dx > max_i)
{
break ;[BAIL_OUT_OF_LOOP]
}
h := FN_IS_TOKEN_HERE(I_SRC, I_OLD, dx)
;if(8==dx)
;{
; msgbox, HACK_8 dx[%dx%] h[%h%] I_SRC[%I_SRC%] I_OLD[%I_OLD%]
; src_len := StrLen( I_SRC )
; old_len := StrLen( I_OLD )
; msgbox src_len [%src_len%] old_len[%old_len%] I_OLD[%I_OLD%]
;}
if( h > 0)
{
;token found, replace it by concating
;the I_NEW onto output string:
OP := OP . I_NEW
;OP := OP . "[X]"
;msgbox : I_SRC[%I_SRC%] h[%h%] dx[%dx%]
;jump pointer to last character of
;the found token to skip over
;now irrelevant characters:
dx := h
;msgbox, DX: %dx%
}
else
if( 0 == h)
{
msgbox, "H_SHOULD_NOT_BE_ZERO"
}
else
if( h < 0 )
{
;concat character to output:
c := SubStr(I_SRC,dx,1)
OP := OP . c
}
} ;;[LOOP_END];;
msgbox OP : %OP%
;msgbox I_SRC[ %I_SRC%] I_OLD[ %I_OLD%] I_NEW[ %I_NEW%]
return OP ;;<--return output string
}
;Author: John Mark Isaac Madison
;EMAIL : J4M4I5M7#hotmail.com
;unit-test that will run when script boots up:
FN_POLYFILL_STR_REPLACE_TEST()
{
T1 := FN_POLYFILL_STR_REPLACE("WHAT_IS_UP","UP","DOWN")
;;msgbox, T1 : %T1%
i_src := "123_TOKEN_123"
i_old := "TOKEN"
i_new := "NEEEW"
T2 := FN_POLYFILL_STR_REPLACE(i_src,i_old,i_new)
;;msgbox, T2 : %T2%
;;msgbox, "POLYFILL_TEST_RAN"
i_src := "const IS_VARNAME"
i_old := "VARNAME"
i_new := "BASH"
T3 := FN_POLYFILL_STR_REPLACE(i_src,i_old,i_new)
;msgbox, T3 : %T3%
i_src := "123456VARNAME"
i_old := "VARNAME"
i_new := "AB"
T4 := FN_POLYFILL_STR_REPLACE(i_src,i_old,i_new)
if(T1 != "WHAT_IS_DOWN")
{
msgbox [PSR_TEST_FAIL#1]
}
if(T2 != "123_NEEEW_123")
{
msgbox [PSR_TEST_FAIL#2]
}
if(T3 != "const IS_BASH")
{
msgbox [PSR_TEST_FAIL#3]
}
if(T4 != "123456AB")
{
msgbox [PSR_TEST_FAIL#4]
}
return ;rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr;
}
FN_POLYFILL_STR_REPLACE_TEST()
Also, be aware that trimming out newlines in your data is harder than it
should be as well. And will be bound to throw a monkey wrench into whatever string parsing you are doing.
Problem still exists in newer versions.
solve by this alternative syntax for expressions:
newSymbol := StrReplace(Symbol, "" . SearchText, "" . ReplaceText)