Checking a string contains only ASCII characters - go

Does Go have any method or there is a suggestion how to check if a string contains only ASCII characters? What is the right way to do it?
From my research, one of the solution is to check whatever there is any char greater than 127.
func isASCII(s string) bool {
for _, c := range s {
if c > unicode.MaxASCII {
return false
}
}
return true
}

In Go, we care about performance, Therefore, we would benchmark your code:
func isASCII(s string) bool {
for _, c := range s {
if c > unicode.MaxASCII {
return false
}
}
return true
}
BenchmarkRange-4 20000000 82.0 ns/op
A faster (better, more idiomatic) version, which avoids unnecessary rune conversions:
func isASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > unicode.MaxASCII {
return false
}
}
return true
}
BenchmarkIndex-4 30000000 55.4 ns/op
ascii_test.go:
package main
import (
"testing"
"unicode"
)
func isASCIIRange(s string) bool {
for _, c := range s {
if c > unicode.MaxASCII {
return false
}
}
return true
}
func BenchmarkRange(b *testing.B) {
str := ascii()
b.ResetTimer()
for N := 0; N < b.N; N++ {
is := isASCIIRange(str)
if !is {
b.Fatal("notASCII")
}
}
}
func isASCIIIndex(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > unicode.MaxASCII {
return false
}
}
return true
}
func BenchmarkIndex(b *testing.B) {
str := ascii()
b.ResetTimer()
for N := 0; N < b.N; N++ {
is := isASCIIIndex(str)
if !is {
b.Log("notASCII")
}
}
}
func ascii() string {
byt := make([]byte, unicode.MaxASCII+1)
for i := range byt {
byt[i] = byte(i)
}
return string(byt)
}
Output:
$ go test ascii_test.go -bench=.
BenchmarkRange-4 20000000 82.0 ns/op
BenchmarkIndex-4 30000000 55.4 ns/op
$

It looks like your way is best.
ASCII is simply defined as:
ASCII encodes 128 specified characters into seven-bit integers
As such, characters have values 0-27 (or 0-127, 0x0-0x7F).
Go provides no way to check that every rune in a string (or byte in a slice) has numerical values in a specific range, so your code seems to be the best way to do it.

Another option:
package main
import "golang.org/x/exp/utf8string"
func main() {
{
b := utf8string.NewString("south north").IsASCII()
println(b) // true
}
{
b := utf8string.NewString("🧡💛💚💙💜").IsASCII()
println(b) // false
}
}
https://pkg.go.dev/golang.org/x/exp/utf8string#String.IsASCII

Related

Merging Intervals out of hyphenated string ranges

I have a string like this:
ports := []string{"1", "2-7", "12-1200", "10-500"}
I would like to make an integer set out of this like the output should be :
[]intSet{ 1, 2-7, 10-1200 }
Where intSet is some kind of integer set from which I am able to easily remove and add elements.
Update 1
intSet is a list of sets.
So, 2-7 is also a set.
Update 2
Here the largest set is merged.
e.g.
"1" -> 1
"2-7" -> 2-7
"12-1200" & "10-500" => "10..12.....500....1200" -> 10-1200
Since it's a set so it encompasses a unique range for this, a range which covers the whole set.
package main
import (
"fmt"
"log"
"strconv"
"strings"
)
type intSet struct {
start int
end int
}
func (s intSet) String() string {
if s.start == s.end {
return fmt.Sprintf("%d", s.start)
}
return fmt.Sprintf("%d-%d", s.start, s.end)
}
func (s intSet) in(i int) bool {
return s.start <= i && i <= s.end
}
func (s *intSet) union(set intSet) {
if set.start < s.start {
s.start = set.start
}
if set.end > s.end {
s.end = set.end
}
}
func insert(set intSet, is []intSet) bool {
for i, s := range is {
if s.in(set.start) || s.in(set.end) {
is[i].union(set)
return true
}
//updated here with thankful to #mh-cbon
if set.in(s.start) || set.in(s.end) {
is[i].union(set)
return true
}
}
return false
}
func main() {
var set []intSet
ports := []string{"1", "2-7", "12-1200", "10-500", "9-5500"}
for _, port := range ports {
s := strings.Split(port, `-`)
if len(s) < 1 || len(s) > 2 {
log.Fatalln(`set cannot have multiple values or no value`)
}
start, err := strconv.Atoi(s[0])
if err != nil {
log.Fatalln(err)
}
end := start
if len(s) == 2 {
end, err = strconv.Atoi(s[1])
if err != nil {
log.Fatalln(err)
}
}
temSet := intSet{
start: start,
end: end,
}
if !insert(temSet, set) {
set = append(set, temSet)
}
}
fmt.Println(set) //[1 2-7 9-5500]
}
run here

Sort/order a slice string by given slice

How do I sort a slice of string in a order that is given by another slice of strings. If that string is not there in the input slice then just ignore it.
animalsInput := []string{"cat", "bird", "zebra", "fox"}
animalsOrder := []string{"bird", "lion", "fox"}
//desired output
//{"bird", "fox", "cat", "zebra"}
One way you can implement this is by writing a rank map based on the order array
rank:=map[string]int{}
for i, x:=range animalsOrder {
rank[x]=i
}
Then use rank in sort:
sort.Slice(animalsInput,func(i,j int) bool {
irank, ok:=rank[animalsInput[i]]
if !ok {
irank=len(animalsInput)
}
jrank, ok:=rank[animalsInput[j]]
if !ok {
jrank=len(animalsInput)
}
return irank<jrank
})
You can try this code:
package main
import (
"fmt"
)
func sort(in, order []string) (out []string) {
flag := make([]bool, len(in))
out = make([]string, len(in))
orderCountMap := make(map[string]int)
for i := range in {
orderCountMap[in[i]] += 1
}
for i := range in {
if _, found := orderCountMap[in[i]]; found {
flag[i] = true
} else {
flag[i] = false
}
}
p := 0
for i := range order {
if v, found := orderCountMap[order[i]]; found {
for j := 0; j < v; j++ {
out[p] = order[i]
p += 1
}
}
}
for i := range flag {
if !flag[i] {
out[p] = in[i]
p += 1
}
}
return
}
func main(){
animalsInput := []string{"cat", "bird", "zebra", "fox"}
animalsOrder := []string{"bird", "lion", "fox", "zebra", "cat"}
out := sort(animalsInput, animalsOrder)
fmt.Println(out)
}

Simple encryption of a string

I want to encrypt a string with Go, my actual code is:
package main
import (
"fmt"
)
const key = "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98" //some random numbers here
func Encrypt(input string) (output string) {
for i := 0; i < len(input); i++ {
output += fmt.Sprintf("\\x%02x", input[i] ^ key[i % len(key)])
}
return output;
}
func Decrypt(input string) (output string) {
key := "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98"
for i := 0; i < len(input); i++ {
output += string(input[i] ^ key[i % len(key)])
}
return output;
}
func main() {
stringa := "password"
encrypted := Encrypt(stringa)
fmt.Println(encrypted)
fmt.Println(Decrypt(encrypted))
fmt.Println(stringa)
}
\xcd\xd3\x4e\xcf\x57\x8d\xfe\xfc
áE^O|?è«áE U|?ï_á?|?'üáE[U|?êû
password
Problem is after encrypt string, when I try to decrypt return different output. Where did I go wrong?
It looks like your goal is to xor the the bytes in a string with the bytes in a key. Here's one way to do it:
func xor(input string) string {
output := make([]byte, len(input))
for i := 0; i < len(input); i++ {
output[i] = input[i] ^ key[i%len(key)]
}
return string(output)
}
The Encrypt and Decrypt functions are the same:
func Encrypt(input string) string { return xor(input) }
func Decrypt(input string) string { return xor(input) }

Golang convert list objects to string

I have two structs:
type A struct {
BankCode string `json:"bankCode"`
BankName string `json:"bankName"`
}
And:
type B struct {
A
extra string `json:" extra"`
}
And two slices:
listsA []A and listsB []B
I want to get bankCodes from listA and listB. bankcodes only contains bankcodes. It is a []string
It will be so easy as using two function.
func getBankCodes(data []A) []string {
res := make([]string, len(data))
for i := 0; i < len(data); i++ {
res[i] = data[i].BankCode
}
return res
}
func getBankCodes(data []B) []string {
res := make([]string, len(data))
for i := 0; i < len(data); i++ {
res[i] = data[i].BankCode
}
return res
}
How to use one common function ?
Well the clean solution would be to use an interface, since go doesn't support classic inheritance, so something like []parentclass can't work. Interfaces however can only describe functions not a common field, so you have to implement a Getter (essentially).
// GetBankCoder provides a function that gives the BankCode
type GetBankCoder interface {
getBankCode() string
}
// implement GetBankCoder for A (and indirectly for B)
func (a A) getBankCode() string {
return a.BankCode
}
and make your getBankCodes work on that interface type, notice the parameter of the function as well as the statement inside the loop:
func getBankCodes(data []GetBankCoder) []string { // <-- changed
res := make([]string, len(data))
for i := 0; i < len(data); i++ {
res[i] = data[i].getBankCode() // <-- changed
}
return res
}
There are other solutions where the function parameter is of interface{} type and then reflection is used to assure you can actually do .BankCode, but I don't like those, as they are not adding more clarity either.
... However, I couldn't get the golang playground to make this work correctly without putting it into a []GetBankCoder var first, before giving it to the function.
banks := make([]GetBankCoder, 0)
banks = append(banks, A{ BankCode: "ABC", BankName: "ABC Bank"})
getBankCodes(banks)
You may use one common function like so:
func BankCodes(data interface{}) []string {
if reflect.TypeOf(data).Kind() != reflect.Slice {
panic("err: data is not slice")
}
slice := reflect.Indirect(reflect.ValueOf(data))
res := make([]string, slice.Len())
for i := 0; i < slice.Len(); i++ {
a := slice.Index(i).Interface().(BankCoder)
res[i] = a.Bankcode()
}
return res
}
Code (try on The Go Playground):
package main
import (
"fmt"
"reflect"
)
func main() {
bs := []B{B{A{"BC1", "BN"}, "e"}, B{A{"BC2", "BN"}, "e"}}
strs := BankCodes(bs)
fmt.Println(strs)
as := []A{A{"AC1", "BN"}, A{"AC2", "BN"}}
strs2 := BankCodes(as)
fmt.Println(strs2)
}
func BankCodes(data interface{}) []string {
if reflect.TypeOf(data).Kind() != reflect.Slice {
panic("err: data is not slice")
}
slice := reflect.Indirect(reflect.ValueOf(data))
res := make([]string, slice.Len())
for i := 0; i < slice.Len(); i++ {
a := slice.Index(i).Interface().(BankCoder)
res[i] = a.Bankcode()
}
return res
}
type A struct {
BankCode string `json:"bankCode"`
BankName string `json:"bankName"`
}
type B struct {
A
extra string `json:" extra"`
}
type BankCoder interface {
Bankcode() string
}
func (a A) Bankcode() string {
return a.BankCode
}

Golang container/list creating a FindAll function

I was wondering if this is the way to create and pass 'generic'(yeah I know, a sensitive word in GoLang) lists to a FindAll function.
Here's my attempt:
package main
import (
"container/list"
"fmt"
"strings"
)
func FindAll(lst *list.List, p func(interface{}) bool) *list.List {
ans := list.New()
for i := lst.Front(); i != nil; i = i.Next() {
if p(i.Value) {
ans.PushBack(i.Value)
}
}
return ans
}
func ConvertToInt(p func(int) bool) func(interface{}) bool {
return func(v interface{}) bool {
if value, ok := v.(int); ok {
if p(value) {
return true
} else {
return false
}
} else {
return false
}
}
}
func IsEven(n int) bool {
if n%2 == 0 {
return true
}
return false
}
func ConvertoString(p func(s string) bool) func(interface{}) bool {
return func(v interface{}) bool {
if value, ok := v.(string); ok {
if p(value) {
return true
} else {
return false
}
} else {
return false
}
}
}
func IsHello(str string) bool {
if strings.ToLower(str) == "hello" {
return true
} else {
return false
}
}
func main() {
fmt.Println("Find All Programs!\n\n")
lsti := list.New()
for i := 0; i < 11; i++ {
lsti.PushBack(i)
}
ansIsEven := FindAll(lsti, ConvertToInt(IsEven))
for i := ansIsEven.Front(); i != nil; i = i.Next() {
if value, ok := i.Value.(int); ok {
fmt.Printf("Found even: %d\n", value)
} else {
fmt.Println("Huh! What's that?")
}
}
}
I've been playing with this for a while and thought I'd better get the advice of the Go experts before I convince myself its correct.
The code as-is is pretty fine, but you should ask your self 2 questions:
1. Why shouldn't you use a typed slice? (interface{} performance is slow compared to the explicit type, although it will greatly improve in Go 1.7)
2. Would it be better to implement your specific type as a linked list?
Something like this can be much more efficient:
type IntList []int
func (l IntList) Filter(fn func(v int) bool) IntList {
var o IntList
for _, v := range l {
if fn(v) {
o = append(o, v)
}
}
return o
}
There's almost always a better alternative to container/list, however it all depends on your use case.

Resources