skip regex chars until search using golang - regex

This will skip the 1st 2 characters and start matching left to right
re := regexp.MustCompile("(^.{2})(\\/path\\/subpath((\\/.*)|()))")
fmt.Println(re.MatchString("c:/path/subpath/path/subpath/")) // true
fmt.Println(re.MatchString("c:/patch/subpath/path/subpath/")) // false
notice the second one doesnt hit. even though /path/subpath exists in the string. This is perfect.
now if if dont know how many characters to skip and want to start search at the 1st '/' then i tried this
re2 := regexp.MustCompile("([^\\/])(\\/path\\/subpath((\\/.*)|()))")
fmt.Println(re2.MatchString("cddddd:/path/subpath/path/subpath")) // true
which is perfect. but if i change the 1st path
fmt.Println(re2.MatchString("cddddd:/patch/subpath/path/subpath")) // this is true as well
I don't want the last one to match the second /path/subpath. I want to be able to search in the 1st group, start the second group from there and do a left to right match.
Any help would be great appreciated.

It pays to be more precise about what you want, state what you want in absolute terms, not like "second first should not match third". Instead, say;
I want to capture the path if it begins with /path/subpath in the second group. If a path contains /path/subpath somewhere in later than the beginning, then I don't want that to match.
Also, slashes are not special in regex, so you don't need to double-escape them for nothing.
The third expression, does this:
capture everything that is not a slash from the start anchor
delimit group 1 from group 2 by :
require /path/subpath to be the at the top of the path
capture whatever remains
This may be what you want:
package main
import (
"fmt"
"regexp"
)
func main() {
paths := []string{
"c:/path/subpath/path/subpath/",
"c:/patch/subpath/path/subpath/",
"cddddd:/path/subpath/path/subpath",
}
re1 := regexp.MustCompile("(^.{2})(/path/subpath(/.*))")
re2 := regexp.MustCompile("([^/])(/path/subpath((/.*)|()))")
re3 := regexp.MustCompile(`^([^/]+):/path/subpath(/.*)`)
for i, re := range []*regexp.Regexp{re1, re2, re3} {
i++
for _, s := range paths {
fmt.Println(i, re.MatchString(s), s)
if re.MatchString(s) {
matches := re.FindStringSubmatch(s)
for m, g := range matches {
m++
if m > 1 {
fmt.Printf("\n\t%d %v", m, g)
}
}
}
println()
}
println()
}
}
Output
$ go run so-regex-path.go
(...)
3 true c:/path/subpath/path/subpath/
2 c
3 /path/subpath/
3 false c:/patch/subpath/path/subpath/
3 true cddddd:/path/subpath/path/subpath
2 cddddd
3 /path/subpath

Related

Find all strings in between two strings in Go

I am working on extracting mutliple matches between two strings.
In the example below, I am trying to regex out an A B C substring out of my string.
Here is my code:
package main
import (
"fmt"
"regexp"
)
func main() {
str:= "Movies: A B C Food: 1 2 3"
re := regexp.MustCompile(`[Movies:][^Food:]*`)
match := re.FindAllString(str, -1)
fmt.Println(match)
}
I am clearly doing something wrong in my regex. I am trying to get the A B C string between Movies: and Food:.
What is the proper regex to get all strings between two strings?
In Go, since its RE2-based regexp does not support lookarounds, you need to use capturing mechanism with regexp.FindAllStringSubmatch function:
left := "LEFT_DELIMITER_TEXT_HERE"
right := "RIGHT_DELIMITER_TEXT_HERE"
rx := regexp.MustCompile(`(?s)` + regexp.QuoteMeta(left) + `(.*?)` + regexp.QuoteMeta(right))
matches := rx.FindAllStringSubmatch(str, -1)
Note the use of regexp.QuoteMeta that automatically escapes all special regex metacharacters in the left- and right-hand delimiters.
The (?s) makes . match across lines and (.*?) captures all between ABC and XYZ into Group 1.
So, here you can use
package main
import (
"fmt"
"regexp"
)
func main() {
str:= "Movies: A B C Food: 1 2 3"
r := regexp.MustCompile(`Movies:\s*(.*?)\s*Food`)
matches := r.FindAllStringSubmatch(str, -1)
for _, v := range matches {
fmt.Println(v[1])
}
}
See the Go demo. Output: A B C.

What would be a regex to match all occurrences after =, space separated?

I have /components/component[name=fan/10 index=55]/cpu
I want a regex that givens me fan/10 and 55.
I tried stuff like =(.*)\s, but doesn't work. But I'm guessing it has to be done using capturing groups (the () ) somehow?
You may use
=([^\]\s]+)
See regex demo
Details
= - an equals sign
([^\]\s]+) - Capturing group 1: any 1 or more chars other than ] and whitespace.
GO demo:
package main
import (
"fmt"
"regexp"
)
func main() {
s := "/components/component[name=fan/10 index=55]/cpu"
rx := regexp.MustCompile(`=([^\]\s]+)`)
matches := rx.FindAllStringSubmatch(s, -1)
for _, v := range matches {
fmt.Println(v[1])
}
}
Output:
fan/10
55
You may try to use something like this:
s := "/components/component[name=fan/10 index=55]/cpu"
re := regexp.MustCompile(`=([^\s\]]*)`)
matches := re.FindAllStringSubmatch(s, -1)
fmt.Println(matches)
Result will be:
[[=fan/10 fan/10] [=55 55]]

Go regex find substring

I have a string:
s := "root 1 12345 /root/pathtomyfolder/jdk/jdk.1.8.0.25 org.catalina.startup"
I need to grep the version number to a string
Tried,
var re = regexp.MustCompile(`jdk.*`)
func main() {
matches := re.FindStringSubmatch(s)
fmt.Printf ("%q", matches)
}
You need to specify capturing groups to extract submatches, as described in the package overview:
If 'Submatch' is present, the return value is a slice identifying the
successive submatches of the expression. Submatches are matches of
parenthesized subexpressions (also known as capturing groups) within
the regular expression, numbered from left to right in order of
opening parenthesis. Submatch 0 is the match of the entire expression,
submatch 1 the match of the first parenthesized subexpression, and so
on.
Something along the lines of the following:
func main() {
var re = regexp.MustCompile(`jdk\.([^ ]+)`)
s := "root 1 12345 /root/pathtomyfolder/jdk/jdk.1.8.0.25 org.catalina.startup"
matches := re.FindStringSubmatch(s)
fmt.Printf("%s", matches[1])
// Prints: 1.8.0.25
}
You'll of course want to check whether there actually is a submatch, or matches[1] will panic.
if you need the value in a string variable to be used / manipulated elsewhere, you could add the following in the given example Marc gave:
a := fmt.Sprintf("%s", matches[1])

Positive lookahead + overlapping matches regex

I'm looking for a regex to match all % that are not followed by a valid 2-characters hex code (2 characters in a-fA-F0-9). I came up with (%)(?=([0-9a-fA-F][^0-9a-fA-F]|[^0-9a-fA-F])) which works well but is not supported in golang, because of the positive lookahead (?=).
How can I translate it (or maybe make it simpler?), so that it works with go?
For example, given the string %d%2524e%25f%255E00%%%252611%25, it should match the first % and the first two ones of the %%% substring.
ie: https://regex101.com/r/y0YQ1I/2
I only tried this on regex101 (marked golang regex), but it seems that it works as expected:
%[0-9a-fA-F][0-9a-fA-F]|(%)
or simpler:
%[0-9a-fA-F]{2}|(%)
The real challenge here is that the matches at position 19 and 20 are overlapping, which means we can't use any of the go builtin "FindAll..." functions since they only find non-overlapping matches. This means that we've got to match the regex repeatedly against substrings starting after subsequent match indices if we want to find them all.
For the regex itself I've used a non-capturing group (?:...) instead of a lookahead assertion. Additionally, the regex will also match percent-signs at the end of the string, since they cannot be followed by two hex digits:
func findPlainPercentIndices(s string) []int {
re := regexp.MustCompile(`%(?:[[:xdigit:]][[:^xdigit:]]|[[:^xdigit:]]|$)`)
indices := []int{}
idx := 0
for {
m := re.FindStringIndex(s[idx:])
if m == nil {
break
}
nextidx := idx + m[0]
indices = append(indices, nextidx)
idx = nextidx + 1
}
return indices
}
func main() {
str := "%d%2524e%25f%255E00%%%252611%25%%"
// 012345678901234567890123456789012
// 0 1 2 3
fmt.Printf("OK: %#v\n", findPlainPercentIndices(str))
// OK: []int{0, 19, 20, 31, 32}
}

Golang regexp to match multiple patterns between keyword pairs

I have a string which has two keywords: "CURRENT NAME(S)" and "NEW NAME(S)" and each of these keywords are followed by a bunch of words. I want to extract those set of words beyond each of these keywords. To elaborate with a code:
s := `"CURRENT NAME(S)
Name1, Name2",,"NEW NAME(S)
NewName1,NewName2"`
re := regexp.MustCompile(`"CURRENT NAME(S).*",,"NEW NAME(S).*"`)
segs := re.FindAllString(s, -1)
fmt.Println("segs:", segs)
segs2 := re.FindAllStringSubmatch(s, -1)
fmt.Println("segs2:", segs2)
As you can see, the string 's' has the input. "Name1,Name2" is the current names list and "NewName1, NewName2" is the new names list. I want to extract these two lists. The two lists are separated by a comma. Each of the keywords are beginning with a double quote and their reach ends, when their corresponding double quote ends.
What is the way to use regexp such that the program can print "Name1, Name2" and "NewName1,NewName2" ?
The issue with your regex is that the input string contains newline symbols, and . in Go regex does not match a newline. Another issue is that the .* is a greedy pattern and will match as many symbols as it can up to the last second keyword. Also, you need to escape parentheses in the regex pattern to match the ( and ) literal symbols.
The best way to solve the issue is to change .* into a negated character class pattern [^"]* and place it inside a pair of non-escaped ( and ) to form a capturing group (a construct to get submatches from the match).
Here is a Go demo:
package main
import (
"fmt"
"regexp"
)
func main() {
s := `"CURRENT NAME(S)
Name1, Name2",,"NEW NAME(S)
NewName1,NewName2"`
re := regexp.MustCompile(`"CURRENT NAME\(S\)\s*([^"]*)",,"NEW NAME\(S\)\s*([^"]*)"`)
segs2 := re.FindAllStringSubmatch(s,-1)
fmt.Printf("segs2: [%s; %s]", segs2[0][1], segs2[0][2])
}
Now, the regex matches:
"CURRENT NAME\(S\) - a literal string "CURRENT NAME(S)`
\s* - zero or more whitespaces
([^"]*) - Group 1 capturing 0+ chars other than "
",,"NEW NAME\(S\) - a literal string ",,"NEW NAME(S)
\s* - zero or more whitespaces
([^"]*) - Group 2 capturing 0+ chars other than "
" - a literal "
If your input doesn't change then the simplest way would be to use submatches (groups). You can try something like this:
// (?s) is a flag that enables '.' to match newlines
var r = regexp.MustCompile(`(?s)CURRENT NAME\(S\)(.*)",,"NEW NAME\(S\)(.*)"`)
fmt.Println(r.MatchString(s))
m := r.FindSubmatch([]byte(s)) // FindSubmatch requires []byte
for _, match := range m {
s := string(match)
fmt.Printf("Match - %d: %s\n", i, strings.Trim(s, "\n")) //remove the newline
}
Output: (Note that the first match is the entire input string because it completely matches the regex (https://golang.org/pkg/regexp/#Regexp.FindSubmatch)
Match - 0: CURRENT NAME(S)
Name1, Name2",,"NEW NAME(S)
NewName1,NewName2"
Match - 1: Name1, Name2
Match - 2: NewName1,NewName2
Example: https://play.golang.org/p/0cgBOMumtp
For a fixed format like in the example, you can also avoid regular expressions and perform explicit parsing as in this example - https://play.golang.org/p/QDIyYiWJHt:
package main
import (
"fmt"
"strings"
)
func main() {
s := `"CURRENT NAME(S)
Name1, Name2",,"NEW NAME(S)
NewName1,NewName2"`
names := []string{}
parts := strings.Split(s, ",,")
for _, part := range parts {
part = strings.Trim(part, `"`)
part = strings.TrimPrefix(part, "CURRENT NAME(S)")
part = strings.TrimPrefix(part, "NEW NAME(S)")
part = strings.TrimSpace(part)
names = append(names, part)
}
fmt.Println("Names:")
for _, name := range names {
fmt.Println(name)
}
}
Output:
Names:
Name1, Name2
NewName1,NewName2
It uses a few more lines of code but may make it easier to understand the processing logic at a first glance.