Replace a string at the beginning and end only - string

I want to replace first a and last a but not the a in the abcd.
Currently, it returns hello hellobcd hello but I would like to have hello abcd hello
The reason I use ReplaceAll is that I don't necessarily know the position and amount of how a appears. But if a in a combined string, I don't want to replace it, but just leave it as it is. What will be a solution in Go to solve this problem?
package main
import (
"fmt"
"strings"
)
func main() {
item := "hello"
test := "a abcd a"
s := "a"
item = strings.ReplaceAll(test, s, item)
fmt.Println(item)
}
Output:
hello hellobcd hello
Playground:
https://play.golang.org/p/D1VzvipblKu

You could use a regular expression:
package main
import (
"fmt"
"regexp"
)
func main() {
regexForA := regexp.MustCompile(`\ba\b`)
test := "a abcd a"
output := regexForA.ReplaceAllLiteralString(test,`hello`)
fmt.Println(output)
}
Output:
hello abcd hello
Playground link to run this example: https://play.golang.org/p/Cs99TrQDUtK

You can split the problem into two: first take the string from the start and do a plain replace with a limit of one. That will give you:
hello abcd a
Then you reverse the string and do the same thing. You can for sure do it a lot more optimized, but that will get the job done and is still quite readable:
func Reverse(s string) (result string) {
for _,v := range s {
result = string(v) + result
}
return
}
func main() {
item := "hello"
test := "a abcd a"
s := "a"
result := strings.Replace(test, s, item, 1)
result = Reverse(strings.Replace(Reverse(result), s, Reverse(item), 1))
fmt.Println(result)
}
prints
hello abcd hello
Note you need to reverse the first input string, your replacement and then the result to get it back into the right order.
playground link: https://play.golang.org/p/tuzxehhnEDu

If I were doing such thing, I'd just split string (strings.Fields) and then replace basing on string comparison, pseudocode:
for i, field := range fields {
if field == "a" {
fields[i] = "hello"
}
}
I wrote it on phone and never tested, but You should understand a concept :)

Related

How can I remove the last 4 characters from a string?

I want to remove the last 4 characters from a string, so "test.txt" becomes "test".
package main
import (
"fmt"
"strings"
)
func main() {
file := "test.txt"
fmt.Print(strings.TrimSuffix(file, "."))
}
This will safely remove any dot-extension - and will be tolerant if no extension is found:
func removeExtension(fpath string) string {
ext := filepath.Ext(fpath)
return strings.TrimSuffix(fpath, ext)
}
Playground example.
Table tests:
/www/main.js -> '/www/main'
/tmp/test.txt -> '/tmp/test'
/tmp/test2.text -> '/tmp/test2'
/tmp/test3.verylongext -> '/tmp/test3'
/user/bob.smith/has.many.dots.exe -> '/user/bob.smith/has.many.dots'
/tmp/zeroext. -> '/tmp/zeroext'
/tmp/noext -> '/tmp/noext'
-> ''
Though there is already an accepted answer, I want to share some slice tricks for string manipulation.
Remove last n characters from a string
As the title says, remove the last 4 characters from a string, it is very common usage of slices, ie,
file := "test.txt"
fmt.Println(file[:len(file)-4]) // you can replace 4 with any n
Output:
test
Playground example.
Remove file extensions:
From your problem description, it looks like you are trying to trim the file extension suffix (ie, .txt) from the string.
For this, I would prefer #colminator's answer from above, which is
file := "test.txt"
fmt.Println(strings.TrimSuffix(file, filepath.Ext(file)))
You can use this to remove everything after last "."
go playground
package main
import (
"fmt"
"strings"
)
func main() {
sampleInput := []string{
"/www/main.js",
"/tmp/test.txt",
"/tmp/test2.text",
"/tmp/test3.verylongext",
"/user/bob.smith/has.many.dots.exe",
"/tmp/zeroext.",
"/tmp/noext",
"",
"tldr",
}
for _, str := range sampleInput {
fmt.Println(removeExtn(str))
}
}
func removeExtn(input string) string {
if len(input) > 0 {
if i := strings.LastIndex(input, "."); i > 0 {
input = input[:i]
}
}
return input
}

Isolating String Output

I currently have a script that performs an os command, that returns a great deal of data, at the end of the data it gives a total such that:
N Total.
N can be any number from 0 upward.
I want to perform this command, and take N then put it into a value. I have the command running and I'm storing it in a bytes.Buffer, however I'm unsure how to scrape this so that I only get the number. The "N Total." string is always at the end of the output. Any help would be appreciated as I've seen various different methods but they all seem quite convoluted.
You can use a bufio.Scanner to read the command's output line-wise. Then just remember the last line and parse it once the command has finished.
package main
import (
"bufio"
"fmt"
"io"
"os/exec"
"strings"
)
func main() {
r, w := io.Pipe()
cmd := exec.Command("fortune")
cmd.Stdout = w
go func() {
cmd.Run()
r.Close()
w.Close()
}()
sc := bufio.NewScanner(r)
var lastLine string
for sc.Scan() {
line := sc.Text()
fmt.Println("debug:", line)
if strings.TrimSpace(line) != "" {
lastLine = line
}
}
fmt.Println(lastLine)
}
Sample output:
debug: "Get back to your stations!"
debug: "We're beaming down to the planet, sir."
debug: -- Kirk and Mr. Leslie, "This Side of Paradise",
debug: stardate 3417.3
stardate 3417.3
Parsing lastLine is left as an excercise for the reader.
You can split the string by \n and get the last line.
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
output := `
Some os output
Some more os output
Again some os output
1001 Total`
// If you're getting the string from the bytes.Buffer do this:
// output := myBytesBuffer.String()
outputSplit := strings.Split(output, "\n") // Break into lines
// Get last line from the end.
// -1 assumes the numbers in the last line. Change it if its not.
lastLine := outputSplit[len(outputSplit)-1]
lastLine = strings.Replace(lastLine, " Total", "", -1) // Remove text
number, _ := strconv.Atoi(lastLine) // Convert from text to number
fmt.Println(number)
}
peterSO points out that for big output the above may be slow.
Here's another way that uses a compiled regexp expression to match against a small subset of bytes.
package main
import (
"bytes"
"fmt"
"os/exec"
"regexp"
"strconv"
)
func main() {
// Create regular expression. You only create this once.
// Would be regexpNumber := regexp.MustCompile(`(\d+) Total`) for you
regexpNumber := regexp.MustCompile(`(\d+) bits physical`)
// Whatever your os command is
command := exec.Command("cat", "/proc/cpuinfo")
output, _ := command.Output()
// Your bytes.Buffer
var b bytes.Buffer
b.Write(output)
// Get end of bytes slice
var end []byte
if b.Len()-200 > 0 {
end = b.Bytes()[b.Len()-200:]
} else {
end = b.Bytes()
}
// Get matches. matches[1] contains your number
matches := regexpNumber.FindSubmatch(end)
// Convert bytes to int
number, _ := strconv.Atoi(string(matches[1])) // Convert from text to number
fmt.Println(number)
}

Go: how to check if a string contains multiple substrings?

strings.Contains(str_to_check, substr) takes only one argument as the substring to check, how do I check multiple substrings without using strings.Contains() repeatedly?
eg. strings.Contains(str_to_check, substr1, substr2)
Yes, you can do this without calling strings.Contains() multiple times.
If you know substrings in advance the easiest way to check this with regular expression. And if a string to check is long and you have quite a few substrings it can be more fast then calling multiple strings.Contains
Example https://play.golang.org/p/7PokxbOOo7:
package main
import (
"fmt"
"regexp"
)
var re = regexp.MustCompile(`first|second|third`)
func main() {
fmt.Println(re.MatchString("This is the first example"))
fmt.Println(re.MatchString("This is the second example after first"))
fmt.Println(re.MatchString("This is the third example"))
fmt.Println(re.MatchString("This is the forth example"))
}
Output:
true
true
true
false
If the subs to check are dynamic it may be a bit more difficult to create regex as you need to escape special characters and regex compilation is not fast so strings.Contains() may be better in this case though it's better test if your code is performance critical.
Another good option could be to write your own scanner that can leverage common prefixes in substrings (if any) using prefix tree.
You can write your own utility function using strings.Contains() that can work for multiple sub-strings.
Here's an example that returns Boolean (true/false) in case of complete / partial match and the total number of matches:
package main
import (
"fmt"
"strings"
)
func checkSubstrings(str string, subs ...string) (bool, int) {
matches := 0
isCompleteMatch := true
fmt.Printf("String: \"%s\", Substrings: %s\n", str, subs)
for _, sub := range subs {
if strings.Contains(str, sub) {
matches += 1
} else {
isCompleteMatch = false
}
}
return isCompleteMatch, matches
}
func main() {
isCompleteMatch1, matches1 := checkSubstrings("Hello abc, xyz, abc", "abc", "xyz")
fmt.Printf("Test 1: { isCompleteMatch: %t, Matches: %d }\n", isCompleteMatch1, matches1)
fmt.Println()
isCompleteMatch2, matches2 := checkSubstrings("Hello abc, abc", "abc", "xyz")
fmt.Printf("Test 2: { isCompleteMatch: %t, Matches: %d }\n", isCompleteMatch2, matches2)
}
Output:
String: "Hello abc, xyz, abc", Substrings: [abc xyz]
Test 1: { isCompleteMatch: true, Matches: 2 }
String: "Hello abc, abc", Substrings: [abc xyz]
Test 2: { isCompleteMatch: false, Matches: 1 }
Here's the live example: https://play.golang.org/p/Xka0KfBrRD
Another solution would be using a combination of regexp and suffixarray. From the documentation:
Package suffixarray implements substring search in logarithmic time using an in-memory suffix array.
package main
import (
"fmt"
"index/suffixarray"
"regexp"
"strings"
)
func main() {
fmt.Println(contains("first secondthird", "first", "second", "third"))
fmt.Println(contains("first secondthird", "first", "10th"))
}
func contains(str string, subStrs ...string) bool {
if len(subStrs) == 0 {
return true
}
r := regexp.MustCompile(strings.Join(subStrs, "|"))
index := suffixarray.New([]byte(str))
res := index.FindAllIndex(r, -1)
exists := make(map[string]int)
for _, v := range subStrs {
exists[v] = 1
}
for _, pair := range res {
s := str[pair[0]:pair[1]]
exists[s] = exists[s] + 1
}
for _, v := range exists {
if v == 1 {
return false
}
}
return true
}
(In Go Playground)
[H]ow do I check multiple substrings without using strings.Contains() repeatedly?
Not at all. You have to call Contains repeatedly.

Counting characters in golang string

I am trying to count "characters" in go. That is, if a string contains one printable "glyph", or "composed character" (or what someone would ordinarily think of as a character), I want it to count 1. For example, the string "Hello, δΈ–πŸ––πŸΏπŸ––η•Œ", should count 11, since there are 11 characters, and a human would look at this and say there are 11 glyphs.
utf8.RuneCountInString() works well in most cases, including ascii, accents, asian characters and even emojis. However, as I understand it runes correspond to code points, not characters. When I try to use basic emojis it works, but when I use emojis that have different skin tones, I get the wrong count: https://play.golang.org/p/aFIGsB6MsO
From what I read here and here the following should work, but I still don't seem to be getting the right results (it over-counts):
func CountCharactersInString(str string) int {
var ia norm.Iter
ia.InitString(norm.NFC, str)
nc := 0
for !ia.Done() {
nc = nc + 1
ia.Next()
}
return nc
}
This doesn't work either:
func GraphemeCountInString(str string) int {
re := regexp.MustCompile("\\PM\\pM*|.")
return len(re.FindAllString(str, -1))
}
I am looking for something similar to this in Objective C:
+ (NSInteger)countCharactersInString:(NSString *) string {
// --- Calculate the number of characters enterd by user and update character count label
NSInteger count = 0;
NSUInteger index = 0;
while (index < string.length) {
NSRange range = [string rangeOfComposedCharacterSequenceAtIndex:index];
count++;
index += range.length;
}
return count;
}
Straight forward natively use the utf8.RuneCountInString()
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "Hello, δΈ–πŸ––πŸ––η•Œ"
fmt.Println("counts =", utf8.RuneCountInString(str))
}
I wrote a package that allows you to do this: https://github.com/rivo/uniseg. It breaks strings according to the rules specified in Unicode Standard Annex #29 which is what you are looking for. Here is how you would use it in your case:
package main
import (
"fmt"
"github.com/rivo/uniseg"
)
func main() {
fmt.Println(uniseg.GraphemeClusterCount("Hello, δΈ–πŸ––πŸΏπŸ––η•Œ"))
}
This will print 11 as you expect.
Have you tried strings.Count?
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Count("Hello, δΈ–πŸ––πŸ––η•Œ", "πŸ––")) // Returns 2
}
Reference to the example of API document.
https://golang.org/pkg/unicode/utf8/#example_DecodeLastRuneInString
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "Hello, δΈ–πŸ––η•Œ"
count := 0
for len(str) > 0 {
r, size := utf8.DecodeLastRuneInString(str)
count++
fmt.Printf("%c %v\n", r, size)
str = str[:len(str)-size]
}
fmt.Println("count:",count)
}
I think the easiest way to do this would be like this:
package main
import "fmt"
func main() {
str := "Hello, δΈ–πŸ––πŸ––η•Œ"
var counter int
for range str {
counter++
}
fmt.Println(counter)
}
This one prints 11

Go: Retrieve a string from between two characters or other strings

Let's say for example that I have one string, like this:
<h1>Hello World!</h1>
What Go code would be able to extract Hello World! from that string? I'm still relatively new to Go. Any help is greatly appreciated!
If the string looks like whatever;START;extract;END;whatever you can use this which will get the string in between:
// GetStringInBetween Returns empty string if no start string found
func GetStringInBetween(str string, start string, end string) (result string) {
s := strings.Index(str, start)
if s == -1 {
return
}
s += len(start)
e := strings.Index(str[s:], end)
if e == -1 {
return
}
e += s + e - 1
return str[s:e]
}
What happens here is it will find first index of START, adds length of START string and returns all that exists from there until first index of END.
There are lots of ways to split strings in all programming languages.
Since I don't know what you are especially asking for I provide a sample way to get the output
you want from your sample.
package main
import "strings"
import "fmt"
func main() {
initial := "<h1>Hello World!</h1>"
out := strings.TrimLeft(strings.TrimRight(initial,"</h1>"),"<h1>")
fmt.Println(out)
}
In the above code you trim <h1> from the left of the string and </h1> from the right.
As I said there are hundreds of ways to split specific strings and this is only a sample to get you started.
Hope it helps, Good luck with Golang :)
DB
I improved the Jan KardaΕ‘`s answer.
now you can find string with more than 1 character at the start and end.
func GetStringInBetweenTwoString(str string, startS string, endS string) (result string,found bool) {
s := strings.Index(str, startS)
if s == -1 {
return result,false
}
newS := str[s+len(startS):]
e := strings.Index(newS, endS)
if e == -1 {
return result,false
}
result = newS[:e]
return result,true
}
Here is my answer using regex. Not sure why no one suggested this safest approach
package main
import (
"fmt"
"regexp"
)
func main() {
content := "<h1>Hello World!</h1>"
re := regexp.MustCompile(`<h1>(.*)</h1>`)
match := re.FindStringSubmatch(content)
if len(match) > 1 {
fmt.Println("match found -", match[1])
} else {
fmt.Println("match not found")
}
}
Playground - https://play.golang.org/p/Yc61x1cbZOJ
In the strings pkg you can use the Replacer to great affect.
r := strings.NewReplacer("<h1>", "", "</h1>", "")
fmt.Println(r.Replace("<h1>Hello World!</h1>"))
Go play!
func findInString(str, start, end string) ([]byte, error) {
var match []byte
index := strings.Index(str, start)
if index == -1 {
return match, errors.New("Not found")
}
index += len(start)
for {
char := str[index]
if strings.HasPrefix(str[index:index+len(match)], end) {
break
}
match = append(match, char)
index++
}
return match, nil
}
Read up on the strings package. Have a look into the SplitAfter function which can do something like this:
var sample = "[this][is my][string]"
t := strings.SplitAfter(sample, "[")
That should produce a slice something like: "[", "this][", "is my][", "string]". Using further functions for Trimming you should get your solution. Best of luck.
func Split(str, before, after string) string {
a := strings.SplitAfterN(str, before, 2)
b := strings.SplitAfterN(a[len(a)-1], after, 2)
if 1 == len(b) {
return b[0]
}
return b[0][0:len(b[0])-len(after)]
}
the first call of SplitAfterN will split the original string into array of 2 parts divided by the first found after string, or it will produce array containing 1 part equal to the original string.
second call of SplitAfterN uses a[len(a)-1] as input, as it is "the last item of array a". so either string after after or the original string str. the input will be split into array of 2 parts divided by the first found before string, or it will produce array containing 1 part equal to the input.
if after was not found than we can simply return b[0] as it is equal to a[len(a)-1]
if after is found, it will be included at the end of b[0] string, therefore you have to trim it via b[0][0:len(b[0])-len(after)]
all strings are case sensitive

Resources