I am trying to write some regular expressions that match and pull apart strings that look like software versions (but they aren't really, so semver parsing, for instance, won't work). I'm having some trouble matching what I am calling "prefix" and "suffix" parts of the input string.
The output I am getting from the following is, strange..
// Sample inputs:
// * '1.2.3-thing' (Prefix: '1.2.3', Suffix: '-thing')
// * '1.2.3+1' (Prefix: '1.2.3', Suffix: '+1')
// * '1.2.3' (Prefix: '1.2.3', Suffix: '')
// * '1' (Prefix: '1', Suffix: '')
// * '1-x' (Prefix: '1', Suffix: '-x')
// * '1-x-x' (Prefix: '1', Suffix: '-x-x')
// * '1.2.3-thing.1' (Prefix: '1.2.3', Suffix: '-thing.1')
// * '1.2-thing-1' (Prefix: '1.2', Suffix: '-thing-1')
// * 'k1.2.3-thing' (Prefix: 'k1.2.3', Suffix: '-thing')
// * 'k-thing-x' (Prefix: 'k', Suffix: '-thing-x')
//
func InspectVersionTag(tag string) {
re := regexp.MustCompile(`^([^\-]+)([\-+].+)$`)
suffix := ""
if re.MatchString(tag) {
tag = re.ReplaceAllString(tag, `$1`)
suffix = re.ReplaceAllString(tag, `$2`)
}
fmt.Println(fmt.Sprintf("Prefix is: %s", tag))
fmt.Println(fmt.Sprintf("Suffix is: %s", suffix))
}
// Current sample output
//
// Input: 1.2.3+1
// Prefix is: 1.2.3
// Suffix is: 1.2.3
Regexp is the wrong tool for this simple task, because it is slow, difficult to read, and as this question indicates, difficult to reason about. For these same reasons, regexp is the wrong tool for almost all tasks it is used for.
In your case, all you need to do is just split on your separators: -
and +
:
func InspectVersionTag(tag string) {
var suffix string
for _, sep := range []string{"-","+"} {
if strings.Contains(tag, sep) {
parts := strings.SplitN(tag, sep, 2)
tag, suffix = parts[0], sep+parts[1]
continue
}
}
fmt.Printf("Prefix: %s Suffix: %s
", tag, suffix)
}
See playground link.
Given your samples, it should be really easy. I wrote the following code from the top of my head. It might not even compile but you should get the idea:
func parseVersion(ver string) (prefix, suffix string) {
parts := strings.SplitAfter(ver, "-", 2)
if len(parts) == 1 {
parts = strings.SplitAfter(ver, "+", 2)
}
if len(parts) == 1 {
return ver, ""
}
return parts[0], parts [1]
}
You should definitely list all your examples in an automatic test instead of a comment. And if the above code doesn't help, your examples aren't realistic enough.
Here is how I have solved a similar problem. Have a look at Test_MkParser_PkgbasePattern
and Test_MkParser_Dependency
here:
https://github.com/rillig/pkglint/blob/master/mkparser_test.go
It can easily get complicated. That's why you should write a test for each interesting case, right from the beginning.