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

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

Related

Filling a struct based on substring matches within a loop

If we take a look at the following code, how can we fill a struct variable with values taken from a slice of strings? https://go.dev/play/p/KkcPzr5r28w
package main
import (
"fmt"
"os"
"strings"
)
type Config struct {
Operation string
Stop string
Start string
File string
}
func ParseConfig(list []string) Config {
var c Config
for _, elem := range list {
if strings.Contains(elem, "op:") {
subList := strings.SplitAfterN(elem, ":", 2)
c.Operation = subList[1]
} else if strings.Contains(elem, "stop:") {
subList := strings.SplitAfterN(elem, ":", 2)
c.Stop = subList[1]
} else if strings.Contains(elem, "start:") {
subList := strings.SplitAfterN(elem, ":", 2)
c.Start = subList[1]
} else if strings.Contains(elem, "file:") {
subList := strings.SplitAfterN(elem, ":", 2)
c.File = subList[1]
}
}
return c
}
func main() {
c := ParseConfig(os.Args[1:])
fmt.Println(c) // {count the quick /tmp/file1.txt}
}
This program doesn't return the right response when invoked with these parameters:
go run scan.go op:count start:quick stop:the file:/tmp/file1.txt
I was wondering what's wrong? What is the best way to refactor the code to solve the problem?
Hopefully I've fixed it, thanks to Gophers's community: https://go.dev/play/p/u_Dc7ctbsib
package main
import (
"fmt"
"strings"
)
type Config struct {
Operation string
Stop string
Start string
File string
}
func main() {
list := []string{"op:count", "start:quick", "stop:the", "file:/tmp/file1.txt"}
fmt.Println(list)
var c Config
for _, v := range list {
if strings.HasPrefix(v, "op:") {
subList := strings.SplitAfterN(v, ":", 2)
c.Operation = subList[1]
} else if strings.Contains(v, "stop:") {
subList := strings.SplitAfterN(v, ":", 2)
c.Stop = subList[1]
} else if strings.Contains(v, "start:") {
subList := strings.SplitAfterN(v, ":", 2)
c.Start = subList[1]
} else if strings.Contains(v, "file:") {
subList := strings.SplitAfterN(v, ":", 2)
c.File = subList[1]
}
}
fmt.Println(c) // {count the quick /tmp/file1.txt}
}
Due to the fact that "stop:the" also wrongly matches "op:", Operation is finally set to "the" instead of "count". The problem seems to be solved now that strings.Contains was replaced with strings.HasPrefix.

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

Update a string value in loop

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

Resources