How to add variable to string variable in golang - go

I'm trying to add a value to a variable string in golang, without use printf because I'm using revel framework and this is for a web enviroment instead of console, this is the case:
data := 14
response := `Variable string content`
so I can't get variable data inside variable response, like this
response := `Variable string 14 content`
Any idea?

Why not use fmt.Sprintf?
data := 14
response := fmt.Sprintf("Variable string %d content", data)

I believe that the accepted answer is already the best practice one. Just like to give an alternative option based on #Ari Pratomo answer:
package main
import (
"fmt"
"strconv"
)
func main() {
data := 14
response := "Variable string " + strconv.Itoa(data) + " content"
fmt.Println(response) //Output: Variable string 14 content
}
It using strconv.Itoa() to convert an integer to string, so it can be concatenated with the rest of strings.
Demo: https://play.golang.org/p/VnJBrxKBiGm

You can use text/template:
package main
import (
"strings"
"text/template"
)
func format(s string, v interface{}) string {
t, b := new(template.Template), new(strings.Builder)
template.Must(t.Parse(s)).Execute(b, v)
return b.String()
}
func main() {
data := 14
response := format("Variable string {{.}} content", data)
println(response)
}

If you want to keep the string in variable rather than to print out, try like this:
data := 14
response := "Variable string" + data + "content"

Related

Go: CSV NewReader not getting the correct number of fields

How to get the correct number of fields when using NewReader ?
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`||""FOO""||`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v\n", len(record))
}
https://go.dev/play/p/gg-KYRciWFH
It should return 5, but instead I'm getting 3:
record length: 3
Program exited.
EDIT
I'm actually working with a big CSV file containing many double quotes.
After examining your code, I decided to modify it slightly and then print the results:
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`x||""FOO""|x|x\n`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v, Data: %v\n", len(record), strings.Join(record, ", "))
}
When you run this, the data is printed as x, , "FOO"||x|x\n". My thought is that when you end your entry with two double-quotes, the parser is assuming the string is still being quoted and therefore lumps the rest of the line into the third entry. This appears to be a bug with how lazy-quoting works in the csv package, however, when examining the documentation for LazyQuotes, you'll see this:
If LazyQuotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field.
This doesn't mention anything about finding double quotes within double quotes. To fix this, you should either remove the quotes altogether or replace the double double-quotes ("") with double quotes (").
One other thing you might consider would be using the gocsv package. I've worked with this package in the past and it's reasonably stable. I'm not sure how it would respond to this specific issue, but it might be worth your time checking it out.
Note:
The encoding/csv package implements the RFC 4180 standard. If you have such input, that's not an RFC 4180 compliant CSV file and encoding/csv will not parse it properly.
You're misusing the quotes. Quoting a single field FOO is like this:
parser := csv.NewReader(strings.NewReader(`||"FOO"||`))
If you want the field to have the "FOO" value, you have to use 2 double quotes in a quoted field, so it should be:
parser := csv.NewReader(strings.NewReader(`||"""FOO"""||`))
This will output 5. Try it on the Go Playground.
What you have is this:
parser := csv.NewReader(strings.NewReader(`||""FOO""||`))
Since the second " character is not followed by a separator character, the field is not interrupted and the rest is processed as the content of the quoted field (which will terminate at the end of the line).
If you print the record:
fmt.Println(record)
fmt.Printf("%#v", record)
Output will be (try it on the Go Playground):
[ "FOO"||]
[]string{"", "", "\"FOO\"||"}
Quotes are a part of csv format.
There is a problem with go/csv shielding, you can try something like this:
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`||FOO||`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v\n", len(record))
fmt.Println(strings.Join(record, " /SEP/ "))
}
or like this:
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`||"""FOO"""||`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v\n", len(record))
fmt.Println(strings.Join(record, " SEP "))
}

Appending to go lang slice using reflection

For some reason, it appears that adding new element to slice using reflection doesn't update slice itself. This is the code to demonstrate:
package main
import (
"fmt"
"reflect"
)
func appendToSlice(arrPtr interface{}) {
valuePtr := reflect.ValueOf(arrPtr)
value := valuePtr.Elem()
value = reflect.Append(value, reflect.ValueOf(55))
fmt.Println(value.Len()) // prints 1
}
func main() {
arr := []int{}
appendToSlice(&arr)
fmt.Println(len(arr)) // prints 0
}
Playground link : https://play.golang.org/p/j3532H_mUL
Is there something I'm missing here?
reflect.Append works like append in that it returns a new slice value.
You are assigning this value to the value variable in the appendToSlice function, which replaces the previous reflect.Value, but does not update the original argument.
To make it more clear what's happening, take the equivalent function to your example without reflection:
func appendToSlice(arrPtr *[]int) {
value := *arrPtr
value = append(value, 55)
fmt.Println(len(value))
}
What you need to use is the Value.Set method to update the original value:
func appendToSlice(arrPtr interface{}) {
valuePtr := reflect.ValueOf(arrPtr)
value := valuePtr.Elem()
value.Set(reflect.Append(value, reflect.ValueOf(55)))
fmt.Println(value.Len())
}
https://play.golang.org/p/Nhabg31Sju
package main
import "fmt"
import "reflect"
type Foo struct {
Name string
}
func main() {
_type := []Foo{}
fmt.Printf("_type: v(%v) T(%T)\n", _type, _type)
reflection := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(_type).Elem()), 0, 0)
reflectionValue := reflect.New(reflection.Type())
reflectionValue.Elem().Set(reflection)
slicePtr := reflect.ValueOf(reflectionValue.Interface())
sliceValuePtr := slicePtr.Elem()
sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"a"})))
sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"b"})))
sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(Foo{"c"})))
values := []Foo{Foo{"d"}, Foo{"e"}}
for _, val := range values {
sliceValuePtr.Set(reflect.Append(sliceValuePtr, reflect.ValueOf(val)))
}
result := sliceValuePtr.Interface()
fmt.Printf("result: %T = (%v)\n", result, result)
}
take a look at: https://play.golang.org/p/vXOqTVSEleO

How to build a URL / Query in Golang

Background -
I need to build a URL / query based on user input from a form that will be used to make an API call.
Problem -
When building the URL, the params are not properly escaped. For example, the query "bad santa" ends up with a space between it instead of "+".
Current Output -
e.g. https://api.example.org/3/search/movie?query=bad
santa&api_key=#######
Expected Output -
e.g. https://api.example.org/3/search/movie?query=bad+santa&api_key=#######
Code Example -
Root URL -
var SearchUrl = "https://www.example.org/3/search/movie?query="
Get params taken from user input -
var MovieSearch []string = r.Form["GetSearchKey"]
API Key -
var apiKey = "&api_key=######"
I am using the ArrayToString() to parse the form input data
func ArrayToString(array []string) string{
str := strings.Join(array, "+")
return str
}
Then building the URL -
var SearchUrl = "https://api.example.org/3/search/movie?query="
var MovieSearch []string = r.Form["GetSearchKey"]
var apiKey = "&api_key=########"
UrlBuild := []string {SearchUrl, ArrayToString(MovieSearch), apiKey}
OUTPUT_STRING := ArrayToString(UrlBuild)
Question -
How to build a URL with user input GET params that are escaped properly?
Normally, one should use url package's Values.
Here's an example, that does what I think you want, on play
Both a simple main, and in http.HandlerFunc form:
package main
import "fmt"
import "net/url"
import "net/http"
func main() {
baseURL := "https://www.example.org/3/search/movie"
v := url.Values{}
v.Set("query", "this is a value")
perform := baseURL + "?" + v.Encode()
fmt.Println("Perform:", perform)
}
func formHandler(w http.ResponseWriter, r *http.Request) {
baseURL := "https://www.example.org/3/search/movie"
v := url.Values{}
v.Set("query", r.Form.Get("GetSearchKey")) // take GetSearchKey from submitted form
v.Set("api_ley", "YOURKEY") // whatever your api key is
perform := baseURL + "?" + v.Encode() // put it all together
fmt.Println("Perform:", perform) // do something with it
}
Output:
Perform: https://www.example.org/3/search/movie?query=this+is+a+value
Notice how the values are put in to query string, properly escaped, for you.
You can escape parameters using https://golang.org/pkg/net/url/#QueryEscape, instead of doing it yourself.
Besides you should be using https://golang.org/pkg/net/url/#URL to build up your url:
params := fmt.Sprintf("?query=%s&api_key=######", url.QueryEscape("name"))
perform := url.URL{
Scheme: "https",
Host: "api.example.com",
Path: "3/search/movie",
RawQuery: params,
}
fmt.Println(perform) // <- Calls .String()
I recommend to check https://golang.org/doc/effective_go.html.
If your data comes in []string:
func ArrayToQuery(values []string) string {
return url.QueryEscape(strings.Join(values, " "))
}
If MovieSearch contains one element with the value "bad santa", what you're seeing looks correct. It's joining those three strings and putting "+" between them.
If there is a space in the word you will need to replace it.
Example
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Replace("bad santa", " ", "+", -1))
}
So you should probably do it like this
func main() {
a := []string{"bad", "santa"}
fmt.Printf("%q\n", a)
j := ArrayToString(a)
strings.Replace(j, " ", "+",-1)
fmt.Printf("%q\n", j)
}
Here is a link to the Go Documentation - https://golang.org/pkg/strings/#Replace

How do I parse URLs in the format of /id/123 not ?foo=bar

I'm trying to parse an URL like:
http://example.com/id/123
I've read through the net/url docs but it seems like it only parses strings like
http://example.com/blah?id=123
How can I parse the ID so I end up with the value of the id in the first example?
This is not one of my own routes but a http string returned from an openid request.
In your example /id/123 is a path and you can get the "123" part by using Base from the path module.
package main
import (
"fmt"
"path"
)
func main() {
fmt.Println(path.Base("/id/123"))
}
For easy reference, here's the docs on the path module. http://golang.org/pkg/path/#example_Base
You can try using regular expression as follow:
import "regexp"
re, _ := regexp.Compile("/id/(.*)")
values := re.FindStringSubmatch(path)
if len(values) > 0 {
fmt.Println("ID : ", values[1])
}
Here is a simple solution that works for URLs with the same structure as yours (you can improve to suit those with other structures)
package main
import (
"fmt"
"net/url"
)
var path = "http://localhost:8080/id/123"
func getFirstParam(path string) (ps string) {
// ignore first '/' and when it hits the second '/'
// get whatever is after it as a parameter
for i := 1; i < len(path); i++ {
if path[i] == '/' {
ps = path[i+1:]
}
}
return
}
func main() {
u, _ := url.Parse(path)
fmt.Println(u.Path) // -> "/id/123"
fmt.Println(getFirstParam(u.Path)) // -> "123"
}
Or, as #gollipher suggested, use the path package
import "path"
func main() {
u, _ := url.Parse(path)
ps := path.Base(u.Path)
}
With this method it's faster than regex, provided you know before hand the structure of the URL you are getting.

Convert data to base64 encode in go

I am new to go programming language and I'm stock on this scenario on my code.
Here's my example code:
a := genreAPI{Genre{"Pop"}, Genre{"Rock"}}
fmt.Println("Value of a :", a)
The current output is: Value of a : [{Pop} {Rock}]
How can I achieved an output like this:
Value of a : [{UG9w} {Um9jaw==}]
which is a base64 encode?
I am not sure what exactly is not clear from the documentation. Not only it has a clear name which explains states what the method is doing, it also has an example.
package main
import (
"encoding/base64"
"fmt"
)
func main() {
data := []byte("Pop")
str := base64.StdEncoding.EncodeToString(data)
fmt.Println(str) // UG9w
}
Go Playground
You can customise the output of print functions by providing a String() method for your type. Either for the whole Genre or just for the name variable.
Example:
package main
import (
"encoding/base64"
"fmt"
)
type Base64String string
func (b Base64String) String() string {
return base64.StdEncoding.EncodeToString([]byte(b))
}
type Genre struct {
Name Base64String
}
func main() {
a := []Genre{Genre{"Pop"}, Genre{"Rock"}}
fmt.Println(a) // prints [{UG9w} {Um9jaw==}]
fmt.Println(string(a[0].Name)) // prints Pop
}

Resources