Regex101 icon indicating copy to clipboard operation
Regex101 copied to clipboard

Golang generated code is not working as expected

Open kpym opened this issue 4 years ago • 8 comments

Code Generator Language

Go

Code Snippet

I want to replace A(.)C in ABC with C${1}A (the result should be CBA). The current code is


package main

import (
    "regexp"
    "fmt"
)

func main() {
    var re = regexp.MustCompile(`^A(.)C$`)
    var str = []byte(`ABC`)
    var substitution = []byte("C${1}A")
    var i = 0
    var count = 1
    
    str = re.ReplaceAllFunc(str, func(s []byte) []byte {
        if count < 0 {
            return substitution
        } else if i < count {
            i += 1
            
            return substitution
        }
        
        return s
    })
    
    fmt.Println(string(str))
}

which is not working.

The desired code is

package main

import (
    "regexp"
    "fmt"
)

func main() {
    var re = regexp.MustCompile(`^A(.)C$`)
    var str = []byte(`ABC`)
    var substitution = []byte("C${1}A")
    
    str = re.ReplaceAll(str, substitution)
    
    fmt.Println(string(str))
}

kpym avatar Jul 12 '20 17:07 kpym

Hi! Can you please elaborate on "not working"? Do you get an error/message? (I've never done anything in go)

Update: nevermind, ran in the Go Playground, output of first seems literal but have no knowledge of why. Proposed code seems to be just fine with one or more replacements within the string.

Thank you!

working-name avatar Jul 12 '20 17:07 working-name

You can check the result (C${1}A) in the online playground.

The code is doing "string" replacement, not "regex" one.

You can also check the code that I suggest in the same online playground.

kpym avatar Jul 12 '20 17:07 kpym

Hmm, it looks like you've compiled a non-global regex, is that correct? In that scenario, only the first occurrence should be replaced. The first code examples (tries) to do that, while the second does not (unless I'm misinterpreting something).

Perhaps you can help correct the whole code generator for golang, if there are cases where it is incorrect? You can view the four different scenarios here:

Non-global substitution: https://regex101.com/r/rRAG2X/1/codegen?language=golang Global substitution: https://regex101.com/r/rRAG2X/2/codegen?language=golang Non-global match: https://regex101.com/r/rRAG2X/3/codegen?language=golang Global match: https://regex101.com/r/rRAG2X/4/codegen?language=golang

Thanks in advance!

firasdib avatar Jul 13 '20 20:07 firasdib

You are right, the only problem is in the case of non-global substitution. Here is my proposal.

package main

import (
	"fmt"
	"regexp"
)

func main() {
	var re = regexp.MustCompile(`A(.)C`)
	var str = "ABCABC"
	var substitution = `C${1}A`
	var count = 1 // nagative counter is equivalent to global case (replace all)

	str = re.ReplaceAllStringFunc(str, func(s string) string {
		if count == 0 {
			return s
		}

		count -= 1
		return re.ReplaceAllString(s, substitution)
	})

	fmt.Println(string(str))
}

What I have done :

  • I use ReplaceAllStringFunc in place of ReplaceAllFunc so we can use strings and not []byte like in the other three cases.
  • I return re.ReplaceAllString(s,substitution) in place of substitution because if substitution contains group identifiers (like $1) they must be expanded.
  • I use string literals (`...`) only for the regular expressions but use a regular string ("...") for the str variable. I think that this is something that should de corrected in all four cases (not only in the non-global replacement case).
  • I have simplified the code by removing the i variable.

You can check and play with this code in the golang playground.

kpym avatar Jul 14 '20 07:07 kpym

I have made a proposal to manage this case easily in Go, but it was not accepted (for backward compatibility reasons). So putting the right code generator may help Go developers to write non-global replace properly.

kpym avatar Oct 19 '20 07:10 kpym

I will try to get around to this soon.

firasdib avatar Oct 19 '20 07:10 firasdib

@firasdib thanks. Here is an alternative code that introduce the missing ReplaceString function with limiting parameter n.

package main

import (
	"fmt"
	"regexp"
)

func ReplaceString(src, search, repl string, n int) string {

	var re = regexp.MustCompile(search)

	src = re.ReplaceAllStringFunc(src, func(s string) string {
		if n == 0 {
			return s
		}

		n -= 1
		return re.ReplaceAllString(s, repl)
	})

	return src
}

func main() {
	var re = `A(.)C`
	var str = "ABCABC"
	var substitution = `C${1}A`

	fmt.Println(ReplaceString(str, re, substitution, 1))
}

This code in Google Playground.

You can use this one or the code from my previous comment.

kpym avatar Oct 19 '20 07:10 kpym

Did you ever fix this @firasdib ?

Ouims avatar Jan 03 '21 16:01 Ouims