Put string between two % in Golang - go

I have a string in Golang called mystring and I want to put it between 2 percentage signs (like this %mystring%). However until now I wasn't able to do it.
The things that I have tried are:
value := fmt.Sprintf("%%s%",mystring)
value := fmt.Sprintf("%s%s%s","%",mystring,"%")
value := fmt.Sprintf("/% %s/%",mystring)
But when I print it, in the end I receive a nil. Example: the value of mystring is "HelloWorld" then I get: %HelloWorld%nil
Right now I am receiving this as result:
/%s/%!(NOVERB)%!(EXTRA string=HelloWorld)<nil>
So, what am I missing?
Thanks.

You need to escape the %'s in format string using another %:
value := fmt.Sprintf("%%%s%%",mystring)

Use %% in your format string for an actual %.
For example:
func main() {
mystring := "hello"
value := fmt.Sprintf("%%%s%%", mystring)
fmt.Println(value)
}
Prints: %hello%
This is clearly documented at the beginning of the docs for fmt:
%% a literal percent sign; consumes no value

Related

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

golang, £ char causing weird  character

I have a function that generates a random string from a string of valid characters. I'm occasionally getting weird results when it selects a £
I've reproduced it to the following minimal example:
func foo() string {
validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~#:!£$%^&*"
var result strings.Builder
for i := 0; i < len(validChars); i++ {
currChar := validChars[i]
result.WriteString(string(currChar))
}
return result.String()
}
I would expect this to return
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~#:!£$%^&*
But it doesn't, it produces
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~#:!£$%^&*
^
where did you come from ?
if I take the £ sign out of the original validChars string, that weird A goes away.
func foo() string {
validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~#:!$%^&*"
var result strings.Builder
for i := 0; i < len(validChars); i++ {
currChar := validChars[i]
result.WriteString(string(currChar))
}
return result.String()
}
This produces
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~#:!$%^&*
A string is a type alias for []byte. Your mental model of a string is probably that it consists of a slice of characters - or, as we call it in Go: a slice of rune.
For many runes in your validChars string this is fine, as they are part of the ASCII chars and can therefore be represented in a single byte in UTF-8. However, the £ rune is represented as 2 bytes.
Now if we consider a string £, it consists of 1 rune but 2 bytes. As I've mentioned, a string is really just a []byte. If we grab the first element like you are effectively doing in your sample, we will only get the first of the two bytes that represent £. When you convert it back to a string, it gives you an unexpected rune.
The fix for your problem is to first convert string validChars to a []rune. Then, you can access its individual runes (rather than bytes) by index, and foo will work as expected. You can see it in action in this playground.
Also note that len(validChars) will give you the count of bytes in the string. To get the count of runes, use utf8.RuneCountInString instead.
Finally, here's a blog post from Rob Pike on the subject that you may find interesting.

more than one character in rune literal

I have a string as just MyString and I want to append in this data something like this:
MYString ("1", "a"), ("1", "b") //END result
My code is something like this:
query := "MyString";
array := []string{"a", "b"}
for i , v := range array{
id := "1"
fmt.Println(v,i)
query += '("{}", "{}"), '.format(id, v)
}
but I am getting two errors:
./prog.go:15:23: more than one character in rune literal
./prog.go:15:39: '\u0000'.format undefined (type rune has no field or method format)
You can't use single quotes for Strings in Go. You can only use double-quotes or backticks.
Single quotes are used for single characters, called runes
Change your line to:
query += "(\"{}\", \"{}\"), ".format(id, v)
or
query += `("{}", "{}"), `.format(id, v)
However, Go is not python. Go doesn't have a format method like that. But it has fmt.Sprintf.
So to really fix it, use:
query = fmt.Sprintf(`%s("%s", "%s"), `, query, id, v)
Issue here is single quotes . Go Compiler expects a character only when encounters '' . Rather use double quotes with escape symbol as explained in above example.

Golang Sprintf formatting a string and using it multiple times

I try to generate a sql query using Sprintf() where I have to use the same variable two times
myStr := "test"
str := Sprintf("SELECT ... WHERE a = '%#[1]s' or b = '%#[1]s'", myStr)
fmt.Println(str)
This snippets outputs the expected string
SELECT ... WHERE a = 'test' or b = 'test'
but go vet says:
unrecognized printf flag for verb 's': '#' (vet)
And I am puzzled why. Switching the printf verb to v satisfies go vet but adds " around my string. And I honestly doesn't see a mistake in using %#[1]s.
Any thoughts?
Using printf to construct queries is a bad idea, it opens you up to SQL injection.
See named parameters in the sql package.
There is no # Sprintf flag for a string verb (the flag # is e.g. adding 0x for hex values: %#x). So remove it to make your go vet troubles disappear:
myStr := "test"
str := Sprintf("SELECT ... WHERE a = '%[1]s' or b = '%[1]s'", myStr)
fmt.Println(str)
But: If any part of your constructed query (myStr) comes from external input (i.e. user input), you really should follow Hein's advise and use named parameters.

Golang enforce type for yaml.marshal

I'm running into this case where I have to marshal a map[string]interface{} and print it out. What happens is that when I print it out, the yaml lib (gopkg.in/yaml.v2) converts the ints into strings and surrounds them by quotes while the strings are printed as strings without quotes. Example:
prop1: "1"
prop2: test
prop3: test2
I need other systems to read these values but could not find a way on enforcing the numeric types when I do yaml.Marshal().
My code for the above output looks something like this:
// receive output from somewhere else
pad := "%-16s%s"
raw, err := yaml.Marshal(output)
sArr := strings.Split(raw, "\n")
for _, s := range sArr {
fmt.Fprintln(c.StdOut, fmt.Sprintf(pad, s, ""))
}
Any suggestions how to overcome this?
Thanks in advance!
JC

Resources