Update a string value in loop - string

Is it possible to update the value of a string when we execute a for loop?
package main
import (
"fmt"
"strings"
)
func Chop(r int, s string) string {
return s[r:]
}
func main() {
s:= "ThisIsAstring1ThisIsAstring2ThisIsAstring3"
for strings.Contains(s, "string") {
// Original value > ThisIsAstring1ThisIsAstring2ThisIsAstring3
fmt.Println(s)
// I delete a part of the string > ThisIsAstring1
remove := len(s)/3
// Now, I update the value of string > string := ThisIsAstring2ThisIsAstring3
s := Chop(remove, s)
fmt.Println(s)
break
}
}
I don't know how to do it.

I have no clue what the use case is, but here goes. Let's start with identifying the issues in your code:
// You cannot use a reserved keyword "string" as a variable name
string:= "ThisIsAstring1ThisIsAstring2ThisIsAstring3"
for strings.Contains(string, "string") {
// Remove is a float, but you need to pass an int into your chop function
remove := len(string)/3
// You're reassigning your string variable. You really just want =, not :=
string := Chop(remove, string)
fmt.Println(string)
}
Now, here's a solution that will work for your use case:
str := "ThisIsAstring1ThisIsAstring2ThisIsAstring3"
for strings.Contains(str, "string") {
fmt.Println(str)
remove := int(len(str) / 3)
str = Chop(remove, str)
}
fmt.Println(str)
GoPlay:
https://play.golang.org/p/NdROIFDS_5

Related

Go (strings) - Trim repeated prefix/suffix substring

I need to trim a repeated prefix and suffix substring ("%20") of a string.
Example: %20%20Hello%20World%20%20 --> Hello%20World
The best I could come with is something like:
func trimPrefix(s string, prefix string) string {
for ; strings.HasPrefix(s, prefix); {
s = s[len(prefix):]
}
return s
}
func trimSuffix(s string, suffix string) string {
for ; strings.HasSuffix(s, suffix); {
s = s[:len(s)-len(suffix)]
}
return s
}
func trim(s, ss string) string {
return trimPrefix(trimSuffix(s, ss), ss)
}
Is there a more elegant way to do it in Go?
You can do the following:
func trimSubstr(s string, substr string) (t string) {
for {
t = strings.TrimPrefix(s, substr)
t = strings.TrimSuffix(t, substr)
if t == s { // exit if nothing was trimmed from s
break
}
s = t // update to last result
}
return t
}
https://go.dev/play/p/eIk6A8K3Q_1
strings.Trim does that
Trim returns a slice of the string s with all leading and trailing Unicode code points contained in cutset removed.
package main
import (
"fmt"
"strings"
)
func main() {
s := "%20%20Hello%20World%20%20"
t := strings.Trim(s, "%20")
fmt.Println(t) // prints Hello%20World
}
This works just fine if your prefix and suffix runes always appear in the same order given in cutset param.
If your input string may present those characters in different orders (see comments to this answer for details), then your solution is good. You can use strings.TrimPrefix and strings.TrimSuffix from the standard lib instead of rolling your own, and combine it in one function:
func trim(s, sub string) string {
for strings.HasPrefix(s, sub) {
s = strings.TrimPrefix(s, sub)
}
for strings.HasSuffix(s, sub) {
s = strings.TrimSuffix(s, sub)
}
return s
}

How to convert Camel case string to snake case

I have a string
str := "IGotInternAtGeeksForGeeks"
I try to convert it in to
str = "i_got_intern_at_geeks_for_geeks"
Try this,
import (
"fmt"
"strings"
"regexp"
)
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
func ToSnakeCase(str string) string {
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
return strings.ToLower(snake)
}
Run:
func main() {
fmt.Println(ToSnakeCase("IGotInternAtGeeksForGeeks"))
}
Output:
i_got_intern_at_geeks_for_geeks
NOTE: This will not work for many non-English languages.
I know this is old post but, I've create a package named gobeam/Stringy You can easily convert camel case string to snake case and kebab case and vice versa. Example:
package main
import (
"fmt"
stringy "github.com/gobeam/Stringy"
)
func main() {
str := stringy.New("HelloGuysHowAreYou?")
snakeStr := str.SnakeCase("?", "")
fmt.Println(snakeStr.ToLower()) // hello_guys_how_are_you
fmt.Println(snakeStr.ToUpper()) // HELLO_GUYS_HOW_ARE_YOU
}
Without reguar expression version.
Letters only, because the use case is struct field db tag. Feel free to modify it for other use cases.
func ToSnake(camel string) (snake string) {
var b strings.Builder
diff := 'a' - 'A'
l := len(camel)
for i, v := range camel {
// A is 65, a is 97
if v >= 'a' {
b.WriteRune(v)
continue
}
// v is capital letter here
// irregard first letter
// add underscore if last letter is capital letter
// add underscore when previous letter is lowercase
// add underscore when next letter is lowercase
if (i != 0 || i == l-1) && ( // head and tail
(i > 0 && rune(camel[i-1]) >= 'a') || // pre
(i < l-1 && rune(camel[i+1]) >= 'a')) { //next
b.WriteRune('_')
}
b.WriteRune(v + diff)
}
return b.String()
}
// here is the test
func TestToSnake(t *testing.T) {
input := "MyLIFEIsAwesomE"
want := "my_life_is_awesom_e"
if got := ToSnake(input); got != want {
t.Errorf("ToSnake(%v) = %v, want %v", input, got, want)
}
}
Faster and simpler version:
import "bytes"
func SnakeCase(camel string) string {
var buf bytes.Buffer
for _, c := range camel {
if 'A' <= c && c <= 'Z' {
// just convert [A-Z] to _[a-z]
if buf.Len() > 0 {
buf.WriteRune('_')
}
bytes.WriteRune(c - 'A' + 'a')
} else {
bytes.WriteRune(c)
}
}
return buf.String()
}
Known bugs:
1. no-ascii
2. reversed upper abbreviate word, eg. baseURL will be ugly base_u_r_l, but not base_url, consider use white list to filter.
wrapped it into a package
import (
"fmt"
"github.com/buxizhizhoum/inflection"
)
func example () {
// to convert a string to underscore
res := inflection.Underscore("aA")
// will return a_a
fmt.Println(res)
// to convert a string to camelize
// will return AA
fmt.Println(inflection.Camelize("a_a", true))
}

String splitting before character

I'm new to go and have been using split to my advantage. Recently I came across a problem I wanted to split something, and keep the splitting char in my second slice rather than removing it, or leaving it in the first slice as with SplitAfter.
For example the following code:
strings.Split("email#email.com", "#")
returned: ["email", "email.com"]
strings.SplitAfter("email#email.com", "#")
returned: ["email#", "email.com"]
What's the best way to get ["email", "#email.com"]?
Use strings.Index to find the # and slice to get the two parts:
var part1, part2 string
if i := strings.Index(s, "#"); i >= 0 {
part1, part2 = s[:i], s[i:]
} else {
// handle case with no #
}
Run it on the playground.
Could this work for you?
s := strings.Split("email#email.com", "#")
address, domain := s[0], "#"+s[1]
fmt.Println(address, domain)
// email #email.com
Then combing and creating a string
var buffer bytes.Buffer
buffer.WriteString(address)
buffer.WriteString(domain)
result := buffer.String()
fmt.Println(result)
// email#email.com
You can use bufio.Scanner:
package main
import (
"bufio"
"strings"
)
func email(data []byte, eof bool) (int, []byte, error) {
for i, b := range data {
if b == '#' {
if i > 0 {
return i, data[:i], nil
}
return len(data), data, nil
}
}
return 0, nil, nil
}
func main() {
s := bufio.NewScanner(strings.NewReader("email#email.com"))
s.Split(email)
for s.Scan() {
println(s.Text())
}
}
https://golang.org/pkg/bufio#Scanner.Split

How to check if there is a special character in string or if a character is a special character in GoLang

After reading a string from the input, I need to check if there is a special character in it
You can use strings.ContainsAny to see if a rune exists:
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.ContainsAny("Hello World", ",|"))
fmt.Println(strings.ContainsAny("Hello, World", ",|"))
fmt.Println(strings.ContainsAny("Hello|World", ",|"))
}
Or if you want to check if there are only ASCII characters, you can use strings.IndexFunc:
package main
import (
"fmt"
"strings"
)
func main() {
f := func(r rune) bool {
return r < 'A' || r > 'z'
}
if strings.IndexFunc("HelloWorld", f) != -1 {
fmt.Println("Found special char")
}
if strings.IndexFunc("Hello World", f) != -1 {
fmt.Println("Found special char")
}
}
Depending on your definition of special character, the simplest solution would probably to do a for range loop on your string (which yield runes instead of bytes), and for each rune check if it is in your list of allowed/forbidden runes.
See Strings, bytes, runes and characters in Go for more about the relations between string, bytes and runes.
Playground example
package main
var allowed = []rune{'a','b','c','d','e','f','g'}
func haveSpecial(input string) bool {
for _, char := range input {
found := false
for _, c := range allowed {
if c == char {
found = true
break
}
}
if !found {
return true
}
}
return false
}
func main() {
cases := []string{
"abcdef",
"abc$€f",
}
for _, input := range cases {
if haveSpecial(input) {
println(input + ": NOK")
} else {
println(input + ": OK")
}
}
}
You want to use the unicode package, which has a nice function to check for symbols.
https://golang.org/pkg/unicode/#IsSymbol
package main
import (
"fmt"
"unicode"
)
func hasSymbol(str string) bool {
for _, letter := range str {
if unicode.IsSymbol(letter) {
return true
}
}
return false
}
func main() {
var strs = []string {
"A quick brown fox",
"A+quick_brown<fox",
}
for _, str := range strs {
if hasSymbol(str) {
fmt.Printf("String '%v' contains symbols.\n", str)
} else {
fmt.Printf("String '%v' did not contain symbols.\n", str)
}
}
}
This will provide the following output:
String 'A quick brown fox' did not contain symbols.
String 'A+quick_brown<fox' contains symbols.
I ended up doing something like this
alphabet := "abcdefghijklmnopqrstuvwxyz"
alphabetSplit := strings.Split(alphabet, "")
inputLetters := strings.Split(input, "")
for index, value := range inputLetters {
special:=1
for _, char :=range alphabetSplit{
if char == value {
special = 0
break
}
}
It might have anything wrong because since I used it to something specific i had to edit to post it here

Implementing dynamic strings in golang

I have following global string,
studentName := "Hi ? ,Welcome"
Now I want to take this string dynamically
func returnName(name string) string{
return studentName+name
}
This function should return string as
Hi name,welcome.
string should take name from parameter,and return dynamic string.What is the best way to implement this in golang.
If you want to keep things simple, you can probably just use fmt.Sprintf.
studentName := fmt.Sprintf("Hi, %s! Welcome.", name)
The %s part will get replaced by the value of name.
If your input gets bigger (more complex) or if you need to substitute different values multiple times, then templates are more effective, cleaner and more flexible. Check out the text/template package.
The template package parses your template once, builts a tree from it, and once you need to replace values, it builds the output on the fly.
Take a look at this example:
const templ = `Hi {{.Name}}!
Welcome {{.Place}}.
Please bring {{.ToBring}}
`
You can parse such a template with this line:
t := template.Must(template.New("").Parse(templ))
Prepare its input data either as a struct or as a map:
data := map[string]string{
"Name": "Bob",
"Place": "Home",
"ToBring": "some beers",
}
And you can have the result with Template.Execute():
err := t.Execute(os.Stdout, data) // Prints result to the standard output
Here's the complete, runnable example: (try it on the Go Playground)
package main
import (
"os"
"text/template"
)
func main() {
data := map[string]string{
"Name": "Bob",
"Place": "Home",
"ToBring": "some beers",
}
t := template.Must(template.New("").Parse(templ))
if err := t.Execute(os.Stdout, data); err != nil { // Prints result to the standard output
panic(err)
}
// Now change something:
data["Name"] = "Alice"
data["ToBring"] = "a Teddy Bear"
if err := t.Execute(os.Stdout, data); err != nil {
panic(err)
}
}
const templ = `
Hi {{.Name}}!
Welcome {{.Place}}.
Please bring {{.ToBring}}
`
Output:
Hi Bob!
Welcome Home.
Please bring some beers
Hi Alice!
Welcome Home.
Please bring a Teddy Bear
Getting the result as a string:
If you want the result as a string, you can write the result to a bytes.Buffer and get the string using the Buffer.String() method:
buf := bytes.Buffer{}
t.Execute(&buf, data)
var result string = buf.String()
Complete program (try it on the Go Playground):
package main
import (
"bytes"
"fmt"
"text/template"
)
func main() {
data := map[string]string{
"Name": "Bob",
"Place": "Home",
"ToBring": "some beers",
}
fmt.Print(Execute(data))
}
var t = template.Must(template.New("").Parse(templ))
func Execute(data interface{}) string {
buf := bytes.Buffer{}
if err := t.Execute(&buf, data); err != nil {
fmt.Println("Error:", err)
}
return buf.String()
}
const templ = `
Hi {{.Name}}!
Welcome {{.Place}}.
Please bring {{.ToBring}}
`
You could consider the function strings.Replace
return Replace(studentName, "? ", name, 1)
With '1', it replaces the first "? " it finds in studentName.
Replace returns a copy of studentName, with "? " substituted with name.
This strictly respect the original question (global var with that exact content)
Now, if you start changing the question, like for instance with a different content (a global variable studentName := "Hi %s ,Welcome"), then you could use fmt.Sprintf() as in 425nesp's answer
return fmt.Sprintf(studentName, name)
That would use the format 'verbs' %s, default format for string.
Assuming the global string is always the same you could do.
func returnName(name string) string {
buf := bytes.Buffer{}
buf.WriteString("Hi ")
buf.WriteString(name)
buf.WriteString(", welcome")
return buf.String()
}
or
func returnName(name string) string {
return "Hi " + name + ", welcome"
}
if the string is a dynamic template you could use the template package or a simple Replace if there wont be other ? marks or Sprintf
You can also use template.Template
combined with strings.Builder:
package main
import (
"strings"
"text/template"
)
func returnName(name string) string {
t, b := new(template.Template), new(strings.Builder)
template.Must(t.Parse("Hi {{.}}, welcome.")).Execute(b, name)
return b.String()
}
func main() {
println(returnName("Akash"))
}

Resources